using AsbCloudApp.Data; using AsbCloudApp.Data.ProcessMaps; 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; using AsbCloudApp.Data.WellOperation; namespace AsbCloudInfrastructure.Services.ProcessMaps.Report; public class ProcessMapReportDrillingService : IProcessMapReportDrillingService { private readonly IWellService wellService; private readonly IChangeLogRepository processMapPlanBaseRepository; private readonly IDataSaubStatRepository dataSaubStatRepository; private readonly IWellOperationRepository wellOperationRepository; private readonly IWellOperationCategoryRepository wellOperationCategoryRepository; public ProcessMapReportDrillingService(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(new[] { idWell }) { OperationType = WellOperation.IdOperationTypeFact, GeDepth = geDepth, LeDepth = leDepth }; var wellOperations = await wellOperationRepository .GetAsync(requestWellOperationFact, token); var orderedWellOperations = wellOperations .OrderBy(operation => operation.DateStart) .ToArray(); if (!wellOperations.Any()) return Enumerable.Empty(); var geDate = wellOperations.Min(p => p.DateStart); var leDate = wellOperations.Max(p => (p.DateStart.AddHours(p.DurationHours))); var dataSaubStats = (await dataSaubStatRepository.GetAsync(well.IdTelemetry.Value, geDate, leDate, token)).ToArray(); if (!dataSaubStats.Any()) return Enumerable.Empty(); var wellOperationCategories = wellOperationCategoryRepository.Get(false); var wellSectionTypes = wellOperationRepository.GetSectionTypes(); var result = CalcByIntervals( request, processMapPlanWellDrillings, dataSaubStats, orderedWellOperations, wellOperationCategories, wellSectionTypes); return result; } private static IEnumerable CalcByIntervals( DataSaubStatRequest request, IEnumerable processMapPlanWellDrillings, Span dataSaubStats, IEnumerable wellOperations, IEnumerable wellOperationCategories, IEnumerable wellSectionTypes ) { var list = new List(); var firstElemInInterval = dataSaubStats[0]; var orderedWellOperations = wellOperations .OrderBy(o => o.DateStart) .ToArray(); var lastFoundIndex = 0; int GetSection(DataSaubStatDto data) { if(lastFoundIndex < orderedWellOperations.Length - 1) { lastFoundIndex = Array.FindIndex(orderedWellOperations, lastFoundIndex, o => o.DateStart > data.DateStart) - 1; lastFoundIndex = lastFoundIndex < 0 ? orderedWellOperations.Length - 1 : lastFoundIndex; } var operation = orderedWellOperations[lastFoundIndex]; return operation.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 wellSectionType = wellSectionTypes .Where(c => c.Id == idWellSectionType) .First(); var elem = CalcStat(processMapPlan, span, wellOperationCategoryName, wellSectionType); if (elem is not null) list.Add(elem); } } return list; } private static bool IsModeMatchOperationCategory(int idMode, int idCategory) { return (idMode == 1 && idCategory == 5003) || (idMode == 2 && idCategory == 5002); } private static ProcessMapReportDataSaubStatDto? CalcStat( ProcessMapPlanDrillingDto? processMapPlanFilteredByDepth, Span span, string wellOperationCategoryName, WellSectionTypeDto wellSectionType ) { var firstElemInInterval = span[0]; var lastElemInInterval = span[^1]; var deltaDepth = lastElemInInterval.DepthEnd - firstElemInInterval.DepthStart; var aggregatedValues = CalcAggregate(span); var result = new ProcessMapReportDataSaubStatDto() { IdWellSectionType = wellSectionType.Id, DateStart = firstElemInInterval.DateStart, WellSectionTypeName = wellSectionType.Caption, 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 }, TopDriveSpeed = 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 }, }; return result; } private static ( 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 * 100 / diffDepthTotal, SetpointUsageAxialLoad: sumDiffDepthByAxialLoad * 100 / diffDepthTotal, SetpointUsageRotorTorque: sumDiffDepthByRotorTorque * 100 / diffDepthTotal, SetpointUsageRopPlan: sumDiffDepthByRopPlan * 100/ diffDepthTotal, DrilledTime: drilledTime ); } private static bool IsNewInterval(DataSaubStatDto currentElem, DataSaubStatDto firstElem, DataSaubStatRequest request) { static 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; } }