From 5a2435795ac4f0fcaeb5fec294d8ed4146857f51 Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Wed, 19 Oct 2022 13:55:10 +0500 Subject: [PATCH] #6370638 refactored --- .../Services/Subsystems/DepthInterpolation.cs | 57 ++- ...SubsystemOperationTimeBackgroundService.cs | 356 ++++++++++-------- 2 files changed, 228 insertions(+), 185 deletions(-) diff --git a/AsbCloudInfrastructure/Services/Subsystems/DepthInterpolation.cs b/AsbCloudInfrastructure/Services/Subsystems/DepthInterpolation.cs index 06a0fd24..750786c4 100644 --- a/AsbCloudInfrastructure/Services/Subsystems/DepthInterpolation.cs +++ b/AsbCloudInfrastructure/Services/Subsystems/DepthInterpolation.cs @@ -7,35 +7,56 @@ namespace AsbCloudInfrastructure.Services.Subsystems.Utils #nullable enable internal class DepthInterpolation { - private List<(DateTimeOffset dateMin, float depthMin, DateTimeOffset dateMax, float depthMax)> collection; - (DateTimeOffset x, float y) lastValue = default; - public DepthInterpolation(List<(DateTimeOffset dateMin, float depthMin, DateTimeOffset dateMax, float depthMax)> collection) + private readonly TimeSpan maxDeltaDate = TimeSpan.FromHours(12); + private readonly IEnumerator<(DateTimeOffset x, float y)> enumerator; + private (DateTimeOffset x, float y) p0; + private bool canMoveNext; + + public DepthInterpolation(IEnumerable<(DateTimeOffset x, float y)> collection) { - this.collection = collection; + enumerator = collection.GetEnumerator(); + canMoveNext = enumerator.MoveNext(); + p0 = enumerator.Current; + } + + ~DepthInterpolation() + { + enumerator.Dispose(); } public float GetDepth(DateTimeOffset date) - { - for (int i = 0; i < collection.Count; i++) + { + // ошибка в телеметрии см. прим.: idTelemetry = 93 && date between '2021-11-16 17:18:40.000 +0500' and '2021-11-16 17:19:37.000 +0500' + while (canMoveNext && enumerator.Current.x < date) { - if (date <= collection[i].dateMax && date >= collection[i].dateMin) - { - lastValue.x = date; - lastValue.y = CalcValue(collection[i], (date - collection[i].dateMin).TotalSeconds); - } + p0 = enumerator.Current; + canMoveNext = enumerator.MoveNext(); } - return lastValue.y; + return CalcValue(date); } - private float CalcValue((DateTimeOffset dateMin, float depthMin, DateTimeOffset dateMax, float depthMax) item, double timestamp) + private float CalcValue(DateTimeOffset date) { - if (item.depthMin == item.depthMax) + var p1 = enumerator.Current; + if (canMoveNext || p1 == default || p0.y == p1.y || p0.x > date) + return p0.y; + + if (p1.x < date) + return p1.y; + + if (p1.x - p0.x > maxDeltaDate && Math.Abs(p1.y - p0.y) > 0.2d) { - return item.depthMin; + if (date - p0.x < p1.x - date) + return p0.y; + else + return p1.y; } - var w = timestamp / (item.dateMax - item.dateMin).TotalSeconds; - var d = ((item.depthMax - item.depthMin) * w) + item.depthMin; - return (float)d; + + var a = (p1.y - p0.y) / (p1.x - p0.x).TotalSeconds; + var b = p0.y; + var x = (date - p0.x).TotalSeconds; + var y = a * x + b; + return (float)y; } } #nullable disable diff --git a/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeBackgroundService.cs b/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeBackgroundService.cs index 70e87d76..9f77a77e 100644 --- a/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeBackgroundService.cs +++ b/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeBackgroundService.cs @@ -1,6 +1,7 @@ using AsbCloudDb.Model; using AsbCloudDb.Model.Subsystems; using AsbCloudInfrastructure.Services.Subsystems.Utils; +using iText.Forms.Xfdf; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; @@ -25,6 +26,7 @@ namespace AsbCloudInfrastructure.Services.Subsystems private const int idSubsytemSpinMaster = 65536; private const int idSubsytemAkb = 1; private const int idSubsytemMse = 2; + public SubsystemOperationTimeBackgroundService(IConfiguration configuration) { connectionString = configuration.GetConnectionString("DefaultConnection"); @@ -108,6 +110,25 @@ namespace AsbCloudInfrastructure.Services.Subsystems } return affected; } + + private static async Task ExecuteReaderAsync(IAsbCloudDbContext db, string query, CancellationToken token) + { + var connection = db.Database.GetDbConnection(); + if ( + connection?.State is null || + connection.State == ConnectionState.Broken || + connection.State == ConnectionState.Closed) + { + await db.Database.OpenConnectionAsync(token); + connection = db.Database.GetDbConnection(); + } + using var command = connection.CreateCommand(); + command.CommandText = query; + + var result = await command.ExecuteReaderAsync(token); + return result; + } + private static async Task> OperationTimeSaubAsync(int idTelemetry, DateTimeOffset begin, IAsbCloudDbContext db, CancellationToken token) { static bool isSubsytemAkb(short? mode) @@ -126,47 +147,6 @@ namespace AsbCloudInfrastructure.Services.Subsystems if ((state & 1) > 0) return true; return false; - } - - static List GetSubsystemOperationTimes (List dataRows, Predicate satisfyCondition, int idSubsystem, int idTelemetry) - { - if (dataRows is null) - return new List(); - if (dataRows.Count < 2) - return new List(); - var listSubsystemOperationTime = new List(); - var foundSubsystem = satisfyCondition(dataRows[0]); - var dateStart = dataRows[0].Date; - var depthStart = dataRows[0].Depth; - - for (int i = 1; i 0" + $" order by date ) as tt " + - $"where (tt.mode_prev is null or tt.mode != tt.mode_prev) and tt.date >= @begin " + + $"where (tt.mode_lag is null or (tt.mode != tt.mode_lag and tt.mode_lead != tt.mode_lag)) and tt.date >= '{begin:u}' " + $"order by tt.date;"; - var idTelemetryParam = new NpgsqlParameter("@idTelemetry", idTelemetry); - var beginParam = new NpgsqlParameter("@begin", begin); + using var result = await ExecuteReaderAsync(db, query, token); - await db.Database.OpenConnectionAsync(token); - using var command = db.Database.GetDbConnection().CreateCommand(); - command.CommandText = query; - command.Parameters.Add(idTelemetryParam); - command.Parameters.Add(beginParam); + var subsystemsOperationTimes = new List(); + + (bool isEnable, DateTimeOffset date, float depth) akbPre = default; + (bool isEnable, DateTimeOffset date, float depth) msePre = default; - using var result = await command.ExecuteReaderAsync(token); - - var subsystemOperationTime = new List(); - var dataRowList = new List(); while (result.Read()) { - var dateRowItem = new DataRow() + var mode = result.GetFieldValue(1); + var state = result.GetFieldValue(3); + + var isAkbEnable = isSubsytemAkb(mode); + var isMseEnable = IsSubsystemMse(state); + var date = result.GetFieldValue(0); + var depth = result.GetFieldValue(2); + + if (!akbPre.isEnable && isAkbEnable) { - Date = result.GetFieldValue(0), - Mode = result.GetFieldValue(1), - Depth = result.GetFieldValue(2), - State = result.GetFieldValue(3) - }; - dataRowList.Add(dateRowItem); + akbPre = (true, date, depth); + } + else if (akbPre.isEnable && !isAkbEnable) + { + var subsystemOperationTime = new SubsystemOperationTime + { + IdTelemetry = idTelemetry, + IdSubsystem = idSubsytemAkb, + DateStart = akbPre.date, + DateEnd = date, + DepthStart = akbPre.depth, + DepthEnd = depth, + }; + if (IsValid(subsystemOperationTime)) + subsystemsOperationTimes.Add(subsystemOperationTime); + akbPre.isEnable = false; + } + + if (!msePre.isEnable && isMseEnable) + { + msePre = (true, date, depth); + } + else if (msePre.isEnable && !isMseEnable) + { + var subsystemOperationTime = new SubsystemOperationTime + { + IdTelemetry = idTelemetry, + IdSubsystem = idSubsytemMse, + DateStart = akbPre.date, + DateEnd = date, + DepthStart = akbPre.depth, + DepthEnd = depth, + }; + if (IsValid(subsystemOperationTime)) + subsystemsOperationTimes.Add(subsystemOperationTime); + + msePre.isEnable = false; + } } - var akbOperationTimes = GetSubsystemOperationTimes(dataRowList, d => isSubsytemAkb(d.Mode), idSubsytemAkb, idTelemetry); - var mseOperationTimes = GetSubsystemOperationTimes(dataRowList, d => IsSubsystemMse(d.State), idSubsytemMse, idTelemetry); - subsystemOperationTime.AddRange(akbOperationTimes); - subsystemOperationTime.AddRange(mseOperationTimes); - return subsystemOperationTime; + + return subsystemsOperationTimes; } - private static async Task?> OperationTimeSpinAsync(int idTelemetry, DateTimeOffset begin, IAsbCloudDbContext db, CancellationToken token) + private static async Task> OperationTimeSpinAsync(int idTelemetry, DateTimeOffset begin, IAsbCloudDbContext db, CancellationToken token) { static int? GetSubsytemId(short? mode, int? state) { @@ -227,18 +239,7 @@ namespace AsbCloudInfrastructure.Services.Subsystems return null; } - static async Task GetResultFromQueryAsync(string query, DateTimeOffset paramDate, int paramIdTelemetry, IAsbCloudDbContext db, CancellationToken token) - { - var idTelemetryParam = new NpgsqlParameter("@idTelemetry", paramIdTelemetry); - var beginParam = new NpgsqlParameter("@begin", paramDate); - await db.Database.OpenConnectionAsync(token); - var command = db.Database.GetDbConnection().CreateCommand(); - command.CommandText = query; - command.Parameters.Add(idTelemetryParam); - command.Parameters.Add(beginParam); - var result = await command.ExecuteReaderAsync(token); - return result; - } + var querySpin = $"select " + $" tspin.date, " + @@ -248,104 +249,125 @@ namespace AsbCloudInfrastructure.Services.Subsystems $" select " + $" date, " + $" mode, " + - $" lag(mode, 1) over (order by date) as mode_pre, " + + $" lag(mode, 1) over (order by date) as mode_lag, " + + $" lead(mode, 1) over (order by date) as mode_lead, " + $" state, " + - $" lag(state, 1) over (order by date) as state_pre " + + $" lag(state, 1) over (order by date) as state_lag " + $" from t_telemetry_data_spin " + - $" where id_telemetry = @idTelemetry and date >= @begin" + + $" where id_telemetry = {idTelemetry} and date >= '{begin:u}'" + $" order by date ) as tspin " + - $"where mode_pre is null or state_pre is null or mode != mode_pre or state != state_pre " + + $"where mode_lag is null or state_lag is null or (mode != mode_lag and mode_lead != mode_lag) or state != state_lag " + $"order by date;"; - var queryDepthFromSaub = - "select " + - " min(\"date\") as min_date, " + - " min(well_depth) as min_well_depth, " + - " max(\"date\") as max_date, " + - " max(well_depth) as max_well_depth " + - "from " + - " t_telemetry_data_saub " + - "where id_telemetry = @idTelemetry and \"date\" >= @begin " + - "group by ceil(well_depth * 10) " + - "order by min_date; "; - - var resultSpin = await GetResultFromQueryAsync(querySpin,begin,idTelemetry,db,token); - - var subsystemOperationTime = new List(32); - var dataSpinList = new List(); - while (resultSpin.Read()) + var rows = new List<(int? IdSubsystem, DateTimeOffset Date)>(32); { - var dateRowItem = new DataRow() + using var resultSpin = await ExecuteReaderAsync(db, querySpin, token); + int? idSubsystemLast = null; + while (resultSpin.Read()) { - Date = resultSpin.GetFieldValue(0), - Mode = resultSpin.GetFieldValue(1), - State = resultSpin.GetFieldValue(2) - }; - dataSpinList.Add(dateRowItem); - } - await resultSpin.DisposeAsync(); - dataSpinList.OrderBy(s => s.Date); - using var resultDepthFromSaub = await GetResultFromQueryAsync(queryDepthFromSaub, begin, idTelemetry, db, token); - var dataDepthFromSaub = new List<(DateTimeOffset dateMin, float depthMin, DateTimeOffset dateMax, float depthMax)>(); - while (resultDepthFromSaub.Read()) - { - var dateRowItem = - ( - resultDepthFromSaub.GetFieldValue(0), - resultDepthFromSaub.GetFieldValue(1), - resultDepthFromSaub.GetFieldValue(2), - resultDepthFromSaub.GetFieldValue(3) - ); - dataDepthFromSaub.Add(dateRowItem); - } - dataDepthFromSaub.OrderBy(o => o.dateMin); - var depthInterpolation = new DepthInterpolation(dataDepthFromSaub); - foreach (var item in dataSpinList) - { - item.Depth = depthInterpolation.GetDepth(item.Date); - } - if (dataSpinList.Any()) - { - var mode = dataSpinList[0].Mode; - var state = dataSpinList[0].State; - var idSubsystem = GetSubsytemId(mode, state); - var dateStart = dataSpinList[0].Date; - var depthStart = dataSpinList[0].Depth; - for (int i = 1; i < dataSpinList.Count; i++) - { - var dateEnd = dataSpinList[i].Date; - var depthEnd = dataSpinList[i].Depth; - if (idSubsystem.HasValue) + var mode = resultSpin.GetFieldValue(1); + var state = resultSpin.GetFieldValue(2); + var idSubsystem = GetSubsytemId(mode, state); + if(idSubsystemLast != idSubsystem) { - var operationTimeItem = new SubsystemOperationTime() - { - IdTelemetry = idTelemetry, - IdSubsystem = idSubsystem.Value, - DateStart = dateStart, - DateEnd = dateEnd, - DepthEnd = depthEnd, - DepthStart = depthStart - }; - subsystemOperationTime.Add(operationTimeItem); - } - mode = dataSpinList[i].Mode; - state = dataSpinList[i].State; - idSubsystem = GetSubsytemId(mode, state); - dateStart = dateEnd; - depthStart = depthEnd; + idSubsystemLast = idSubsystem; + var date = resultSpin.GetFieldValue(0); + rows.Add((idSubsystem, date)); + } + } + await resultSpin.DisposeAsync(); + } + + if (rows.Count < 2) + return Enumerable.Empty(); + + var minSpinDate = rows.Min(i => i.Date); + var maxSpinDate = rows.Max(i => i.Date); + var depthInterpolation = await GetInterpolation(db, idTelemetry, minSpinDate, maxSpinDate, token); + + if (depthInterpolation is null) + return Enumerable.Empty(); + + var subsystemsOperationTimes = new List(32); + + for (int i = 1; i < rows.Count; i++) + { + var r0 = rows[i - 1]; + var r1 = rows[i]; + if (r0.IdSubsystem is not null && r0.IdSubsystem != r1.IdSubsystem) + { + var subsystemOperationTime = new SubsystemOperationTime() + { + IdTelemetry = idTelemetry, + IdSubsystem = r0.IdSubsystem.Value, + DateStart = r0.Date, + DateEnd = r1.Date, + DepthStart = depthInterpolation.GetDepth(r0.Date), + DepthEnd = depthInterpolation.GetDepth(r1.Date), + }; + + if (IsValid(subsystemOperationTime)) + subsystemsOperationTimes.Add(subsystemOperationTime); } } - return subsystemOperationTime; + + return subsystemsOperationTimes; + } + + private static bool IsValid(SubsystemOperationTime item) + { + var validateCode = GetValidateErrorCode(item); + if (validateCode != 0) + { + var str = System.Text.Json.JsonSerializer.Serialize(item); + Trace.TraceWarning($"Wrong({validateCode}) SubsystemOperationTime: {str}"); + } + return validateCode == 0; + } + + private static int GetValidateErrorCode(SubsystemOperationTime item) + { + if (item.DateStart > item.DateEnd) + return -1; + if ((item.DateEnd - item.DateStart).TotalHours > 48) + return -2; + if (item.DepthEnd < item.DepthStart) + return -3; + if (item.DepthEnd - item.DepthStart > 2000d) + return -4; + if (item.DepthEnd < 0d) + return -5; + if (item.DepthStart < 0d) + return -6; + if (item.DepthEnd > 24_0000d) + return -5; + if (item.DepthStart > 24_0000d) + return -6; + return 0; + } + + private static async Task GetInterpolation(IAsbCloudDbContext db, int idTelemetry, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token) + { + var dataDepthFromSaub = await db.TelemetryDataSaub + .Where(d => d.IdTelemetry == idTelemetry) + .Where(d => d.DateTime >= dateBegin) + .Where(d => d.DateTime <= dateEnd) + .Where(d => d.WellDepth != null) + .Where(d => d.WellDepth > 0) + .GroupBy(d => Math.Ceiling(d.WellDepth ?? 0 * 10)) + .Select(g => new { + DateMin = g.Min(d => d.DateTime), + DepthMin = g.Min(d => d.WellDepth) ?? 0, + }) + .OrderBy(i => i.DateMin) + .ToArrayAsync(token); + + if (!dataDepthFromSaub.Any()) + return null; + + var depthInterpolation = new DepthInterpolation(dataDepthFromSaub.Select(i=>(i.DateMin, i.DepthMin))); + return depthInterpolation; } } - - internal class DataRow - { - public DateTimeOffset Date { get; set; } - public short? Mode { get; set; } - public float? Depth { get; set; } - public short? State { get; set; } - } - #nullable disable }