using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using AsbCloudApp.Data; using AsbCloudApp.Data.ProcessMaps; using AsbCloudApp.Data.ProcessMaps.Report; using AsbCloudApp.Data.SAUB; using AsbCloudApp.Exceptions; using AsbCloudApp.Repositories; using AsbCloudApp.Services; using AsbCloudApp.Services.ProcessMaps.WellDrilling; using AsbCloudInfrastructure.Services.ProcessMaps.Report.Data; namespace AsbCloudInfrastructure.Services.ProcessMaps.Report; public class ProcessMapReportWellDrillingService : IProcessMapReportWellDrillingService { private readonly IWellService wellService; private readonly IProcessMapPlanRepository processMapPlanWellDrillingRepository; private readonly ITelemetryDataSaubService telemetryDataSaubService; private readonly IWellOperationRepository wellOperationRepository; public ProcessMapReportWellDrillingService(IWellService wellService, IProcessMapPlanRepository processMapPlanWellDrillingRepository, ITelemetryDataSaubService telemetryDataSaubService, IWellOperationRepository wellOperationRepository) { this.wellService = wellService; this.processMapPlanWellDrillingRepository = processMapPlanWellDrillingRepository; this.telemetryDataSaubService = telemetryDataSaubService; this.wellOperationRepository = wellOperationRepository; } public async Task> GetAsync(int idWell, CancellationToken token) { var well = await wellService.GetOrDefaultAsync(idWell, token) ?? throw new ArgumentInvalidException(nameof(idWell), $"Скважина с Id: {idWell} не найдена"); if (!well.IdTelemetry.HasValue) return Enumerable.Empty(); var processMapPlanWellDrillings = await processMapPlanWellDrillingRepository.GetByIdWellAsync(idWell, token); if (!processMapPlanWellDrillings.Any()) return Enumerable.Empty(); var telemetryDataStat = (await telemetryDataSaubService.GetTelemetryDataStatAsync(well.IdTelemetry.Value, token)).ToArray(); if (!telemetryDataStat.Any()) return Enumerable.Empty(); var result = CalcByIntervals(processMapPlanWellDrillings, telemetryDataStat); return result; } private IEnumerable CalcByIntervals( IEnumerable processMapPlanWellDrillings, TelemetryDataSaubStatDto[] telemetryDataStat) { var processMapIntervals = CalcDepthIntervals(processMapPlanWellDrillings); var result = new List(processMapIntervals.Count() * 4); var telemetryIndexStart = Array.FindIndex(telemetryDataStat, t => t.WellDepthMin >= processMapIntervals.First().DepthStart); if (telemetryIndexStart < 0) return Enumerable.Empty(); IDictionary sectionTypes = wellOperationRepository .GetSectionTypes() .ToDictionary(s => s.Id, s => s.Caption); foreach (var interval in processMapIntervals) { var processMapPlanWellDrillingInterval = processMapPlanWellDrillings .Where(p => p.DepthStart <= interval.DepthEnd && p.DepthEnd >= interval.DepthStart); if (!processMapPlanWellDrillingInterval.Any()) continue; var telemetryIndexEnd = Array.FindIndex(telemetryDataStat, telemetryIndexStart, t => t.WellDepthMin >= interval.DepthEnd); if (telemetryIndexEnd < 0) telemetryIndexEnd = telemetryDataStat.Length - 1; var telemetryDataInterval = telemetryDataStat.AsSpan(telemetryIndexStart, telemetryIndexEnd - telemetryIndexStart); IEnumerable subIntervalsResult = CalcSubIntervals(interval, processMapPlanWellDrillingInterval, telemetryDataInterval, sectionTypes); result.AddRange(subIntervalsResult); telemetryIndexStart = telemetryIndexEnd; } return result; } private static IEnumerable<(double DepthStart, double DepthEnd)> CalcDepthIntervals( IEnumerable processMapPlanWellDrillings) { if (!processMapPlanWellDrillings.Any()) yield break; var intervalStarts = processMapPlanWellDrillings .OrderBy(i => i.DepthStart) .Select(p => p.DepthStart) .Distinct() .ToArray(); for (var i = 1; i < intervalStarts.Length; i++) yield return (intervalStarts[i - 1], intervalStarts[i]); yield return (intervalStarts[^1], processMapPlanWellDrillings.Max(p => p.DepthEnd)); } private static IEnumerable CalcSubIntervals( (double DepthStart, double DepthEnd) interval, IEnumerable processMapPlanWellDrillingInterval, Span telemetryDataInterval, IDictionary sectionTypes) { var telemetryDataIntervalLength = telemetryDataInterval.Length; if (telemetryDataInterval.Length == 0) return Enumerable.Empty(); var result = new List(); var telemetryIndexStart = 0; var subInterval = interval; for (var i = telemetryIndexStart + 1; i < telemetryDataIntervalLength; i++) { if (IsDifferent(telemetryDataInterval[telemetryIndexStart], telemetryDataInterval[i])) { subInterval.DepthEnd = telemetryDataInterval[i - 1].WellDepthMax; var telemetryRowSpan = telemetryDataInterval[telemetryIndexStart..(i - 1)]; if (!telemetryRowSpan.IsEmpty) { var intervalReportRow = CalcSubIntervalReportRow(subInterval, processMapPlanWellDrillingInterval, telemetryRowSpan, sectionTypes); result.Add(intervalReportRow); } telemetryIndexStart = i; subInterval.DepthStart = subInterval.DepthEnd; } } subInterval.DepthEnd = interval.DepthEnd; var intervalReportRowLast = CalcSubIntervalReportRow(subInterval, processMapPlanWellDrillingInterval, telemetryDataInterval[telemetryIndexStart..telemetryDataIntervalLength], sectionTypes); result.Add(intervalReportRowLast); return result; } private static ProcessMapReportWellDrillingDto CalcSubIntervalReportRow( (double DepthStart, double DepthEnd) subInterval, IEnumerable processMapPlanWellDrillings, Span telemetryRowSpan, IDictionary sectionTypes) { var telemetryStat = new TelemetryStat(telemetryRowSpan); var processMapByMode = processMapPlanWellDrillings.FirstOrDefault(p => p.IdMode == telemetryStat.IdMode); var processMapFirst = processMapPlanWellDrillings.First(); var idWellSectionType = processMapByMode?.IdWellSectionType ?? processMapFirst.IdWellSectionType; var result = new ProcessMapReportWellDrillingDto { IdWell = processMapByMode?.IdWell ?? processMapFirst.IdWell, IdWellSectionType = idWellSectionType, WellSectionTypeName = sectionTypes[idWellSectionType], DepthStart = subInterval.DepthStart, DepthEnd = subInterval.DepthEnd, DateStart = telemetryStat.DateStart, MechDrillingHours = telemetryStat.DrillingHours, DrillingMode = telemetryStat.ModeName, DeltaDepth = telemetryStat.DeltaDepth, PressureDiff = telemetryStat.Pressure.MakeParams(processMapByMode?.Pressure.Plan), AxialLoad = telemetryStat.AxialLoad.MakeParams(processMapByMode?.AxialLoad.Plan), TopDriveTorque = telemetryStat.RotorTorque.MakeParams(processMapByMode?.TopDriveTorque.Plan), SpeedLimit = telemetryStat.BlockSpeed.MakeParams(processMapByMode?.RopPlan), Rop = new PlanFactDto { Plan = processMapByMode?.RopPlan, Fact = telemetryStat.Rop }, UsagePlan = processMapByMode?.UsageSaub ?? telemetryStat.UsagePredictPlan, UsageFact = telemetryStat.UsageSaub, }; return result; } private static bool IsDifferent(TelemetryDataSaubStatDto intervalStart, TelemetryDataSaubStatDto current) { if (intervalStart.WellDepthMin > current.WellDepthMin) return true; if (intervalStart.IdMode != current.IdMode) return true; if (Math.Abs(intervalStart.PressureSp - current.PressureSp) > 5d) return true; if (Math.Abs(intervalStart.AxialLoadSp - current.AxialLoadSp) > 1d) return true; if (Math.Abs(intervalStart.RotorTorqueSp - current.RotorTorqueSp) > 5d) return true; var blockSpeedSpDiff = Math.Abs(intervalStart.BlockSpeedSp - current.BlockSpeedSp); if (!(blockSpeedSpDiff > 5d)) return false; switch (intervalStart.BlockSpeedSp) { case <= 30: case > 30 when blockSpeedSpDiff > 15d: case > 80 when blockSpeedSpDiff > 20d: return true; } return false; } }