using AsbCloudApp.Data.ProcessMap; using AsbCloudApp.Data.SAUB; using AsbCloudApp.Exceptions; using AsbCloudApp.Repositories; using AsbCloudApp.Services; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace AsbCloudInfrastructure.Services.ProcessMap { #nullable enable public partial class ProcessMapReportService : IProcessMapReportService { private readonly IWellService wellService; private readonly IWellOperationRepository wellOperationRepository; private readonly IProcessMapPlanRepository processMapPlanRepository; private readonly ITelemetryDataSaubService telemetryDataSaubService; public ProcessMapReportService( IWellService wellService, IWellOperationRepository wellOperationRepository, IProcessMapPlanRepository processMapPlanRepository, ITelemetryDataSaubService telemetryDataSaubService) { this.wellService = wellService; this.wellOperationRepository = wellOperationRepository; this.processMapPlanRepository = processMapPlanRepository; this.telemetryDataSaubService = telemetryDataSaubService; } /// public async Task> GetProcessMapAsync(int idWell, CancellationToken token) { var well = wellService.GetOrDefault(idWell) ?? throw new ArgumentInvalidException("idWell not found", nameof(idWell)); var idTelemetry = well.IdTelemetry ?? throw new ArgumentInvalidException("telemetry by well not found", nameof(idWell)); var processMapPlan = await processMapPlanRepository.GetByIdWellAsync(idWell, token); if (!processMapPlan.Any()) return Enumerable.Empty(); var telemetryDataStat = (await telemetryDataSaubService.GetTelemetryDataStatAsync(idTelemetry, token)).ToArray(); if (!telemetryDataStat.Any()) return Enumerable.Empty(); var result = CalcByIntervals(processMapPlan, telemetryDataStat); return result; } private IEnumerable CalcByIntervals(IEnumerable processMapPlan, TelemetryDataSaubStatDto[] telemetryDataStat) { var processMapIntervals = processMapPlan .Select(p => (p.DepthStart, p.DepthEnd)) .Distinct() .OrderBy(i => i.DepthStart); 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(); foreach (var interval in processMapIntervals) { var processMapPlanInterval = processMapPlan .Where(p => p.DepthStart >= interval.DepthStart && p.DepthEnd <= interval.DepthEnd); var telemetryIndexEnd = Array.FindIndex(telemetryDataStat, telemetryIndexStart, t => t.WellDepthMin >= interval.DepthEnd); var telemetryDataInterval = telemetryDataStat.AsSpan(telemetryIndexStart, telemetryIndexEnd - telemetryIndexStart); IEnumerable subIntervalsResult = CalcSubIntervals(interval, processMapPlanInterval, telemetryDataInterval, sectionTypes); result.AddRange(subIntervalsResult); telemetryIndexStart = telemetryIndexEnd; } return result; } private IEnumerable CalcSubIntervals( (double DepthStart, double DepthEnd) interval, IEnumerable processMapPlanInterval, 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; i < telemetryDataIntervalLength; i++) { if (!IsSimilar(telemetryDataInterval[telemetryIndexStart], telemetryDataInterval[i])) { subInterval.DepthEnd = telemetryDataInterval[i].WellDepthMax; var intervalReportRow = CalcSubIntervalReportRow(subInterval, processMapPlanInterval, telemetryDataInterval[telemetryIndexStart..i], sectionTypes); result.Add(intervalReportRow); telemetryIndexStart = i; subInterval.DepthStart = subInterval.DepthEnd; } } subInterval.DepthEnd = interval.DepthEnd; var intervalReportRowLast = CalcSubIntervalReportRow(subInterval, processMapPlanInterval, telemetryDataInterval[telemetryIndexStart..telemetryDataIntervalLength], sectionTypes); result.Add(intervalReportRowLast); return result; } private ProcessMapReportDto CalcSubIntervalReportRow( (double DepthStart, double DepthEnd) subInterval, IEnumerable processMap, Span telemetryRowSpan, IDictionary sectionTypes) { var telemetryFirst = telemetryRowSpan[0]; var telemetryLast = telemetryRowSpan[^1]; var mode = GetIdMode(telemetryRowSpan); var processMapByMode = processMap.FirstOrDefault(p => p.IdMode == mode.IdMode); var processMapFirst = processMap.First(); var idWellSectionType = processMapByMode?.IdWellSectionType ?? processMapFirst.IdWellSectionType; var telemetryStat = AnalyzeTelemetry(telemetryRowSpan); var result = new ProcessMapReportDto { IdWell = processMapByMode?.IdWell ?? processMapFirst.IdWell, IdWellSectionType = idWellSectionType, WellSectionTypeName = sectionTypes[idWellSectionType], DepthStart = subInterval.DepthStart, DepthEnd = subInterval.DepthEnd, DateStart = telemetryFirst.DateMin, MechDrillingHours = (telemetryLast.DateMax - telemetryFirst.DateMin).TotalHours, DrillingMode = mode.ModeName, DeltaDepth = telemetryLast.WellDepthMax - telemetryFirst.WellDepthMin, PressureDiff = telemetryStat.Pressure.MakeParams(processMapByMode?.Pressure.Plan), AxialLoad = telemetryStat.AxialLoad.MakeParams(processMapByMode?.AxialLoad.Plan), TopDriveTorque = telemetryStat.RotorTorque.MakeParams(processMapByMode?.TopDriveTorque.Plan), SpeedL​imit = telemetryStat.BlockSpeed.MakeParams(processMapByMode?.RopPlan), Rop = telemetryStat.Rop, Usage = telemetryStat.UsageSaub, }; return result; } private static TelemetryStat AnalyzeTelemetry(Span telemetry) { var stat = new TelemetryStat(); for (int i = 0; i < telemetry.Length; i++) stat.UpdateStat(telemetry[i]); return stat; } private static (int IdMode, string ModeName) GetIdMode(Span telemetryRowSpan) { /// Режим работы САУБ в телеметрии: /// 0 - "РУЧНОЙ" /// 1 - "БУРЕНИЕ В РОТОРЕ" /// 3 - "БУРЕНИЕ В СЛАЙДЕ" for (int i = 0; i < telemetryRowSpan.Length; i++) { var idMode = telemetryRowSpan[i].IdMode; if (idMode == 0) return (0, "Ручной"); if (idMode == 1) return (1, "Ротор"); if (idMode == 3) return (2, "Слайд"); } return (0, "Ручной"); } private static bool IsSimilar(TelemetryDataSaubStatDto telemetry1, TelemetryDataSaubStatDto telemetry2) { if (telemetry1.IdMode != telemetry2.IdMode) return false; if (Math.Abs(telemetry1.PressureSp - telemetry2.PressureSp) > 5d) return false; if (Math.Abs(telemetry1.AxialLoadSp - telemetry2.AxialLoadSp) > 1d) return false; if (Math.Abs(telemetry1.RotorTorqueSp - telemetry2.RotorTorqueSp) > 5d) return false; var blockSpeedSpDiff = Math.Abs(telemetry1.BlockSpeedSp - telemetry2.BlockSpeedSp); if (blockSpeedSpDiff > 5d) { if (telemetry1.BlockSpeedSp < 30) return false; else if (telemetry1.BlockSpeedSp > 30 && blockSpeedSpDiff > 15d) return false; else if (telemetry1.BlockSpeedSp > 80 && blockSpeedSpDiff > 20d) return false; } return true; } } class ParamStat { private double spWSum; private double pvWSum; private double? limitMaxWSum; private double spUsageDepth; private double deltaDepthSum; private readonly Func getterSp; private readonly Func getterPv; private readonly Func? getterLimitMax; private readonly int idFeedRegulator; private TelemetryDataSaubStatDto? previous; public ParamStat(Func getterSp, Func getterPv, Func? getterLimitMax, int idFeedRegulator) { this.getterSp = getterSp; this.getterPv = getterPv; this.getterLimitMax = getterLimitMax; this.idFeedRegulator = idFeedRegulator; } public void UpdateStat(TelemetryDataSaubStatDto current) { if(previous is not null) { var deltaDepth = current.WellDepthMin - previous.WellDepthMin; if (deltaDepth > 0) { var deltaDepthHalf = deltaDepth / 2; double CalculateWeight(Func getter) => (getter(previous!) + getter(current)) * deltaDepthHalf; spWSum = CalculateWeight(getterSp); pvWSum = CalculateWeight(getterPv); if(getterLimitMax is not null) limitMaxWSum = CalculateWeight(getterLimitMax!); if (current.IdFeedRegulator == idFeedRegulator) spUsageDepth += deltaDepth; deltaDepthSum += deltaDepth; } } previous = current; } public ProcessMapReportParamsDto MakeParams(double? spPlan) => new ProcessMapReportParamsDto { SetpointPlan = spPlan, SetpointFact = spWSum / deltaDepthSum, Fact = pvWSum / deltaDepthSum, Limit = limitMaxWSum / deltaDepthSum, PercDrillingBySetpoint = spUsageDepth / deltaDepthSum, }; } class TelemetryStat { public ParamStat Pressure { get; } public ParamStat AxialLoad {get; } public ParamStat RotorTorque {get; } public ParamStat BlockSpeed {get; } private TelemetryDataSaubStatDto? previous; private double depthSum = 0d; private double hoursSum = 0d; public double Rop => depthSum / hoursSum; private double depthWithSaub = 0d; public double UsageSaub => depthWithSaub / depthSum; public TelemetryStat() { BlockSpeed = new(t => t.BlockSpeedSp, t => t.BlockSpeed, null, 1); Pressure = new(t => t.PressureSp, t => t.Pressure, t=>t.PressureDeltaLimitMax, 2); RotorTorque = new(t => t.RotorTorqueSp, t => t.RotorTorque, t=>t.RotorTorqueLimitMax, 3); AxialLoad = new(t => t.AxialLoadSp, t => t.AxialLoad, t=>t.AxialLoadLimitMax, 4); } public void UpdateStat(TelemetryDataSaubStatDto current) { if(previous is not null) { var deltaDepth = current.WellDepthMin - previous.WellDepthMin; if(deltaDepth > 0) { var deltaHours = (current.DateMin - previous.DateMax).TotalHours; depthSum += deltaDepth; hoursSum += deltaHours; if(current.IdMode == 1 || current.IdMode == 3) depthWithSaub += deltaDepth; } } previous = current; Pressure.UpdateStat(current); AxialLoad.UpdateStat(current); RotorTorque.UpdateStat(current); BlockSpeed.UpdateStat(current); } }; #nullable disable }