using AsbCloudApp.Data; using AsbCloudApp.Data.DetectedOperation; using AsbCloudApp.Data.Subsystems; using AsbCloudApp.Exceptions; using AsbCloudApp.Requests; using AsbCloudApp.Services; using AsbCloudDb.Model; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using AsbCloudInfrastructure.Services.DetectOperations; namespace AsbCloudInfrastructure.Services.Subsystems; internal class SubsystemService : ISubsystemService { private const int IdSubsystemAPD = 1; private const int IdSubsystemAPDRotor = 11; private const int IdSubsystemAPDSlide = 12; private const int IdSubsystemOscillation = 65536; private readonly ICrudRepository subsystemRepository; private readonly IWellService wellService; private readonly IDetectedOperationService detectedOperationService; private readonly ITelemetryDataSaubService telemetryDataSaubService; private IDictionary subsystems = new Dictionary(); public SubsystemService(ICrudRepository subsystemRepository, IWellService wellService, IDetectedOperationService detectedOperationService, ITelemetryDataSaubService telemetryDataSaubService) { this.wellService = wellService; this.subsystemRepository = subsystemRepository; this.detectedOperationService = detectedOperationService; this.telemetryDataSaubService = telemetryDataSaubService; } public async Task> GetStatAsync(SubsystemRequest request, CancellationToken token) { var well = await wellService.GetOrDefaultAsync(request.IdWell, token) ?? throw new ArgumentInvalidException(nameof(request.IdWell), $"Well Id: {request.IdWell} does not exist"); var detectedOperationSummaryRequest = new DetectedOperationRequest { IdWell = request.IdWell, IdsTelemetries = new[] { well.IdTelemetry!.Value }, IdsCategories = WellOperationCategory.MechanicalDrillingSubIds, GeDateStart = request.GeDate, LeDateEnd = request.LeDate, GeDepth = request.GeDepth, LeDepth = request.LeDepth, }; var operations = await detectedOperationService.GetOperationsAsync(detectedOperationSummaryRequest, token); if (request.IdDriller.HasValue) operations = operations.Where(o => o.Driller is not null && o.Driller.Id == request.IdDriller.Value); if (!operations.Any()) return Enumerable.Empty(); var stat = await CalcStatAsync(operations, token); return stat; } public async Task> GetStatByActiveWells(int idCompany, DateTime? gtDate, DateTime? ltDate, CancellationToken token) { var activeWells = await wellService.GetAsync(new() { IdCompany = idCompany, IdState = 1 }, token); var result = await GetStatAsync(activeWells, gtDate, ltDate, token); return result; } public async Task> GetStatByActiveWells(IEnumerable wellIds, CancellationToken token) { var activeWells = await wellService.GetAsync(new() { Ids = wellIds, IdState = 1 }, token); var result = await GetStatAsync(activeWells, null, null, token); return result; } private async Task> CalcStatAsync(IEnumerable operations, CancellationToken token) { if (!subsystems.Any()) subsystems = (await subsystemRepository.GetAllAsync(token)).ToDictionary(s => s.Id, s => s); var oscillationStat = CalcOscillationStat(operations); var apdStat = CalcApdStat(operations); var stat = new List { oscillationStat }; stat.AddRange(apdStat); return stat; } private SubsystemStatDto CalcOscillationStat(IEnumerable operations) { operations = operations.Where(o => o.IdCategory == WellOperationCategory.IdSlide); var (sumDepthInterval, usedTimeHours, operationCount) = AggregateOperations(IdSubsystemOscillation, operations); var oscillationStat = new SubsystemStatDto { IdSubsystem = IdSubsystemOscillation, SubsystemName = subsystems.TryGetValue(IdSubsystemOscillation, out var subsystemDto) ? subsystemDto.Name : "unknown", UsedTimeHours = usedTimeHours, SumOperationDepthInterval = operations.Sum(o => o.DepthEnd - o.DepthStart), SumOperationDurationHours = operations.Sum(o => o.DurationMinutes / 60), SumDepthInterval = sumDepthInterval, OperationCount = operationCount, }; oscillationStat.KUsage = oscillationStat.SumDepthInterval / oscillationStat.SumOperationDepthInterval; return oscillationStat; } private IEnumerable CalcApdStat(IEnumerable operations) { var apdRotorAndSlide = operations .Where(o => WellOperationCategory.MechanicalDrillingSubIds.Contains(o.IdCategory)) .GroupBy(o => o.IdCategory) .Select(group => { var idSubsystem = group.Key switch { WellOperationCategory.IdRotor => IdSubsystemAPDRotor, WellOperationCategory.IdSlide => IdSubsystemAPDSlide, _ => throw new ArgumentException($"IdCategory: {group.Key} does not supported in this method", nameof(group.Key)) }; var (sumDepthInterval, usedTimeHours, operationCount) = AggregateOperations(idSubsystem, group); var subsystemStat = new SubsystemStatDto { IdSubsystem = idSubsystem, SubsystemName = subsystems.TryGetValue(idSubsystem, out var subsystemDto) ? subsystemDto.Name : "unknown", UsedTimeHours = usedTimeHours, SumOperationDepthInterval = group.Sum(o => o.DepthEnd - o.DepthStart), SumOperationDurationHours = group.Sum(o => o.DurationMinutes / 60), SumDepthInterval = sumDepthInterval, OperationCount = operationCount, }; subsystemStat.KUsage = subsystemStat.SumDepthInterval / subsystemStat.SumOperationDepthInterval; return subsystemStat; }); if (!apdRotorAndSlide.Any()) return Enumerable.Empty(); var apdSum = new SubsystemStatDto { IdSubsystem = IdSubsystemAPD, SubsystemName = subsystems.TryGetValue(IdSubsystemAPD, out var subsystemDto) ? subsystemDto.Name : "unknown", UsedTimeHours = apdRotorAndSlide.Sum(part => part.UsedTimeHours), SumOperationDepthInterval = apdRotorAndSlide.Sum(part => part.SumOperationDepthInterval), SumOperationDurationHours = apdRotorAndSlide.Sum(part => part.SumOperationDurationHours), SumDepthInterval = apdRotorAndSlide.Sum(part => part.SumDepthInterval), OperationCount = apdRotorAndSlide.Sum(part => part.OperationCount), }; apdSum.KUsage = apdSum.SumDepthInterval / apdSum.SumOperationDepthInterval; var apdStat = new List { apdSum }; apdStat.AddRange(apdRotorAndSlide); return apdStat; } private static (double SumDepthInterval, double UsedTimeHours, int Count) AggregateOperations(int idSubsystem, IEnumerable operations) => idSubsystem switch { IdSubsystemAPDRotor => CalcOperationsByEnableSubsystems(operations, EnabledSubsystemsFlags.AutoRotor), IdSubsystemAPDSlide => CalcOperationsByEnableSubsystems(operations, EnabledSubsystemsFlags.AutoSlide | EnabledSubsystemsFlags.AutoOscillation), IdSubsystemOscillation => CalcOperationsByEnableSubsystems(operations, EnabledSubsystemsFlags.AutoOscillation), _ => throw new ArgumentException($"IdSubsystem: {idSubsystem} does not supported in this method", nameof(idSubsystem)) }; private static (double SumDepthInterval, double UsedTimeHours, int OperationCount) CalcOperationsByEnableSubsystems( IEnumerable operations, EnabledSubsystemsFlags enabledSubsystems) { var filtered = operations.Where(o => enabledSubsystems.HasEnabledSubsystems(o.EnabledSubsystems)); var sumDepthInterval = filtered.Sum(o => o.DepthEnd - o.DepthStart); var usedTimeHours = filtered.Sum(o => o.DurationMinutes / 60); var operationCount = filtered.Count(); return (sumDepthInterval, usedTimeHours, operationCount); } private async Task> GetStatAsync(IEnumerable wells, DateTime? geDate, DateTime? leDate, CancellationToken token) { if (!wells.Any()) return Enumerable.Empty(); var idsTelemetries = wells .Where(w => w.IdTelemetry is not null) .Select(w => w.IdTelemetry!.Value) .Distinct(); var wellsStat = new List(); foreach (var well in wells) { var hoursOffset = well.Timezone.Hours; var geDateStartUtc = geDate?.ToUtcDateTimeOffset(hoursOffset); var leDateUtc = leDate?.ToUtcDateTimeOffset(hoursOffset); var request = new DetectedOperationRequest { IdsTelemetries = idsTelemetries, IdsCategories = WellOperationCategory.MechanicalDrillingSubIds, GeDateStart = geDateStartUtc, LeDateEnd = leDateUtc, }; var operations = await detectedOperationService .GetOperationsAsync(request, token); var wellStat = new SubsystemActiveWellStatDto { Well = well }; var telemetryOperations = operations.Where(o => o.IdTelemetry == well.IdTelemetry); if (!telemetryOperations.Any()) continue; var subsystemStat = await CalcStatAsync(telemetryOperations, token); if (!subsystemStat.Any()) continue; wellStat.SubsystemAPD = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemAPD); wellStat.SubsystemOscillation = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemOscillation); } return wellsStat; } }