using AsbCloudDb.Model; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; using AsbCloudInfrastructure.Services.DetectOperations.Detectors; using AsbCloudInfrastructure.Background; using Microsoft.Extensions.DependencyInjection; namespace AsbCloudInfrastructure.Services.DetectOperations; public class WorkOperationDetection: Work { private static readonly DetectorAbstract[] detectors = new DetectorAbstract[] { new DetectorDrilling(), new DetectorSlipsTime() // new DetectorRotor(), // new DetectorSlide(), //new DetectorDevelopment(), //new DetectorTemplating(), //new DetectorStaticSurveying(), //new DetectorFlashingBeforeConnection(), //new DetectorFlashing(), //new DetectorTemplatingWhileDrilling(), }; public WorkOperationDetection() :base("Operation detection") { Timeout = TimeSpan.FromMinutes(20); OnErrorAsync = (id, exception, token) => { var text = $"work {id}, when {CurrentState?.State}, throw error:{exception.Message}"; Trace.TraceWarning(text); return Task.CompletedTask; }; } protected override async Task Action(string id, IServiceProvider services, Action onProgressCallback, CancellationToken token) { using var db = services.GetRequiredService(); db.Database.SetCommandTimeout(TimeSpan.FromMinutes(5)); var lastDetectedDates = await db.DetectedOperations .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 joinedlastDetectedDates = telemetryIds .GroupJoin(lastDetectedDates, t => t, o => o.IdTelemetry, (outer, inner) => new { IdTelemetry = outer, inner.SingleOrDefault()?.LastDate, }); var affected = 0; var count = joinedlastDetectedDates.Count(); var i = 0d; foreach (var item in joinedlastDetectedDates) { var stopwatch = Stopwatch.StartNew(); var startDate = item.LastDate ?? DateTimeOffset.MinValue; onProgressCallback($"start detecting telemetry: {item.IdTelemetry} from {startDate}", i++ / count); var newOperations = await DetectOperationsAsync(item.IdTelemetry, startDate, db, token); stopwatch.Stop(); if (newOperations.Any()) { db.DetectedOperations.AddRange(newOperations); affected += await db.SaveChangesAsync(token); } } } private static async Task> DetectOperationsAsync(int idTelemetry, DateTimeOffset begin, IAsbCloudDbContext db, CancellationToken token) { var query = db.TelemetryDataSaub .AsNoTracking() .Where(d => d.IdTelemetry == idTelemetry) .Where(d => d.BlockPosition >= 0) .Select(d => new DetectableTelemetry { DateTime = d.DateTime, IdUser = d.IdUser, Mode = d.Mode, WellDepth = d.WellDepth, Pressure = d.Pressure, HookWeight = d.HookWeight, BlockPosition = d.BlockPosition, BitDepth = d.BitDepth, RotorSpeed = d.RotorSpeed, }) .OrderBy(d => d.DateTime); var take = 4 * 86_400; // 4 дня var startDate = begin; var detectedOperations = new List(8); DetectedOperation? lastDetectedOperation = null; const int minOperationLength = 5; const int maxDetectorsInterpolationFrameLength = 30; const int gap = maxDetectorsInterpolationFrameLength + minOperationLength; while (true) { var data = await query .Where(d => d.DateTime > startDate) .Take(take) .ToArrayAsync(token); if (data.Length < gap) break; var isDetected = false; var positionBegin = 0; var positionEnd = data.Length - gap; while (positionEnd > positionBegin) { foreach (var detector in detectors) { if (!detector.TryDetect(idTelemetry, data, positionBegin, positionEnd, lastDetectedOperation, out var result)) continue; detectedOperations.Add(result!.Operation); lastDetectedOperation = result.Operation; isDetected = true; positionBegin = result.TelemetryEnd; break; } positionBegin += 1; } if (isDetected) startDate = lastDetectedOperation!.DateEnd; else startDate = data[positionEnd].DateTime; } return detectedOperations; } }