using AsbCloudApp.Data; using AsbCloudApp.Data.ProcessMapPlan; using AsbCloudApp.Data.ProcessMaps.Report; using AsbCloudApp.Exceptions; using AsbCloudApp.Repositories; using AsbCloudApp.Requests; using AsbCloudApp.Services; using AsbCloudApp.Services.ProcessMaps.WellDrilling; using AsbCloudDb.Model; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace AsbCloudInfrastructure.Services.ProcessMaps.Report { public class ProcessMapReportDataSaubStatService : IProcessMapReportDataSaubStatService { private readonly IWellService wellService; private readonly IChangeLogRepository processMapPlanBaseRepository; private readonly IDataSaubStatRepository dataSaubStatRepository; private readonly IWellOperationRepository wellOperationRepository; public ProcessMapReportDataSaubStatService(IWellService wellService, IChangeLogRepository processMapPlanBaseRepository, IDataSaubStatRepository dataSaubStatRepository, IWellOperationRepository wellOperationRepository ) { this.wellService = wellService; this.processMapPlanBaseRepository = processMapPlanBaseRepository; this.dataSaubStatRepository = dataSaubStatRepository; this.wellOperationRepository = wellOperationRepository; } public async Task> GetAsync(int idWell, DataSaubStatRequest request, CancellationToken token) { var well = await wellService.GetOrDefaultAsync(idWell, token) ?? throw new ArgumentInvalidException(nameof(idWell), $"Скважина с Id: {idWell} не найдена"); if (!well.IdTelemetry.HasValue) return Enumerable.Empty(); var requestProcessMapPlan = new ProcessMapPlanBaseRequestWithWell(idWell); var processMapPlanWellDrillings = await processMapPlanBaseRepository.Get(requestProcessMapPlan, token); if (!processMapPlanWellDrillings.Any()) return Enumerable.Empty(); var dataSaubStats = (await dataSaubStatRepository.GetAsync(well.IdTelemetry.Value, token)).ToArray(); if (!dataSaubStats.Any()) return Enumerable.Empty(); var requestWellOperationFact = new WellOperationRequest() { IdWell = idWell, OperationType = WellOperation.IdOperationTypeFact }; var wellOperations = await wellOperationRepository .GetAsync(requestWellOperationFact, token); if (!wellOperations.Any()) return Enumerable.Empty(); var timeZone = TimeSpan.FromHours(wellService.GetTimezone(idWell).Hours); var result = CalcByIntervals(request, processMapPlanWellDrillings, dataSaubStats, wellOperations, timeZone); return result; } private IEnumerable CalcByIntervals( DataSaubStatRequest request, IEnumerable processMapPlanWellDrillings, Span dataSaubStats, IEnumerable wellOperations, TimeSpan timeZone ) { var list = new List(); var firstElemInInterval = dataSaubStats[0]; var indexStart = 0; for (var i = 1; i < dataSaubStats.Length; i++) { var currentElem = dataSaubStats[i]; if (IsNewInterval(currentElem, firstElemInInterval, request) || i == dataSaubStats.Length - 1) { var length = i - indexStart; var elem = CalcStat(processMapPlanWellDrillings, dataSaubStats, indexStart, length, wellOperations, timeZone); if (elem != null) list.Add(elem); indexStart = i; firstElemInInterval = currentElem; } } return list; } private ProcessMapReportDataSaubStatDto? CalcStat( IEnumerable processMapPlanDrillingDtos, Span dataSaubStats, int indexStart, int length, IEnumerable wellOperations, TimeSpan timeZone ) { var span = dataSaubStats.Slice(indexStart, length); var firstElemInInterval = span[0]; var lastElemInInterval = span[^1]; var nearestOperation = wellOperations?.MinBy(o => firstElemInInterval.DateStart - o.DateStart); if (nearestOperation is null) return null; var processMapPlanFilteredByDepth = processMapPlanDrillingDtos .Where(x => x.IdWellSectionType == nearestOperation.IdWellSectionType) .Where(x => x.DepthStart >= firstElemInInterval.DepthStart) .Where(x => x.DepthEnd <= lastElemInInterval.DepthEnd) .ToArray(); if (!processMapPlanFilteredByDepth.Any()) return null; var deltaDepth = lastElemInInterval.DepthEnd - firstElemInInterval.DepthStart; var drilledTime = (lastElemInInterval.DateEnd - firstElemInInterval.DateStart).TotalHours; var aggregatedValues = CalcAggregate(span); return new ProcessMapReportDataSaubStatDto() { DateStart = firstElemInInterval.DateStart.ToOffset(timeZone).DateTime, WellSectionTypeName = nearestOperation.WellSectionTypeName ?? string.Empty, DepthStart = firstElemInInterval.DepthStart, DepthEnd = lastElemInInterval.DepthEnd, DeltaDepth = deltaDepth, DrilledTime = drilledTime, DrillingMode = nearestOperation.CategoryName ?? string.Empty, PressureDiff = new ProcessMapReportDataSaubStatParamsDto() { SetpointPlanMax = processMapPlanFilteredByDepth.Max(p => p.DeltaPressurePlan), SetpointPlanMin = processMapPlanFilteredByDepth.Min(p => p.DeltaPressurePlan), SetpointFact = firstElemInInterval.PressureSp - firstElemInInterval.PressureIdle, FactWavg = aggregatedValues.Pressure, Limit = processMapPlanFilteredByDepth.Max(p => p.DeltaPressureLimitMax), SetpointUsage = aggregatedValues.SetpointUsagePressure }, AxialLoad = new ProcessMapReportDataSaubStatParamsDto() { SetpointPlanMax = processMapPlanFilteredByDepth.Max(p => p.AxialLoadPlan), SetpointPlanMin = processMapPlanFilteredByDepth.Min(p => p.AxialLoadPlan), SetpointFact = aggregatedValues.AxialLoadSp, FactWavg = aggregatedValues.AxialLoad, Limit = processMapPlanFilteredByDepth.Max(p => p.AxialLoadLimitMax), SetpointUsage = aggregatedValues.SetpointUsageAxialLoad }, TopDriveTorque = new ProcessMapReportDataSaubStatParamsDto() { SetpointPlanMax = processMapPlanFilteredByDepth.Max(p => p.TopDriveTorquePlan), SetpointPlanMin = processMapPlanFilteredByDepth.Min(p => p.TopDriveTorquePlan), SetpointFact = aggregatedValues.RotorTorqueSp, FactWavg = aggregatedValues.RotorTorque, FactMax = aggregatedValues.RotorTorqueMax, Limit = processMapPlanFilteredByDepth.Max(p => p.TopDriveTorqueLimitMax), SetpointUsage = aggregatedValues.SetpointUsageRotorTorque }, SpeedLimit = new ProcessMapReportDataSaubStatParamsDto { SetpointPlanMax = processMapPlanFilteredByDepth.Max(p => p.RopPlan), SetpointPlanMin = processMapPlanFilteredByDepth.Min(p => p.RopPlan), SetpointFact = aggregatedValues.BlockSpeedSp, FactWavg = deltaDepth / drilledTime, SetpointUsage = aggregatedValues.SetpointUsageRopPlan }, Turnover = new ProcessMapReportDataSaubStatParamsDto { SetpointPlanMax = processMapPlanFilteredByDepth.Max(p => p.TopDriveSpeedPlan), SetpointPlanMin = processMapPlanFilteredByDepth.Min(p => p.TopDriveSpeedPlan), FactWavg = aggregatedValues.RotorSpeed, FactMax = aggregatedValues.RotorSpeedMax }, Flow = new ProcessMapReportDataSaubStatParamsDto { SetpointPlanMax = processMapPlanFilteredByDepth.Max(p => p.FlowPlan), SetpointPlanMin = processMapPlanFilteredByDepth.Min(p => p.FlowPlan), FactWavg = aggregatedValues.MaxFlow, Limit = processMapPlanFilteredByDepth.Max(p => p.FlowLimitMax), }, Rop = new PlanFactDto { Plan = CalcRopPlan(processMapPlanFilteredByDepth), Fact = deltaDepth / drilledTime }, }; } private ( double Pressure, double AxialLoadSp, double AxialLoad, double RotorTorqueSp, double RotorTorque, double RotorTorqueMax, double BlockSpeedSp, double RotorSpeed, double RotorSpeedMax, double MaxFlow, double SetpointUsagePressure, double SetpointUsageAxialLoad, double SetpointUsageRotorTorque, double SetpointUsageRopPlan ) CalcAggregate(Span span) { var sumPressure = 0.0; var sumAxialLoadSp = 0.0; var sumAxialLoad = 0.0; var sumRotorTorqueSp = 0.0; var sumRotorTorque = 0.0; var sumBlockSpeedSp = 0.0; var sumRotorSpeed = 0.0; var maxFlow = 0.0; var maxRotorTorque = 0.0; var maxRotorSpeed = 0.0; var sumDiffDepthByPressure = 0.0; var sumDiffDepthByAxialLoad = 0.0; var sumDiffDepthByRotorTorque = 0.0; var sumDiffDepthByRopPlan = 0.0; var diffDepthTotal = 0.0; for (var i = 0; i < span.Length; i++) { var diffDepth = span[i].DepthEnd - span[i].DepthStart; sumPressure += diffDepth * span[i].Pressure; sumAxialLoadSp += diffDepth * (span[i].AxialLoadSp ?? 0); sumAxialLoad += diffDepth * span[i].AxialLoad; sumRotorTorqueSp += diffDepth * (span[i].RotorTorqueSp ?? 0); sumRotorTorque += diffDepth * span[i].RotorTorque; sumBlockSpeedSp += diffDepth * (span[i].BlockSpeedSp ?? 0); sumRotorSpeed += diffDepth * span[i].RotorSpeed; maxFlow = span[i].Flow > maxFlow ? span[i].Flow : maxFlow; maxRotorTorque = span[i].RotorTorque > maxRotorTorque ? span[i].RotorTorque : maxRotorTorque; maxRotorSpeed = span[i].RotorSpeed > maxRotorSpeed ? span[i].RotorSpeed : maxRotorSpeed; if (span[i].IdFeedRegulator == LimitingParameterDto.Pressure) sumDiffDepthByPressure += span[i].DepthEnd - span[i].DepthStart; if (span[i].IdFeedRegulator == LimitingParameterDto.AxialLoad) sumDiffDepthByAxialLoad += span[i].DepthEnd - span[i].DepthStart; if (span[i].IdFeedRegulator == LimitingParameterDto.RotorTorque) sumDiffDepthByRotorTorque += span[i].DepthEnd - span[i].DepthStart; if (span[i].IdFeedRegulator == LimitingParameterDto.RopPlan) sumDiffDepthByRopPlan += span[i].DepthEnd - span[i].DepthStart; diffDepthTotal += diffDepth; } return ( Pressure: sumPressure / diffDepthTotal, AxialLoadSp: sumAxialLoadSp / diffDepthTotal, AxialLoad: sumAxialLoad / diffDepthTotal, RotorTorqueSp: sumRotorTorqueSp / diffDepthTotal, RotorTorque: sumRotorTorque / diffDepthTotal, RotorTorqueMax: maxRotorTorque, BlockSpeedSp: sumBlockSpeedSp / diffDepthTotal, RotorSpeed: sumRotorSpeed / diffDepthTotal, RotorSpeedMax: maxRotorSpeed, MaxFlow: maxFlow, SetpointUsagePressure: sumDiffDepthByPressure / diffDepthTotal, SetpointUsageAxialLoad: sumDiffDepthByAxialLoad / diffDepthTotal, SetpointUsageRotorTorque: sumDiffDepthByRotorTorque / diffDepthTotal, SetpointUsageRopPlan: sumDiffDepthByRopPlan / diffDepthTotal ); } private double CalcRopPlan(ProcessMapPlanDrillingDto[] processMapPlanFilteredByDepth) { var sumRopPlan = 0.0; var diffDepthTotal = 0.0; for (var i = 0; i < processMapPlanFilteredByDepth.Length; i++) { var diffDepth = processMapPlanFilteredByDepth[i].DepthEnd - processMapPlanFilteredByDepth[i].DepthStart; sumRopPlan += diffDepth * processMapPlanFilteredByDepth[i].RopPlan; diffDepthTotal += diffDepth; } return sumRopPlan / diffDepthTotal; } private bool IsNewInterval(DataSaubStatDto currentElem, DataSaubStatDto firstElem, DataSaubStatRequest request) { bool isNewElemBySpeed(double currentSpeed, double firstSpeed) { //2. Изменение уставки скорости подачи от первого значения в начале интервала при условии: //скорость > 80 м/ч => изменение уставки на ± 20 м/ч; //скорость > 30 м/ч => изменение уставки на ± 15 м/ч; //скорость <= 30 м/ч => изменение уставки на ± 5 м/ч; if (firstSpeed > 80) return Math.Abs(currentSpeed - firstSpeed) >= 20; else if (firstSpeed > 30) return Math.Abs(currentSpeed - firstSpeed) >= 15; else return Math.Abs(currentSpeed - firstSpeed) >= 5; } var isNewElem = (currentElem.IdCategory != firstElem.IdCategory) || (Math.Abs(currentElem.Pressure - firstElem.Pressure) >= request.DeltaPressure) || (Math.Abs(currentElem.AxialLoad - firstElem.AxialLoad) >= request.DeltaAxialLoad) || (Math.Abs(currentElem.RotorTorque - firstElem.RotorTorque) >= request.DeltaRotorTorque) || (Math.Abs((currentElem.AxialLoadSp ?? 0) - (firstElem.AxialLoadSp ?? 0)) >= request.DeltaAxialLoadSp) || (Math.Abs((currentElem.RotorTorqueSp ?? 0) - (firstElem.RotorTorqueSp ?? 0)) >= request.DeltaRotorTorqueSp) || (isNewElemBySpeed(currentElem.Speed, firstElem.Speed)); return isNewElem; } } }