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 telemetryStat = 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); }) .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 ) { 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); return result; } private static ProcessMapReportDto MakeProcessMapReportDto( (double min, double max) depthInterval, IEnumerable sectionOperations, IEnumerable sectionProcessMap) { 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); 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 = CalcDrillStat(depthInterval, slideOperations, intervalProcessMap); dto.Rotor = CalcDrillStat(depthInterval, rotorOperations, intervalProcessMap); } return dto; } private static ProcessMapReportRowDto CalcDrillStat( (double min, double max) depthInterval, IEnumerable intervalModeOperations, IEnumerable intervalProcessMap) { var dto = new ProcessMapReportRowDto(); if(intervalModeOperations.Any()) { var deltaDepth = CalcDeltaDepth(depthInterval, intervalModeOperations); dto.DeltaDepth = deltaDepth; dto.Rop = deltaDepth / CalcHours(depthInterval, intervalModeOperations); dto.PressureDiff = CalcPressureDiff(depthInterval, intervalProcessMap); }; return dto; } private static ProcessMapReportParamsDto CalcPressureDiff( (double min, double max) depthInterval, IEnumerable intervalProcessMap) { var dto = new ProcessMapReportParamsDto(); if (intervalProcessMap.Any()) { dto.SetPointPlan = intervalProcessMap.First().Pressure.Plan; } throw new NotImplementedException(); } 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 }