using AsbCloudApp.Data; using AsbCloudApp.Data.ProcessMap; using AsbCloudApp.Data.SAUB; using AsbCloudApp.Repositories; using AsbCloudApp.Requests; using AsbCloudApp.Services; using AsbCloudDb.Model; using Microsoft.EntityFrameworkCore; 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; public ProcessMapService( IAsbCloudDbContext db, IWellOperationRepository wellOperationService, IProcessMapRepository processMapRepository, ITelemetryService telemetryService) { this.db = db; this.wellOperationRepository = wellOperationService; this.processMapRepository = processMapRepository; this.telemetryService = telemetryService; } 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 GetTelemetryDataAsync(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 async Task> GetTelemetryDataAsync(int idTelemetry, CancellationToken token) { var timezone = telemetryService.GetTimezone(idTelemetry); var timezoneOffset = TimeSpan.FromHours(timezone.Hours); var query = db.Set() .Where(t => t.IdTelemetry == idTelemetry) .Where(t => t.BlockPosition > 0.0001) .Where(t => t.WellDepth > 0.0001) .Where(t => t.WellDepth - t.BitDepth < 0.01) .GroupBy(t => new { H = t.DateTime.Hour, W = Math.Truncate(t.WellDepth!.Value) }) .Select(g => new ProcessTelemetrySaubStat { Count = g.Count(), DateMin = g.Min(t => t.DateTime.UtcDateTime), DateMax = g.Max(t => t.DateTime.UtcDateTime), WellDepthMin = g.Min(t => t.WellDepth!.Value), WellDepthMax = g.Max(t => t.WellDepth!.Value), Pressure = g.Average(t => t.Pressure!.Value), PressureSp = g.Average(t => t.PressureSp!.Value), PressureSpRotor = g.Average(t => t.PressureSpRotor!.Value), PressureSpSlide = g.Average(t => t.PressureSpSlide!.Value), PressureIdle = g.Average(t => t.PressureIdle!.Value), PressureDeltaLimitMax = g.Average(t => t.PressureDeltaLimitMax!.Value), AxialLoad = g.Average(t => t.AxialLoad!.Value), AxialLoadSp = g.Average(t => t.AxialLoadSp!.Value), AxialLoadLimitMax = g.Average(t => t.AxialLoadLimitMax!.Value), RotorTorque = g.Average(t => t.RotorTorque!.Value), RotorTorqueSp = g.Average(t => t.RotorTorqueSp!.Value), RotorTorqueLimitMax = g.Average(t => t.RotorTorqueLimitMax!.Value), BlockSpeed = g.Average(t => t.BlockSpeed!.Value), BlockSpeedSp = g.Average(t => t.BlockSpeedSp!.Value), BlockSpeedSpRotor = g.Average(t => t.BlockSpeedSpRotor!.Value), BlockSpeedSpSlide = g.Average(t => t.BlockSpeedSpSlide!.Value), }) .Where(s => s.WellDepthMin != s.WellDepthMax) .Where(s => s.Count > 3) .OrderBy(t => t.DateMin); var data = await query.ToArrayAsync(token); return data; } 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 ProcessTelemetrySaubStat? CalcIntervalTelemetryDataStat((double min, double max) depthInterval, IEnumerable telemetryDataStat) { ProcessTelemetrySaubStat[] 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 ProcessTelemetrySaubStat { 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, ProcessTelemetrySaubStat? 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 }