diff --git a/AsbCloudApp/Data/SAUB/TelemetryDataSaubStatDto.cs b/AsbCloudApp/Data/SAUB/TelemetryDataSaubStatDto.cs index 40514429..8c7b3198 100644 --- a/AsbCloudApp/Data/SAUB/TelemetryDataSaubStatDto.cs +++ b/AsbCloudApp/Data/SAUB/TelemetryDataSaubStatDto.cs @@ -100,7 +100,7 @@ namespace AsbCloudApp.Data.SAUB /// /// Режим САУБ /// - public short Mode { get; set; } + public short IdMode { get; set; } /// /// Текущий критерий бурения diff --git a/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapReportService.cs b/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapReportService.cs index 9cd63e0e..9cd788a8 100644 --- a/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapReportService.cs +++ b/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapReportService.cs @@ -1,13 +1,8 @@ -using AsbCloudApp.Data; -using AsbCloudApp.Data.ProcessMap; +using AsbCloudApp.Data.ProcessMap; using AsbCloudApp.Data.SAUB; using AsbCloudApp.Exceptions; using AsbCloudApp.Repositories; -using AsbCloudApp.Requests; using AsbCloudApp.Services; -using AsbCloudApp.Services.Subsystems; -using AsbCloudDb.Model; -using AsbCloudApp.Data.Subsystems; using System; using System.Collections.Generic; using System.Linq; @@ -23,23 +18,17 @@ namespace AsbCloudInfrastructure.Services.ProcessMap private readonly IWellOperationRepository wellOperationRepository; private readonly IProcessMapPlanRepository processMapPlanRepository; private readonly ITelemetryDataSaubService telemetryDataSaubService; - private readonly ILimitingParameterRepository limitingParameterRepository; - private readonly ISubsystemOperationTimeService subsystemOperationTimeService; public ProcessMapReportService( IWellService wellService, - IWellOperationRepository wellOperationService, + IWellOperationRepository wellOperationRepository, IProcessMapPlanRepository processMapPlanRepository, - ITelemetryDataSaubService telemetryDataSaubService, - ILimitingParameterRepository limitingParameterRepository, - ISubsystemOperationTimeService subsystemOperationTimeService) + ITelemetryDataSaubService telemetryDataSaubService) { this.wellService = wellService; - this.wellOperationRepository = wellOperationService; + this.wellOperationRepository = wellOperationRepository; this.processMapPlanRepository = processMapPlanRepository; this.telemetryDataSaubService = telemetryDataSaubService; - this.limitingParameterRepository = limitingParameterRepository; - this.subsystemOperationTimeService = subsystemOperationTimeService; } /// @@ -53,50 +42,298 @@ namespace AsbCloudInfrastructure.Services.ProcessMap var processMapPlan = await processMapPlanRepository.GetByIdWellAsync(idWell, token); - if(!processMapPlan.Any()) + if (!processMapPlan.Any()) return Enumerable.Empty(); - var telemetryDataStat = await telemetryDataSaubService.GetTelemetryDataStatAsync(idTelemetry, token); + 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, IEnumerable telemetryDataStat) + private IEnumerable CalcByIntervals(IEnumerable processMapPlan, TelemetryDataSaubStatDto[] telemetryDataStat) { - var result = new List(processMapPlan.Count() * 4); - var intervals = GetProcessMapIntervals(processMapPlan); - + 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<(double, double)> GetProcessMapIntervals(IEnumerable processMapPlan) + private IEnumerable CalcSubIntervals( + (double DepthStart, double DepthEnd) interval, + IEnumerable processMapPlanInterval, + Span telemetryDataInterval, + IDictionary sectionTypes) { - - return Enumerable.Empty<(double, double)>(); - } + var telemetryDataIntervalLength = telemetryDataInterval.Length; + if (telemetryDataInterval.Length == 0) + return Enumerable.Empty(); - private static DateTime GetInterpolatedDate(WellOperationDto operation, double depth) - { - var ratio = (depth - operation.DepthStart) / (operation.DepthEnd - operation.DepthStart); - var deltaHours = operation.DurationHours * ratio; - var interpolatedDate = operation.DateStart + TimeSpan.FromHours(deltaHours); - return interpolatedDate; - } + var result = new List(); + var telemetryIndexStart = 0; + var subInterval = interval; - private static IEnumerable<(double min, double max)> SplitByIntervals(double min, double max) - { - const double step = 100; - var iMin = min; - var iMax = (1 + (int)(min / step)) * step; - for (; iMax < max; iMax += step) + for (var i = telemetryIndexStart; i < telemetryDataIntervalLength; i++) { - yield return (iMin, iMax); - iMin = iMax; + 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; + } } - yield return (iMin, max); + + 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 } diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataSaubService.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataSaubService.cs index 8bf021b8..ddebda4b 100644 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataSaubService.cs +++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataSaubService.cs @@ -42,9 +42,9 @@ namespace AsbCloudInfrastructure.Services.SAUB .Where(t => t.IdTelemetry == idTelemetry) .Where(t => t.BlockPosition > 0.0001) .Where(t => t.WellDepth > 0.0001) - .Where(t => t.WellDepth - t.BitDepth < 0.01) .Where(t => t.Mode != null) .Where(t => modes.Contains(t.Mode.Value)) + .Where(t => t.WellDepth - t.BitDepth < 0.01) .GroupBy(t => new { t.DateTime.Hour, WellDepthX10 = Math.Truncate(t.WellDepth!.Value * 10),