using System; using AsbCloudApp.Data; using AsbCloudApp.Data.DetectedOperation; using AsbCloudApp.Repositories; using AsbCloudApp.Requests; using AsbCloudApp.Services; using AsbCloudDb.Model; using Mapster; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using AsbCloudApp.Data.WellOperation; using AsbCloudInfrastructure.Services.DetectOperations.Detectors; namespace AsbCloudInfrastructure.Services.DetectOperations; public class DetectedOperationService : IDetectedOperationService { private readonly IDetectedOperationRepository operationRepository; private readonly IWellOperationCategoryRepository wellOperationCategoryRepository; private readonly IWellService wellService; private readonly ITelemetryService telemetryService; private readonly IRepositoryWellRelated operationValueRepository; private readonly IScheduleRepository scheduleRepository; private readonly ITelemetryDataSaubService telemetryDataSaubService; private static readonly DetectorAbstract[] detectors = { new DetectorDrilling(), new DetectorSlipsTime(), new DetectorFlashing(), new DetectorConditioning(), }; public DetectedOperationService( IDetectedOperationRepository operationRepository, IWellOperationCategoryRepository wellOperationCategoryRepository, IWellService wellService, ITelemetryService telemetryService, IRepositoryWellRelated operationValueRepository, IScheduleRepository scheduleRepository, ITelemetryDataSaubService telemetryDataSaubService) { this.operationRepository = operationRepository; this.wellOperationCategoryRepository = wellOperationCategoryRepository; this.wellService = wellService; this.telemetryService = telemetryService; this.operationValueRepository = operationValueRepository; this.scheduleRepository = scheduleRepository; this.telemetryDataSaubService = telemetryDataSaubService; } public async Task GetAsync(DetectedOperationByWellRequest request, CancellationToken token) { var dtos = await GetOperationsAsync(request, token); if (dtos?.Any() != true) return new DetectedOperationListDto(); var stats = GetOperationsDrillersStat(dtos); var result = new DetectedOperationListDto { Operations = dtos, Stats = stats }; return result; } public async Task> GetOperationsAsync(DetectedOperationByWellRequest request, CancellationToken token) { var well = await wellService.GetOrDefaultAsync(request.IdWell, token); if (well?.IdTelemetry is null) return Enumerable.Empty(); var requestByTelemetry = new DetectedOperationByTelemetryRequest(well.IdTelemetry.Value, request); var data = await operationRepository.Get(requestByTelemetry, token); var operationValues = await operationValueRepository.GetByIdWellAsync(request.IdWell, token); var schedules = await scheduleRepository.GetByIdWellAsync(request.IdWell, token); var dtos = data.Select(o => Convert(o, operationValues, schedules)); return dtos; } public async Task> GetCategoriesAsync(int? idWell, CancellationToken token) { if(idWell is null) { return wellOperationCategoryRepository.Get(false); } else { var well = await wellService.GetOrDefaultAsync((int )idWell, token); if (well?.IdTelemetry is null) return Enumerable.Empty(); var request = new DetectedOperationByTelemetryRequest() { IdTelemetry = well.IdTelemetry.Value }; var operations = await operationRepository.Get(request, token); var categories = operations .Select(o => o.OperationCategory) .Distinct(); return categories; } } public async Task> GetOperationsStatAsync(DetectedOperationByWellRequest request, CancellationToken token) { var well = await wellService.GetOrDefaultAsync(request.IdWell, token); if (well?.IdTelemetry is null || well.Timezone is null) return Enumerable.Empty(); var requestByTelemetry = new DetectedOperationByTelemetryRequest(well.IdTelemetry.Value, request); var operations = await operationRepository.Get(requestByTelemetry, token); if (!operations.Any()) return Enumerable.Empty(); var dtos = operations .GroupBy(o => (o.IdCategory, o.OperationCategory.Name)) .OrderBy(g => g.Key) .Select(g => new DetectedOperationStatDto { IdCategory = g.Key.IdCategory, Category = g.Key.Name, Count = g.Count(), MinutesAverage = g.Average(o => o.DurationMinutes), MinutesMin = g.Min(o => o.DurationMinutes), MinutesMax = g.Max(o => o.DurationMinutes), MinutesTotal = g.Sum(o => o.DurationMinutes), ValueAverage = g.Average(o => o.Value), ValueMax = g.Max(o => o.Value), ValueMin = g.Min(o => o.Value), }); return dtos; } public async Task> DetectOperationsAsync(int idTelemetry, DateTimeOffset? beginDate, CancellationToken token) { const int take = 4 * 86_400; var detectedOperations = new List(); DetectedOperationDto? lastDetectedOperation = null; const int minOperationLength = 5; const int maxDetectorsInterpolationFrameLength = 30; const int gap = maxDetectorsInterpolationFrameLength + minOperationLength; var timezone = telemetryService.GetTimezone(idTelemetry); while (true) { var request = new TelemetryDataRequest { GeDate = beginDate, Take = take, Order = 0 }; var dtos = await telemetryDataSaubService.GetByTelemetryAsync(idTelemetry, request, token); var detectableTelemetries = dtos .Where(t => t.BlockPosition >= 0) .Select(t => new DetectableTelemetry { DateTime = new DateTimeOffset(t.DateTime, timezone.Offset), IdUser = t.IdUser, Mode = t.Mode, WellDepth = t.WellDepth, Pressure = t.Pressure, HookWeight = t.HookWeight, BlockPosition = t.BlockPosition, BitDepth = t.BitDepth, RotorSpeed = t.RotorSpeed, AxialLoad = t.AxialLoad, }).ToArray(); if (detectableTelemetries.Length <= gap) break; var isDetected = false; var positionBegin = 0; var positionEnd = detectableTelemetries.Length - gap; while (positionEnd > positionBegin) { foreach (var detector in detectors) { if (!detector.TryDetect(idTelemetry, detectableTelemetries, positionBegin, positionEnd, lastDetectedOperation, out var result)) continue; detectedOperations.Add(result!.Operation); lastDetectedOperation = result.Operation; isDetected = true; positionBegin = result.TelemetryEnd; break; } positionBegin += 1; } beginDate = isDetected ? lastDetectedOperation!.DateEnd : detectableTelemetries[positionEnd].DateTime; } return detectedOperations; } public async Task DeleteAsync(DetectedOperationByWellRequest request, CancellationToken token) { var well = await wellService.GetOrDefaultAsync(request.IdWell, token); if (well?.IdTelemetry is null || well.Timezone is null) return 0; var requestByTelemetry = new DetectedOperationByTelemetryRequest(well.IdTelemetry.Value, request); var result = await operationRepository.Delete(-1, requestByTelemetry, token); return result; } private static IEnumerable GetOperationsDrillersStat(IEnumerable operations) { var groups = operations.GroupBy(o => o.Driller); var stats = new List(groups.Count()); foreach (var group in groups) { var itemsWithTarget = group.Where(i => i.OperationValue is not null); var stat = new DetectedOperationDrillersStatDto { Driller = group.Key, AverageValue = group.Sum(e => e.Value) / group.Count(), Count = group.Count(), }; if (itemsWithTarget.Any()) { var itemsOutOfTarget = itemsWithTarget.Where(o => !IsTargetOk(o)); stat.AverageTargetValue = itemsWithTarget.Average(e => e.OperationValue?.TargetValue); stat.Efficiency = 100d * itemsOutOfTarget.Count() / itemsWithTarget.Count(); stat.Loss = itemsOutOfTarget.Sum(DeltaToTarget); } stats.Add(stat); } return stats; } private static bool IsTargetOk(DetectedOperationWithDrillerDto op) { return (op.IdCategory) switch { WellOperationCategory.IdRotor => op.Value > op.OperationValue?.TargetValue, WellOperationCategory.IdSlide => op.Value > op.OperationValue?.TargetValue, WellOperationCategory.IdSlipsTime => op.Value > op.OperationValue?.TargetValue, _ => op.Value > op.OperationValue?.TargetValue, }; } private static double DeltaToTarget(DetectedOperationWithDrillerDto op) { return (op.IdCategory) switch { WellOperationCategory.IdRotor => 0, WellOperationCategory.IdSlide => 0, WellOperationCategory.IdSlipsTime => op.Value - op.OperationValue?.TargetValue??0, _ => 0, }; } private static DetectedOperationWithDrillerDto Convert(DetectedOperationDto operation, IEnumerable operationValues, IEnumerable schedules) { var dto = operation.Adapt(); dto.OperationValue = operationValues.FirstOrDefault(v => v.IdOperationCategory == dto.IdCategory && v.DepthStart <= dto.DepthStart && v.DepthEnd > dto.DepthStart); var dateStart = dto.DateStart.ToUniversalTime(); var timeStart = new TimeDto(dateStart); var driller = schedules.FirstOrDefault(s => s.DrillStart <= dateStart && s.DrillEnd > dateStart && ( s.ShiftStart > s.ShiftEnd ) ^ (s.ShiftStart <= timeStart && s.ShiftEnd > timeStart )) ?.Driller; dto.Driller = driller; return dto; } }