using AsbCloudDb.Model; using Microsoft.EntityFrameworkCore; using System; using System.Data.Common; using System.Data; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Collections.Generic; using AsbCloudInfrastructure.Background; using Microsoft.Extensions.DependencyInjection; namespace AsbCloudInfrastructure.Services { internal static class LimitingParameterCalcWorkFactory { private const string workId = "Limiting parameter calc"; private static readonly TimeSpan workPeriod = TimeSpan.FromMinutes(30); public static WorkPeriodic MakeWork() { var workPeriodic = new WorkPeriodic(workId, WorkAction, workPeriod) { Timeout = TimeSpan.FromMinutes(30) }; return workPeriodic; } // TODO: Разделить этот акшн на более мелкие части И использовать telemetryServiceData<..> вместо прямого обращения к БД. private static async Task WorkAction(string _, IServiceProvider serviceProvider, CancellationToken token) { using var db = serviceProvider.GetRequiredService(); var lastDetectedDates = await db.LimitingParameter .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 telemetryLastDetectedDates = telemetryIds .GroupJoin(lastDetectedDates, t => t, o => o.IdTelemetry, (outer, inner) => new { IdTelemetry = outer, inner.SingleOrDefault()?.LastDate, }); foreach (var item in telemetryLastDetectedDates) { var newLimitingParameters = await GetLimitingParameterAsync(item.IdTelemetry, item.LastDate ?? DateTimeOffset.MinValue, db, token); if (newLimitingParameters?.Any() == true) { db.LimitingParameter.AddRange(newLimitingParameters); await db.SaveChangesAsync(token); } } } private static async Task> GetLimitingParameterAsync(int idTelemetry, DateTimeOffset begin, IAsbCloudDbContext db, CancellationToken token) { var query = $"select " + $"limiting_parameters.date, limiting_parameters.id_feed_regulator, limiting_parameters.well_depth " + $"from ( " + $"select " + $"date, id_feed_regulator, well_depth, " + $"lag(id_feed_regulator, 1) over (order by date) as id_feed_regulator_lag, " + $"lead(id_feed_regulator, 1) over (order by date) as id_feed_regulator_lead " + $"from t_telemetry_data_saub " + $"where id_feed_regulator is not null " + $"and id_telemetry = {idTelemetry}" + $"and date >= '{begin:u}'" + $"order by date) as limiting_parameters " + $"where id_feed_regulator_lag is null " + $"or (id_feed_regulator != id_feed_regulator_lag and id_feed_regulator_lead != id_feed_regulator_lag) " + $"order by date;"; var limitingParameters = new List(32); using (var result = await ExecuteReaderAsync(db, query, token)) { LimitingParameter? limitingLast = null; while (result.Read()) { var date = result.GetFieldValue(0); var idLimiting = result.GetFieldValue(1); var wellDepth = result.GetFieldValue(2); if (limitingLast is null) { limitingLast = new LimitingParameter { DateStart = date, DepthStart = wellDepth, IdFeedRegulator = idLimiting }; } if (limitingLast.IdFeedRegulator != idLimiting) { limitingParameters.Add(new LimitingParameter { IdTelemetry = idTelemetry, IdFeedRegulator = limitingLast.IdFeedRegulator, DateStart = limitingLast.DateStart, DateEnd = date, DepthStart = limitingLast.DepthStart, DepthEnd = wellDepth }); limitingLast = new LimitingParameter { DateStart = date, DepthStart = wellDepth, IdFeedRegulator = idLimiting }; } } } return limitingParameters; } 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; } } }