using AsbCloudApp.Data; using AsbCloudApp.Data.ProcessMap; using AsbCloudApp.Data.SAUB; using AsbCloudApp.Repositories; using AsbCloudApp.Requests; using AsbCloudApp.Services; using AsbCloudDb.Model; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace AsbCloudInfrastructure.Services.ProcessMap { #nullable enable public partial class ProcessMapService : IProcessMapService { private readonly IAsbCloudDbContext db; private readonly IWellOperationRepository wellOperationRepository; private readonly IProcessMapRepository processMapRepository; private readonly ITelemetryService telemetryService; private readonly ITelemetryDataSaubService telemetryDataSaubService; private readonly ILimitingParameterRepository limitingParameterRepository; public ProcessMapService( IAsbCloudDbContext db, IWellOperationRepository wellOperationService, IProcessMapRepository processMapRepository, ITelemetryService telemetryService, ITelemetryDataSaubService telemetryDataSaubService, ILimitingParameterRepository limitingParameterRepository) { this.db = db; this.wellOperationRepository = wellOperationService; this.processMapRepository = processMapRepository; this.telemetryService = telemetryService; this.telemetryDataSaubService = telemetryDataSaubService; this.limitingParameterRepository = limitingParameterRepository; } public async Task> GetProcessMapAsync(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)) .Where(o => o.DepthEnd > o.DepthStart); var processMapDtos = (await processMapRepository.GetByIdWellAsync(idWell, token))!; var idTelemetry = telemetryService.GetOrDefaultIdTelemetryByIdWell(idWell)!.Value; IEnumerable telemetryDataStat = await telemetryDataSaubService.GetTelemetryDataStatAsync(idTelemetry, token); var result = allFactDrillingOperations .GroupBy(o => o.IdWellSectionType) .SelectMany(sectionOperations => { var sectionProcessMap = processMapDtos.Where(p => p.IdWellSectionType == sectionOperations.Key); return HandleSections(sectionOperations, sectionProcessMap, telemetryDataStat); }) .ToList(); return result; } private static IEnumerable HandleSections( IEnumerable sectionOperations, IEnumerable sectionProcessMap, IEnumerable telemetryDataStat) { 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); return result; } private static ProcessMapReportDto MakeProcessMapReportDto( (double min, double max) depthInterval, IEnumerable sectionOperations, IEnumerable sectionProcessMap, IEnumerable telemetryDataStat) { var dto = new ProcessMapReportDto{ DepthStart = depthInterval.min }; 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); if (intervalOperations.Any()) { var firstIntervalOperation = intervalOperations.First(); var slideOperations = intervalOperations.Where(o => o.IdCategory == WellOperationCategory.IdSlide); var rotorOperations = intervalOperations.Where(o => o.IdCategory == WellOperationCategory.IdRotor); dto.DepthStart = depthInterval.min; dto.DateStart = GetInterpolatedDate(firstIntervalOperation, depthInterval.min); dto.IdWell = firstIntervalOperation.IdWell; dto.IdWellSectionType = firstIntervalOperation.IdWellSectionType; dto.WellSectionTypeName = firstIntervalOperation.WellSectionTypeName; dto.MechDrillingHours = CalcHours(depthInterval, sectionOperations); dto.Slide = CalcDrillModeStat(depthInterval, slideOperations, intervalProcessMap, intervalTelemetryDataStat); dto.Rotor = CalcDrillModeStat(depthInterval, rotorOperations, intervalProcessMap, intervalTelemetryDataStat); } 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.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) { var dto = new ProcessMapReportRowDto(); if (intervalModeOperations.Any()) { var deltaDepth = CalcDeltaDepth(depthInterval, intervalModeOperations); dto.DeltaDepth = deltaDepth; dto.Rop = deltaDepth / CalcHours(depthInterval, intervalModeOperations); 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 = double.NaN; }; if (telemetryDataStat is not null) { dto.PressureDiff.SetpointFact = telemetryDataStat.PressureSp; dto.PressureDiff.Fact = telemetryDataStat.Pressure; 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; } 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) { if (operation.DepthStart > depth) throw new ArgumentOutOfRangeException(nameof(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 }