using AsbCloudApp.Data; using AsbCloudApp.Data.ProcessMapPlan; using AsbCloudApp.Data.ProcessMaps.Report; using AsbCloudApp.Exceptions; using AsbCloudApp.Extensions; 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 geDepth = processMapPlanWellDrillings.Min(p => p.DepthStart); var leDepth = processMapPlanWellDrillings.Max(p => p.DepthEnd); var requestWellOperationFact = new WellOperationRequest() { IdWell = idWell, OperationType = WellOperation.IdOperationTypeFact, GeDepth = geDepth, LeDepth = leDepth }; var wellOperations = await wellOperationRepository .GetAsync(requestWellOperationFact, token); if (!wellOperations.Any()) return Enumerable.Empty(); var dataSaubStats = (await dataSaubStatRepository.GetAsync(well.IdTelemetry.Value, geDepth, leDepth, token)).ToArray(); if (!dataSaubStats.Any()) return Enumerable.Empty(); var wellOperationCategories = wellOperationRepository.GetCategories(false); var result = CalcByIntervals(request, processMapPlanWellDrillings, dataSaubStats, wellOperations, wellOperationCategories); return result; } private IEnumerable CalcByIntervals( DataSaubStatRequest request, IEnumerable processMapPlanWellDrillings, Span dataSaubStats, IEnumerable wellOperations, IEnumerable wellOperationCategories ) { 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 span = dataSaubStats.Slice(indexStart, length); indexStart = i; firstElemInInterval = currentElem; var firstElemInSpan = span[0]; var lastElemInISpan = span[^1]; var nearestOperation = wellOperations.MinBy(o => firstElemInSpan.DateStart - o.DateStart); if (nearestOperation is null) continue; var processMapPlanFilteredByDepth = processMapPlanWellDrillings .Where(x => x.IdWellSectionType == nearestOperation.IdWellSectionType) .Where(x => x.DepthStart >= firstElemInSpan.DepthStart) .Where(x => x.DepthEnd <= lastElemInISpan.DepthEnd) .WhereActualAtMoment(DateTimeOffset.Now) .ToArray(); if (!processMapPlanFilteredByDepth.Any()) continue; var wellOperationCategoryName = wellOperationCategories. Where(c => c.Id == currentElem.IdCategory) .FirstOrDefault() ?.Name ?? string.Empty; var elem = CalcStat(processMapPlanFilteredByDepth, span, nearestOperation, wellOperationCategoryName); if (elem is not null) list.Add(elem); } } return list; } private ProcessMapReportDataSaubStatDto? CalcStat( ProcessMapPlanDrillingDto[] processMapPlanFilteredByDepth, Span span, WellOperationDto nearestOperation, string wellOperationCategoryName ) { var firstElemInInterval = span[0]; var lastElemInInterval = span[^1]; var deltaDepth = lastElemInInterval.DepthEnd - firstElemInInterval.DepthStart; var aggregatedValues = CalcAggregate(span); return new ProcessMapReportDataSaubStatDto() { DateStart = firstElemInInterval.DateStart.DateTime, WellSectionTypeName = nearestOperation.WellSectionTypeName ?? string.Empty, DepthStart = firstElemInInterval.DepthStart, DepthEnd = lastElemInInterval.DepthEnd, DeltaDepth = deltaDepth, DrilledTime = aggregatedValues.DrilledTime, DrillingMode = wellOperationCategoryName, 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 / aggregatedValues.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 / aggregatedValues.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, double DrilledTime ) 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; var drilledTime = 0.0; for (var i = 0; i < span.Length; i++) { var diffDepth = span[i].DepthEnd - span[i].DepthStart; sumPressure += diffDepth * (span[i].Pressure - (span[i].PressureIdle ?? 0.0)); 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 += diffDepth; if (span[i].IdFeedRegulator == LimitingParameterDto.AxialLoad) sumDiffDepthByAxialLoad += diffDepth; if (span[i].IdFeedRegulator == LimitingParameterDto.RotorTorque) sumDiffDepthByRotorTorque += diffDepth; if (span[i].IdFeedRegulator == LimitingParameterDto.RopPlan) sumDiffDepthByRopPlan += diffDepth; diffDepthTotal += diffDepth; drilledTime += (span[i].DateEnd - span[i].DateStart).TotalHours; } 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, DrilledTime: drilledTime ); } 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; } } }