using AsbCloudDb.Model; using AsbCloudDb.Model.Subsystems; using AsbCloudInfrastructure.Background; using AsbCloudInfrastructure.Services.Subsystems; using AsbCloudInfrastructure.Services.Subsystems.Utils; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace AsbCloudInfrastructure.Background.PeriodicWorks; public class WorkSubsystemOscillationOperationTimeCalc : WorkSubsystemOperationTimeCalcAbstract { public WorkSubsystemOscillationOperationTimeCalc() : base("Subsystem operation time calc") { Timeout = TimeSpan.FromMinutes(30); } protected override async Task> OperationTimeAsync(int idTelemetry, DateTimeOffset geDate, IAsbCloudDbContext db, CancellationToken token) { static int? GetSubsytemId(short? mode, int? state) { // При изменении следующего кода сообщи в Vladimir.Sobolev@nedra.digital if (state == 7 && (mode & 2) > 0) return idSubsystemTorqueMaster;// демпфер if (state != 0 && state != 5 && state != 6 && state != 7) return idSubsystemSpinMaster;// осцилляция return null; } var querySpin = $"select " + $" tspin.date, " + $" tspin.mode, " + $" tspin.state " + $"from ( " + $" select " + $" date, " + $" mode, " + $" 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_lag " + $" from t_telemetry_data_spin " + $" where id_telemetry = {idTelemetry} and date >= '{geDate:u}'" + $" order by date ) as tspin " + $"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 rows = new List<(int? IdSubsystem, DateTimeOffset Date)>(32); using var resultSpin = await ExecuteReaderAsync(db, querySpin, token); int? idSubsystemLast = null; while (resultSpin.Read()) { var mode = resultSpin.GetFieldValue(1); var state = resultSpin.GetFieldValue(2); var idSubsystem = GetSubsytemId(mode, state); if (idSubsystemLast != idSubsystem) { 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 subsystemsOperationTimes; } 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 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 -7; if (item.DepthStart > 24_0000d) return -8; 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 * 10)) .Select(g => new { DateMin = g.Min(d => d.DateTime), DepthMin = g.Min(d => d.WellDepth), }) .OrderBy(i => i.DateMin) .ToArrayAsync(token); if (!dataDepthFromSaub.Any()) return null; var depthInterpolation = new DepthInterpolation(dataDepthFromSaub.Select(i => (i.DateMin, i.DepthMin))); return depthInterpolation; } }