using AsbCloudDb.Model; using AsbCloudDb.Model.Subsystems; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace AsbCloudInfrastructure.Services.Subsystems { #nullable enable internal class SubsystemOperationTimeBackgroundService : BackgroundService { private readonly string connectionString; private readonly TimeSpan period = TimeSpan.FromHours(1); private const int idSubsytemTorqueMaster = 65537; private const int idSubsytemSpinMaster = 65536; public SubsystemOperationTimeBackgroundService(IConfiguration configuration) { connectionString = configuration.GetConnectionString("DefaultConnection"); } protected override async Task ExecuteAsync(CancellationToken token) { var timeToStartAnalysis = DateTime.Now; var options = new DbContextOptionsBuilder() .UseNpgsql(connectionString) .Options; while (!token.IsCancellationRequested) { if (DateTime.Now > timeToStartAnalysis) { timeToStartAnalysis = DateTime.Now + period; try { using var context = new AsbCloudDbContext(options); var added = await OperationTimeAllTelemetriesAsync(context, token); Trace.TraceInformation($"Total subsystem operation time complete. Added {added} operations time."); } catch (Exception ex) { Trace.TraceError(ex.Message); } GC.Collect(); } var ms = (int)(timeToStartAnalysis - DateTime.Now).TotalMilliseconds; ms = ms > 100 ? ms : 100; await Task.Delay(ms, token).ConfigureAwait(false); } } public override async Task StopAsync(CancellationToken token) { await base.StopAsync(token).ConfigureAwait(false); } private static async Task OperationTimeAllTelemetriesAsync(IAsbCloudDbContext db, CancellationToken token) { var lastDetectedDates = await db.SubsystemOperationTimes .GroupBy(o => o.IdTelemetry) .Select(g => new { IdTelemetry = g.Key, LastDate = g.Max(o => o.DateEnd) }) .ToListAsync(token); var telemetryIds = await db.Telemetries .Where(t => t.Info != null && t.TimeZone != null) .Select(t => t.Id) .ToListAsync(token); var JounedlastDetectedDates = telemetryIds .GroupJoin(lastDetectedDates, t => t, o => o.IdTelemetry, (outer, inner) => new { IdTelemetry = outer, inner.SingleOrDefault()?.LastDate, }); var affected = 0; foreach (var item in JounedlastDetectedDates) { var newOperationsSaub = await OperationTimeSaubAsync(item.IdTelemetry, item.LastDate ?? DateTimeOffset.MinValue, db, token); if (newOperationsSaub?.Any() == true) { db.SubsystemOperationTimes.AddRange(newOperationsSaub); affected += await db.SaveChangesAsync(token); } var newOperationsSpin = await OperationTimeSpinAsync(item.IdTelemetry, item.LastDate ?? DateTimeOffset.MinValue, db, token); if (newOperationsSpin?.Any() == true) { db.SubsystemOperationTimes.AddRange(newOperationsSpin); affected += await db.SaveChangesAsync(token); } } return affected; } private static async Task?> OperationTimeSaubAsync(int idTelemetry, DateTimeOffset begin, IAsbCloudDbContext db, CancellationToken token) { var operationTimeSaub = $"select tt.date, tt.mode, tt.well_depth" + $" from (select date, mode, well_depth, lag(mode,1) over (order by date) as mode_prev" + $" from t_telemetry_data_saub where id_telemetry = @idTelemetry) as tt" + $" where tt.mode != tt.mode_prev and tt.date >=@begin order by tt.date;"; using var command = db.Database.GetDbConnection().CreateCommand(); command.CommandText = operationTimeSaub; SqlParameter telemetry = new SqlParameter("@idTelemetry", idTelemetry); command.Parameters.Add(telemetry); SqlParameter dateStart = new SqlParameter("@begin", begin); command.Parameters.Add(dateStart); db.Database.OpenConnection(); using var result = await command.ExecuteReaderAsync(token); var telemetryOpearationSaubSubsystems = new List(); if (result.HasRows) { while (result.Read()) { var itemEntity = new SubsystemsSpinWithDepth() { Date = result.GetFieldValue(0), IdSubsystem = result.GetFieldValue(1)+1, Depth = result.GetFieldValue(2) }; telemetryOpearationSaubSubsystems.Add(itemEntity); } } var resultSubsystemOperationTime = new List(); var firstItem = telemetryOpearationSaubSubsystems.FirstOrDefault(); if (firstItem is null) return null; int idSubsystem = firstItem.IdSubsystem; DateTimeOffset dateBegin = firstItem.Date; float? depthStart = firstItem.Depth; for (int i = 1; i < telemetryOpearationSaubSubsystems.Count; i++) { if (telemetryOpearationSaubSubsystems[i].IdSubsystem != idSubsystem) { var operationTimeItem = new SubsystemOperationTime() { IdTelemetry = idTelemetry, DateStart = dateBegin, IdSubsystem = telemetryOpearationSaubSubsystems[i - 1].IdSubsystem, DateEnd = telemetryOpearationSaubSubsystems[i].Date, DepthStart = depthStart, DepthEnd = telemetryOpearationSaubSubsystems[i].Depth }; dateBegin = telemetryOpearationSaubSubsystems[i].Date; depthStart = telemetryOpearationSaubSubsystems[i].Depth; idSubsystem = telemetryOpearationSaubSubsystems[i].IdSubsystem; resultSubsystemOperationTime.Add(operationTimeItem); } } return resultSubsystemOperationTime; } private static async Task?> OperationTimeSpinAsync(int idTelemetry, DateTimeOffset begin, IAsbCloudDbContext db, CancellationToken token) { Predicate isSubsystemTorqueState = (int state) => state == 7; Predicate isSubsystemTorqueMode = (short mode) => (mode & 2) > 0; Predicate isSubsystemSpin = (int state) => state != 0 & state != 6 & state != 7; var operationTimeSpinWithDepth = $"select tspin.\"date\", tspin.\"mode\", tspin.\"state\", tsaub.\"well_depth\"" + $" from (select \"date\" ,\"mode\" ,lag(mode, 1) over (order by \"date\") as mode_pre, state , " + $"lag(state, 1) over (order by \"date\") as state_pre from t_telemetry_data_spin where id_telemetry = {idTelemetry} and date >= '{begin}') as tspin " + $"join (select \"date\", well_depth from t_telemetry_data_saub where id_telemetry = {idTelemetry} and date >= '{begin}') as tsaub " + $"on EXTRACT(EPOCH from tspin.date) = EXTRACT(EPOCH from tsaub.date) " + $"where mode!=mode_pre or state != state_pre order by \"date\";"; using var command = db.Database.GetDbConnection().CreateCommand(); command.CommandText = operationTimeSpinWithDepth; SqlParameter telemetry = new SqlParameter("@idTelemetry", idTelemetry); command.Parameters.Add(telemetry); SqlParameter dateStart = new SqlParameter("@begin", begin); command.Parameters.Add(dateStart); db.Database.OpenConnection(); using var result = await command.ExecuteReaderAsync(token); var telemetryOpearationSpinSubsystems = new List(); if (result.HasRows) { while (result.Read()) { var itemEntity = new SubsystemsSpinWithDepth() { Date = result.GetFieldValue(0), Mode = result.GetFieldValue(1), State = result.GetFieldValue(2), Depth = result.GetFieldValue(3) }; int? subsystemId = isSubsystemTorqueState(itemEntity.State) && isSubsystemTorqueMode(itemEntity.Mode) ? idSubsytemTorqueMaster : isSubsystemSpin(itemEntity.State) ? idSubsytemSpinMaster : 0; if (subsystemId.HasValue) { itemEntity.IdSubsystem = subsystemId.Value; telemetryOpearationSpinSubsystems.Add(itemEntity); } } } var resultSubsystemOperationTime = new List(); var firstItem = telemetryOpearationSpinSubsystems.FirstOrDefault(); if (firstItem is null) return null; int idSubsystem = firstItem.IdSubsystem; DateTimeOffset dateBegin = firstItem.Date; float? depthStart = firstItem.Depth; for (int i = 1; i < telemetryOpearationSpinSubsystems.Count; i++) { if (telemetryOpearationSpinSubsystems[i].IdSubsystem != idSubsystem) { var operationTimeItem = new SubsystemOperationTime() { IdTelemetry = idTelemetry, DateStart = dateBegin, IdSubsystem = telemetryOpearationSpinSubsystems[i - 1].IdSubsystem, DateEnd = telemetryOpearationSpinSubsystems[i].Date, DepthStart = depthStart, DepthEnd = telemetryOpearationSpinSubsystems[i].Depth }; dateBegin = telemetryOpearationSpinSubsystems[i].Date; depthStart = telemetryOpearationSpinSubsystems[i].Depth; idSubsystem = telemetryOpearationSpinSubsystems[i].IdSubsystem; if (telemetryOpearationSpinSubsystems[i - 1].IdSubsystem != 0) { resultSubsystemOperationTime.Add(operationTimeItem); } } } return resultSubsystemOperationTime; } } #nullable disable }