using AsbCloudApp.Data; 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; using System.Threading; using System.Threading.Tasks; using AsbCloudInfrastructure.Services.Subsystems; namespace AsbCloudInfrastructure.Services.ProcessMap { #nullable enable public partial class ProcessMapService : IProcessMapService { private readonly IWellService wellService; private readonly IWellOperationRepository wellOperationRepository; private readonly IProcessMapRepository processMapRepository; private readonly ITelemetryDataSaubService telemetryDataSaubService; private readonly ILimitingParameterRepository limitingParameterRepository; private readonly ISubsystemOperationTimeService subsystemOperationTimeService; public ProcessMapService( IWellService wellService, IWellOperationRepository wellOperationService, IProcessMapRepository processMapRepository, ITelemetryDataSaubService telemetryDataSaubService, ILimitingParameterRepository limitingParameterRepository, ISubsystemOperationTimeService subsystemOperationTimeService) { this.wellService = wellService; this.wellOperationRepository = wellOperationService; this.processMapRepository = processMapRepository; this.telemetryDataSaubService = telemetryDataSaubService; this.limitingParameterRepository = limitingParameterRepository; this.subsystemOperationTimeService = subsystemOperationTimeService; } /// public async Task> GetProcessMapAsync(int idWell, CancellationToken token) { var well = wellService.GetOrDefault(idWell) ?? throw new ArgumentInvalidException("idWell not found", nameof(idWell)); var idTelemetry = well.IdTelemetry ?? throw new ArgumentInvalidException("telemetry by well not found", nameof(idWell)); var processMap = (await processMapRepository.GetByIdWellAsync(idWell, token))!; var factDrillingOperations = await GetFactDrillingOperationsAsync(idWell, token); var telemetryDataStat = await telemetryDataSaubService.GetTelemetryDataStatAsync(idTelemetry, token); var limitingParameters = await limitingParameterRepository.GetLimitingParametersAsync(new(), well, token); var subsystemsOperationTime = await GetOperationTimeAsync(idWell, token); var result = factDrillingOperations .GroupBy(o => o.IdWellSectionType) .SelectMany(sectionOperations => { var sectionProcessMap = processMap.Where(p => p.IdWellSectionType == sectionOperations.Key); return HandleSection(sectionOperations, sectionProcessMap, telemetryDataStat, limitingParameters, subsystemsOperationTime!); }) .ToList(); return result; } private Task?> GetOperationTimeAsync(int idWell, CancellationToken token) { var request = new SubsystemOperationTimeRequest { IdWell = idWell, IdsSubsystems = new int[] { SubsystemOperationTimeService.IdSubsystemAKB, SubsystemOperationTimeService.IdSubsystemSpin }, }; return subsystemOperationTimeService.GetOperationTimeAsync(request, token); } private async Task> GetFactDrillingOperationsAsync(int idWell, CancellationToken token) { var operationsRequest = new WellOperationRequest { IdWell = idWell, OperationCategoryIds = WellOperationCategory.MechanicalDrillingSubIds, OperationType = WellOperation.IdOperationTypeFact, SortFields = new[] { nameof(WellOperation.DateStart) } }; var allFactDrillingOperations = await wellOperationRepository.GetAsync(operationsRequest, token); var factDrillingOperations = allFactDrillingOperations.Where(o => o.DepthEnd > o.DepthStart); return factDrillingOperations; } private static IEnumerable HandleSection( IEnumerable sectionOperations, IEnumerable sectionProcessMap, IEnumerable telemetryDataStat, IEnumerable limitingParameters, IEnumerable subsystemsOperationTime) { var minDepth = sectionOperations.Min(o => o.DepthStart); var maxDepth = sectionOperations.Max(o => o.DepthEnd); var depthIntervals = SplitByIntervals(minDepth, maxDepth).ToArray(); var result = new ProcessMapReportDto[depthIntervals.Length]; for (var i = 0; i < depthIntervals.Length; i++ ) result[i] = MakeProcessMapReportDto(depthIntervals[i], sectionOperations, sectionProcessMap, telemetryDataStat, limitingParameters, subsystemsOperationTime); return result; } private static ProcessMapReportDto MakeProcessMapReportDto( (double min, double max) depthInterval, IEnumerable sectionOperations, IEnumerable sectionProcessMap, IEnumerable telemetryDataStat, IEnumerable limitingParameters, IEnumerable subsystemsOperationTime) { var dto = new ProcessMapReportDto{ DepthStart = depthInterval.min }; // TODO: trim items by detpth intervals. Use linear interpolation. var intervalOperations = sectionOperations.Where(o => o.DepthEnd >= depthInterval.min && o.DepthStart <= depthInterval.max); var intervalProcessMap = sectionProcessMap.Where(map => map.DepthEnd >= depthInterval.min && map.DepthStart <= depthInterval.max); var intervalTelemetryDataStat = CalcIntervalTelemetryDataStat(depthInterval, telemetryDataStat); var intervalLimitingParametrs = limitingParameters.Where(l => l.DepthEnd >= depthInterval.min && l.DepthStart <= depthInterval.max); var intervalSubsystemsOperationTime = subsystemsOperationTime.Where(o => o.DepthEnd >= depthInterval.min && o.DepthStart <= depthInterval.max); var firstIntervalOperation = intervalOperations.FirstOrDefault(); if (firstIntervalOperation is not null) { dto.DateStart = GetInterpolatedDate(firstIntervalOperation, depthInterval.min); dto.IdWell = firstIntervalOperation.IdWell; dto.IdWellSectionType = firstIntervalOperation.IdWellSectionType; dto.WellSectionTypeName = firstIntervalOperation.WellSectionTypeName; dto.MechDrillingHours = CalcHours(depthInterval, intervalOperations); } // TODO: Разделить интервальные коллекции на ротор и слайд. Пока нет готовой методики. var slideOperations = intervalOperations.Where(o => o.IdCategory == WellOperationCategory.IdSlide); var rotorOperations = intervalOperations.Where(o => o.IdCategory == WellOperationCategory.IdRotor); dto.Slide = CalcDrillModeStat(depthInterval, slideOperations, intervalProcessMap, intervalTelemetryDataStat, intervalLimitingParametrs, intervalSubsystemsOperationTime); dto.Rotor = CalcDrillModeStat(depthInterval, rotorOperations, intervalProcessMap, intervalTelemetryDataStat, intervalLimitingParametrs, intervalSubsystemsOperationTime); return dto; } private static TelemetryDataSaubStatDto? CalcIntervalTelemetryDataStat((double min, double max) depthInterval, IEnumerable telemetryDataStat) { TelemetryDataSaubStatDto[] data = telemetryDataStat .Where(d => d.WellDepthMin <= depthInterval.max && d.WellDepthMax >= depthInterval.min) .ToArray(); if (!data.Any()) return null; if (data.Length == 1) return data.First(); var result = new TelemetryDataSaubStatDto { WellDepthMin = data.Min(d => d.WellDepthMin), WellDepthMax = data.Max(d => d.WellDepthMax), DateMin = data.Min(d => d.DateMin), DateMax = data.Max(d => d.DateMax), }; var intervalDeltaDepth = result.WellDepthMax - result.WellDepthMin; foreach (var item in data) { var itemWeight = (item.WellDepthMax - item.WellDepthMin) / intervalDeltaDepth; result.Pressure += item.Pressure * itemWeight; result.PressureSp += item.PressureSp * itemWeight; result.PressureSpRotor += item.PressureSpSlide * itemWeight; result.PressureIdle += item.PressureIdle * itemWeight; result.PressureDelta += item.PressureDelta * itemWeight; result.AxialLoad += item.AxialLoad * itemWeight; result.AxialLoadSp += item.AxialLoadSp * itemWeight; result.AxialLoadLimitMax += item.AxialLoadLimitMax * itemWeight; result.RotorTorque += item.RotorTorque * itemWeight; result.RotorTorqueSp += item.RotorTorqueSp * itemWeight; result.RotorTorqueLimitMax += item.RotorTorqueLimitMax * itemWeight; result.BlockSpeed += item.BlockSpeed * itemWeight; result.BlockSpeedSp += item.BlockSpeedSp * itemWeight; result.BlockSpeedSpRotor += item.BlockSpeedSpRotor * itemWeight; result.BlockSpeedSpSlide += item.BlockSpeedSpSlide * itemWeight; } return result; } private static ProcessMapReportRowDto CalcDrillModeStat( (double min, double max) depthInterval, IEnumerable intervalModeOperations, IEnumerable intervalProcessMap, TelemetryDataSaubStatDto? telemetryDataStat, IEnumerable intervalLimitingParametrs, IEnumerable intervalSubsystemsOperationTime) { var dto = new ProcessMapReportRowDto(); if (intervalModeOperations.Any()) { var deltaDepth = CalcDeltaDepth(depthInterval, intervalModeOperations); dto.DeltaDepth = deltaDepth; dto.Rop = deltaDepth / CalcHours(depthInterval, intervalModeOperations); }; if (intervalProcessMap.Any()) { var processMapFirst = intervalProcessMap.First(); dto.PressureDiff.SetpointPlan = processMapFirst.Pressure.Plan; dto.AxialLoad.SetpointPlan = processMapFirst.AxialLoad.Plan; dto.TopDriveTorque.SetpointPlan = processMapFirst.TopDriveTorque.Plan; //dto.SpeedLimit.SetpointPlan = null; } if (telemetryDataStat is not null) { dto.PressureDiff.SetpointFact = telemetryDataStat.PressureSp; dto.PressureDiff.Fact = telemetryDataStat.PressureDelta; dto.PressureDiff.Limit = telemetryDataStat.PressureDeltaLimitMax; dto.AxialLoad.SetpointFact = telemetryDataStat.AxialLoadSp; dto.AxialLoad.Fact = telemetryDataStat.AxialLoad; dto.AxialLoad.Limit = telemetryDataStat.AxialLoadLimitMax; dto.TopDriveTorque.SetpointFact = telemetryDataStat.RotorTorqueSp; dto.TopDriveTorque.Fact = telemetryDataStat.RotorTorque; dto.TopDriveTorque.Limit = telemetryDataStat.RotorTorqueLimitMax; dto.SpeedLimit.SetpointFact = telemetryDataStat.BlockSpeedSp; dto.SpeedLimit.Fact = telemetryDataStat.BlockSpeed; //dto.SpeedLimit.Limit = mull; } if(intervalLimitingParametrs.Any()) { const int idLimParamRop = 1; const int idLimParamPressure = 2; const int idLimParamAxialLoad = 3; const int idLimParamTorque = 4; var intervalLimitingParametrsStat = intervalLimitingParametrs .GroupBy(p => p.IdFeedRegulator) .Select(g => new { IdLimParam = g.Key, SumDepth = g.Sum(p => p.DepthEnd - p.DepthStart), }); var totalDepth = intervalLimitingParametrsStat .Sum(s => s.SumDepth); if (totalDepth > 0) { dto.AxialLoad.PercDrillingSetpoint = intervalLimitingParametrsStat .FirstOrDefault(s => s.IdLimParam == idLimParamAxialLoad)?.SumDepth / totalDepth; dto.PressureDiff.PercDrillingSetpoint = intervalLimitingParametrsStat .FirstOrDefault(s => s.IdLimParam == idLimParamPressure)?.SumDepth / totalDepth; dto.TopDriveTorque.PercDrillingSetpoint = intervalLimitingParametrsStat .FirstOrDefault(s => s.IdLimParam == idLimParamTorque)?.SumDepth / totalDepth; dto.SpeedLimit.PercDrillingSetpoint = intervalLimitingParametrsStat .FirstOrDefault(s => s.IdLimParam == idLimParamRop)?.SumDepth / totalDepth; } } if (intervalSubsystemsOperationTime.Any() && dto.DeltaDepth > 0) { dto.Usage = intervalSubsystemsOperationTime.Sum(t => t.DepthEnd - t.DepthStart) / dto.DeltaDepth.Value; } return dto; } private static double CalcDeltaDepth((double min, double max) depthInterval, IEnumerable intervalOperations) { var ddepth = 0d; foreach (var operation in intervalOperations) { var depthStart = operation.DepthStart > depthInterval.min ? operation.DepthStart : depthInterval.min; var depthEnd = operation.DepthEnd < depthInterval.max ? operation.DepthEnd : depthInterval.max; ddepth += (depthEnd - depthEnd); } return ddepth; } private static double CalcHours((double min, double max) depthInterval, IEnumerable intervalOperations) { var hours = 0d; foreach (var operation in intervalOperations) { var dateStart = operation.DepthStart > depthInterval.min ? operation.DateStart : GetInterpolatedDate(operation, depthInterval.min); var dateEnd = operation.DepthEnd < depthInterval.max ? operation.DateStart + TimeSpan.FromHours(operation.DurationHours) : GetInterpolatedDate(operation, depthInterval.max); hours += (dateEnd - dateStart).TotalHours; } return hours; } 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; } 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) { yield return (iMin, iMax); iMin = iMax; } yield return (iMin, max); } } #nullable disable }