using AsbCloudApp.Data; using AsbCloudApp.Data.DetectedOperation; using AsbCloudApp.Data.ProcessMaps; using AsbCloudApp.Data.Subsystems; using AsbCloudApp.Exceptions; using AsbCloudApp.Repositories; using AsbCloudApp.Requests; using AsbCloudApp.Services; using AsbCloudDb.Model; using AsbCloudInfrastructure.Services.DetectOperations; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace AsbCloudInfrastructure.Services.Subsystems; public 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 IScheduleRepository scheduleRepository; private readonly ITelemetryDataSaubService telemetryDataSaubService; private readonly ITelemetryService telemetryService; private readonly IChangeLogRepository processMapPlanRepository; private IDictionary subsystems = new Dictionary(); public SubsystemService(ICrudRepository subsystemRepository, IWellService wellService, IDetectedOperationService detectedOperationService, IScheduleRepository scheduleRepository, ITelemetryService telemetryService, ITelemetryDataSaubService telemetryDataSaubService, IChangeLogRepository processMapPlanRepository) { this.wellService = wellService; this.subsystemRepository = subsystemRepository; this.detectedOperationService = detectedOperationService; this.scheduleRepository = scheduleRepository; this.telemetryService = telemetryService; this.telemetryDataSaubService = telemetryDataSaubService; this.processMapPlanRepository = processMapPlanRepository; } // получить расписания бурильщиков по скважинам // ScheduleRepository добавить новый метод // [ // get telemetryId by idWell // IWellService.GetOrDefaultStatAsync // получить detectedOperation by telemetry //detectedOperationService.GetOperationsAsync // сгруппировать по бурильщикам // [ // рассчитать статистику // CalcStatAsync // join driller from group // ] // join well (cluster, deposit) // ] public async Task> GetByWellsAsync(GetStatRequest request, CancellationToken token) { var result = new List(); var schedulePage = await scheduleRepository.GetPageAsync(request, token); var wells = await wellService.GetAsync(new WellRequest { Ids = request.IdsWells }, token); foreach (var schedule in schedulePage) { var idWell = schedule.IdWell; var well = wells.FirstOrDefault(w => w.Id == idWell)!; var byWellRequest = new DetectedOperationByWellRequest(idWell, new DetectedOperationRequest()); var detectedOperations = await detectedOperationService .GetOperationsAsync(byWellRequest, token); var detectedOperationsByCurrentDriller = detectedOperations.Where(d => d.Driller?.Id == schedule.IdDriller); var drillerOperationsStat = await CalcStatAsync(detectedOperationsByCurrentDriller, token); var dto = new DrillerDetectedOperationStatDto { Statistic = drillerOperationsStat, Schedule = schedule, Well = well, }; result.Add(dto); } return result; } 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"); if (!well.IdTelemetry.HasValue) return Enumerable.Empty(); var detectedOperationSummaryRequest = new DetectedOperationByWellRequest { IdWell = request.IdWell, IdsCategories = WellOperationCategory.MechanicalDrillingSubIds, GeDateStart = request.GeDate, LeDateEnd = request.LeDate, GeDepthStart = request.GeDepth, LeDepthEnd = 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(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; } public async Task> GetStatPlanFactByWellsAsync(SubsystemPlanFactRequest request, CancellationToken token) { var telemetriesDict = telemetryService .GetOrDefaultTelemetriesByIdsWells(request.IdsWell) .ToDictionary(t => t.Id, t => t.IdWell); var geDate = request.GeDate ?? SubsystemPlanFactRequest.ValidationMinDate; var leDate = request.LeDate ?? DateTimeOffset.UtcNow; var groupedTelemetryDataSaub = await telemetryDataSaubService.GetMinAndMaxWellDepths(telemetriesDict.Keys, geDate, leDate, token); var result = new List(); foreach (var telemetryDataSaubItem in groupedTelemetryDataSaub) { var idWell = telemetriesDict.GetValueOrDefault(telemetryDataSaubItem.Key); if (!idWell.HasValue) continue; var telemetryDataSaubInfo = telemetryDataSaubItem.Value; var requestProcessMapPlan = new ProcessMapPlanBaseRequestWithWell(idWell.Value, telemetryDataSaubInfo.MinDepth, telemetryDataSaubInfo.MaxDepth); var processMapPlanSubsystems = await processMapPlanRepository.GetCurrent(requestProcessMapPlan, token); foreach (var processMapPlanSubsystem in processMapPlanSubsystems) { var stat = new SubsystemPlanFactStatDto(); stat.IdWell = processMapPlanSubsystem.IdWell; stat.IdWellSectionType = processMapPlanSubsystem.IdWellSectionType; stat.DepthStart = processMapPlanSubsystem.DepthStart < telemetryDataSaubInfo.MinDepth ? telemetryDataSaubInfo.MinDepth : processMapPlanSubsystem.DepthStart; stat.DepthEnd = processMapPlanSubsystem.DepthEnd > telemetryDataSaubInfo.MaxDepth ? telemetryDataSaubInfo.MaxDepth : processMapPlanSubsystem.DepthEnd; stat.AutoRotorPlan = processMapPlanSubsystem.AutoRotor; stat.AutoSlidePlan = processMapPlanSubsystem.AutoSlide; stat.AutoOscillationPlan = processMapPlanSubsystem.AutoOscillation; var subsystemRequest = new SubsystemRequest() { IdWell = telemetriesDict.GetValueOrDefault(telemetryDataSaubItem.Key)!.Value, GeDepth = stat.DepthStart, LeDepth = stat.DepthEnd }; var subsystemStatFact = await GetStatAsync(subsystemRequest, token); var subsystemStatFactDict = subsystemStatFact.ToDictionary(s => s.IdSubsystem); stat.AutoRotorFact = subsystemStatFactDict.GetValueOrDefault(IdSubsystemAPDRotor)?.KUsage * 100; stat.AutoSlideFact = subsystemStatFactDict.GetValueOrDefault(IdSubsystemAPDSlide)?.KUsage * 100; stat.AutoOscillationFact = subsystemStatFactDict.GetValueOrDefault(IdSubsystemOscillation)?.KUsage * 100; result.Add(stat); } } 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, }; if (oscillationStat.SumOperationDepthInterval != 0d) 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 DetectedOperationByWellRequest { IdWell = well.Id, IdsCategories = WellOperationCategory.MechanicalDrillingSubIds, GeDateStart = geDateStartUtc, LeDateEnd = leDateUtc, }; var telemetryOperations = await detectedOperationService .GetOperationsAsync(request, token); var wellStat = new SubsystemActiveWellStatDto { Well = well }; 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); wellsStat.Add(wellStat); } return wellsStat; } }