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; private readonly IWellOperationCategoryRepository wellOperationCategoryRepository; public ProcessMapReportDataSaubStatService(IWellService wellService, IChangeLogRepository processMapPlanBaseRepository, IDataSaubStatRepository dataSaubStatRepository, IWellOperationRepository wellOperationRepository, IWellOperationCategoryRepository wellOperationCategoryRepository ) { this.wellService = wellService; this.processMapPlanBaseRepository = processMapPlanBaseRepository; this.dataSaubStatRepository = dataSaubStatRepository; this.wellOperationRepository = wellOperationRepository; this.wellOperationCategoryRepository = wellOperationCategoryRepository; } 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 = wellOperationCategoryRepository.Get(false); var wellSectionTypes = wellOperationRepository.GetSectionTypes(); var result = CalcByIntervals( request, processMapPlanWellDrillings, dataSaubStats, wellOperations, wellOperationCategories, wellSectionTypes); return result; } private IEnumerable CalcByIntervals( DataSaubStatRequest request, IEnumerable processMapPlanWellDrillings, Span dataSaubStats, IEnumerable wellOperations, IEnumerable wellOperationCategories, IEnumerable wellSectionTypes ) { var list = new List(); var firstElemInInterval = dataSaubStats[0]; int GetSection(DataSaubStatDto data) => wellOperations.MinBy(o => data.DateStart - o.DateStart)!.IdWellSectionType; ProcessMapPlanDrillingDto? GetProcessMapPlan(int idWellSectionType, DataSaubStatDto data) => processMapPlanWellDrillings .Where(p => p.IdWellSectionType == idWellSectionType) .Where(p => p.DepthStart <= data.DepthStart) .Where(p => p.DepthEnd >= data.DepthStart) .Where(p => IsModeMatchOperationCategory(p.IdMode, data.IdCategory)) .WhereActualAtMoment(data.DateStart) .FirstOrDefault(); var idWellSectionType = GetSection(firstElemInInterval); var prevProcessMapPlan = GetProcessMapPlan(idWellSectionType, firstElemInInterval); var indexStart = 0; for (var i = 1; i < dataSaubStats.Length; i++) { var currentElem = dataSaubStats[i]; idWellSectionType = GetSection(currentElem); var processMapPlan = GetProcessMapPlan(idWellSectionType, currentElem); if (IsNewInterval(currentElem, firstElemInInterval, request) || i == dataSaubStats.Length - 1 || processMapPlan != prevProcessMapPlan) { prevProcessMapPlan = processMapPlan; var length = i - indexStart; var span = dataSaubStats.Slice(indexStart, length); indexStart = i; firstElemInInterval = currentElem; var firstElemInSpan = span[0]; var lastElemInISpan = span[^1]; var wellOperationCategoryName = wellOperationCategories .Where(c => c.Id == firstElemInSpan.IdCategory) .FirstOrDefault()?.Name ?? string.Empty; var wellSectionTypeName = wellSectionTypes .Where(c => c.Id == idWellSectionType) .FirstOrDefault()?.Caption ?? string.Empty; var elem = CalcStat(processMapPlan, span, wellOperationCategoryName, wellSectionTypeName); if (elem is not null) list.Add(elem); } } return list; } private bool IsModeMatchOperationCategory(int idMode, int idCategory) { return (idMode == 1 && idCategory == 5003) || (idMode == 2 && idCategory == 5002); } private ProcessMapReportDataSaubStatDto? CalcStat( ProcessMapPlanDrillingDto? processMapPlanFilteredByDepth, Span span, string wellOperationCategoryName, string wellSectionTypeName ) { 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 = wellSectionTypeName, DepthStart = firstElemInInterval.DepthStart, DepthEnd = lastElemInInterval.DepthEnd, DeltaDepth = deltaDepth, DrilledTime = aggregatedValues.DrilledTime, DrillingMode = wellOperationCategoryName, PressureDiff = new ProcessMapReportDataSaubStatParamsDto() { SetpointPlan = processMapPlanFilteredByDepth?.DeltaPressurePlan, SetpointFact = firstElemInInterval.PressureSp - firstElemInInterval.PressureIdle, FactWavg = aggregatedValues.Pressure, Limit = processMapPlanFilteredByDepth?.DeltaPressureLimitMax, SetpointUsage = aggregatedValues.SetpointUsagePressure }, AxialLoad = new ProcessMapReportDataSaubStatParamsDto() { SetpointPlan = processMapPlanFilteredByDepth?.AxialLoadPlan, SetpointFact = aggregatedValues.AxialLoadSp, FactWavg = aggregatedValues.AxialLoad, Limit = processMapPlanFilteredByDepth?.AxialLoadLimitMax, SetpointUsage = aggregatedValues.SetpointUsageAxialLoad }, TopDriveTorque = new ProcessMapReportDataSaubStatParamsDto() { SetpointPlan = processMapPlanFilteredByDepth?.TopDriveTorquePlan, SetpointFact = aggregatedValues.RotorTorqueSp, FactWavg = aggregatedValues.RotorTorque, FactMax = aggregatedValues.RotorTorqueMax, Limit = processMapPlanFilteredByDepth?.TopDriveTorqueLimitMax, SetpointUsage = aggregatedValues.SetpointUsageRotorTorque }, SpeedLimit = new ProcessMapReportDataSaubStatParamsDto { SetpointPlan = processMapPlanFilteredByDepth?.RopPlan, SetpointFact = aggregatedValues.BlockSpeedSp, FactWavg = deltaDepth / aggregatedValues.DrilledTime, SetpointUsage = aggregatedValues.SetpointUsageRopPlan }, Turnover = new ProcessMapReportDataSaubStatParamsDto { SetpointPlan = processMapPlanFilteredByDepth?.TopDriveSpeedPlan, FactWavg = aggregatedValues.RotorSpeed, FactMax = aggregatedValues.RotorSpeedMax }, Flow = new ProcessMapReportDataSaubStatParamsDto { SetpointPlan = processMapPlanFilteredByDepth?.FlowPlan, FactWavg = aggregatedValues.MaxFlow, Limit = processMapPlanFilteredByDepth?.FlowLimitMax, }, Rop = new PlanFactDto { Plan = processMapPlanFilteredByDepth?.RopPlan, 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; } } }