diff --git a/AsbCloudApp/Data/ProcessMap/ProcessMapReportDto.cs b/AsbCloudApp/Data/ProcessMap/ProcessMapReportDto.cs index 51e77911..6bc8ace1 100644 --- a/AsbCloudApp/Data/ProcessMap/ProcessMapReportDto.cs +++ b/AsbCloudApp/Data/ProcessMap/ProcessMapReportDto.cs @@ -15,18 +15,24 @@ namespace AsbCloudApp.Data.ProcessMap /// /// Глубина по стволу, м + /// + /// на начало интервала + /// /// - public double Depth { get; set; } + public double DepthStart { get; set; } /// /// Дата/ время + /// + /// на начало интервала + /// /// - public DateTimeOffset Date { get; set; } + public DateTimeOffset DateStart { get; set; } /// - /// Т мех бурения, ч + /// Время мех бурения, ч /// - public double MechDrillingTime { get; set; } + public double MechDrillingHours { get; set; } /// /// Слайд @@ -41,22 +47,12 @@ namespace AsbCloudApp.Data.ProcessMap /// /// название секции скважины /// - public string? WellSectionTypeName { get; set; } + public int IdWellSectionType { get; set; } /// /// название секции скважины /// - public int? IdWellSectionType { get; set; } - - /// - /// Категория - /// - public string? Category { get; set; } - - /// - /// Идентификатор категории - /// - public int? IdCategory { get; set; } + public string WellSectionTypeName { get; set; } = null!; } #nullable disable } diff --git a/AsbCloudApp/Data/ProcessMap/ProcessMapReportRowDto.cs b/AsbCloudApp/Data/ProcessMap/ProcessMapReportRowDto.cs index 6b4e942d..0c1ae626 100644 --- a/AsbCloudApp/Data/ProcessMap/ProcessMapReportRowDto.cs +++ b/AsbCloudApp/Data/ProcessMap/ProcessMapReportRowDto.cs @@ -12,15 +12,20 @@ namespace AsbCloudApp.Data.ProcessMap /// public class ProcessMapReportRowDto { + /// + /// Проходка, м + /// + public double? DeltaDepth { get; set; } + /// /// Перепад давления, атм /// - public ProcessMapReportParamsDto PressureDrop { get; set; } = null!; + public ProcessMapReportParamsDto PressureDiff { get; set; } = null!; /// /// Нагрузка, т /// - public ProcessMapReportParamsDto Load { get; set; } = null!; + public ProcessMapReportParamsDto AxialLoad { get; set; } = null!; /// /// Момент на ВСП, кНхМ @@ -35,17 +40,12 @@ namespace AsbCloudApp.Data.ProcessMap /// /// Процент использования системы АПД, % /// - public double PercentageADFSystemUsage { get; set; } + public double Usage { get; set; } /// /// Фактическая механическая скорость, м/ч /// - public double ActualMechanicalSpeed { get; set; } - - /// - /// Проходка, м - /// - public double? SectionPenetration { get; set; } + public double Rop { get; set; } } #nullable disable } diff --git a/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapService.cs b/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapService.cs index 0dfab7df..8f81b317 100644 --- a/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapService.cs +++ b/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapService.cs @@ -1,9 +1,11 @@ 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; @@ -13,151 +15,236 @@ using System.Threading.Tasks; namespace AsbCloudInfrastructure.Services.ProcessMap { #nullable enable - public class ProcessMapService : IProcessMapService + public partial class ProcessMapService : IProcessMapService { + private readonly IAsbCloudDbContext db; private readonly IWellOperationRepository wellOperationRepository; private readonly IProcessMapRepository processMapRepository; + private readonly ITelemetryService telemetryService; - public ProcessMapService(IWellOperationRepository wellOperationService, - IProcessMapRepository processMapRepository) - { + 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 categoryIds = new int[] { WellOperationCategory.IdSlide, WellOperationCategory.IdRotor }; - var operationsRequest = new WellOperationRequest { IdWell = idWell, - OperationCategoryIds = categoryIds, - OperationType = 1, + OperationCategoryIds = WellOperationCategory.MechanicalDrillingSubIds, + OperationType = WellOperation.IdOperationTypeFact, SortFields = new[]{ nameof(WellOperation.DateStart) } }; - var allOperations = await wellOperationRepository.GetPageAsync(operationsRequest, token); - throw new NotImplementedException(); - //var processMapDtos = await processMapRepository.GetByIdWellAsync(idWell, token).ConfigureAwait(false); + var allFactDrillingOperations = (await wellOperationRepository.GetAsync(operationsRequest, token)) + .Where(o => o.DepthEnd > o.DepthStart); + + var processMapDtos = (await processMapRepository.GetByIdWellAsync(idWell, token))!; - //var result = allOperations - // .GroupBy(x => x.IdWellSectionType) - // .SelectMany(x => handleSections(allOperations, processMapDtos!, x)) - // .ToList(); - - //return result; - } - - private static List handleSections(IEnumerable allWellOperation, IEnumerable processMap, IEnumerable wellOperation) - { - var result = new List(); - var minDepth = wellOperation.Min(o => o.DepthStart); - var maxDepth = wellOperation.Max(o => o.DepthEnd); - - for (var depth = minDepth; depth <= maxDepth; depth += 100) - { - var currentDepth = result.Count() > 0 ? depth + (100 - depth % 100) : depth; - var operation = allWellOperation.First(x => x.DepthEnd >= currentDepth); - var minDate = operation.DateStart; - var maxDate = operation.DateStart.AddHours(operation.DurationHours); - var currentDate = GetDate(currentDepth, minDepth, maxDepth, minDate, maxDate); - var currentHours = GetHourse(currentDepth, minDepth, maxDepth, operation.DurationHours); - var processMapData = processMap.FirstOrDefault(x => x.IdWellSectionType == operation.IdWellSectionType); - - var dto = GetRecord(currentDepth, currentDate, currentHours, operation, processMap); - - if (operation.DepthStart < currentDepth) + var idTelemetry = telemetryService.GetOrDefaultIdTelemetryByIdWell(idWell)!.Value; + IEnumerable telemetryStat = await GetTelemetryDataAsync(idTelemetry, token); + + var result = allFactDrillingOperations + .GroupBy(o => o.IdWellSectionType) + .SelectMany(sectionOperations => { - var prevOperation = allWellOperation.FirstOrDefault(x => x.DepthEnd == operation.DepthStart); - if (prevOperation is not null) - { - var prevDepth = currentDepth - prevOperation.DepthEnd; - var prevMinDate = prevOperation.DateStart; - var prevMaxDate = operation.DateStart.AddHours(operation.DurationHours); - var prevDate = GetDate(prevDepth, prevOperation.DepthStart, prevOperation.DepthEnd, minDate, maxDate); - var prevHours = GetHourse(currentDepth, prevOperation.DepthStart, prevOperation.DepthEnd, prevOperation.DurationHours); - - if (dto.IdCategory != prevOperation.IdCategory) - FillRecordRow(prevDepth, prevHours, dto, prevOperation.IdCategory, processMap); - - dto.MechDrillingTime = currentHours + prevHours; - } - } - - result.Add(dto); - } + var sectionProcessMap = processMapDtos.Where(p => p.IdWellSectionType == sectionOperations.Key); + return HandleSections(sectionOperations, sectionProcessMap); + }) + .ToList(); return result; } - private static ProcessMapReportDto GetRecord(double depth, DateTimeOffset sate, double hours, WellOperationDto operation, IEnumerable processMap) - { - var dto = new ProcessMapReportDto - { - IdWell = operation.IdWell, - Depth = depth, - Date = sate, - MechDrillingTime = hours, - Category = operation.IdCategory == WellOperationCategory.IdSlide ? "Слайд" : "Ротор", - IdCategory = operation.IdCategory, - IdWellSectionType = operation.IdWellSectionType, - WellSectionTypeName = operation.WellSectionTypeName + 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); }; - FillRecordRow(depth, hours, dto, operation.IdCategory, processMap); return dto; } - private static void FillRecordRow(double depth, double hours, ProcessMapReportDto dto, int idCategory, IEnumerable processMap) + private static ProcessMapReportParamsDto CalcPressureDiff( + (double min, double max) depthInterval, + IEnumerable intervalProcessMap) { - var row = new ProcessMapReportRowDto { - SectionPenetration = depth, - ActualMechanicalSpeed = depth / hours, - PercentageADFSystemUsage = dto.Depth / depth * 100 - }; - - var processMapData = processMap.FirstOrDefault(x => x.IdWell == dto.IdWell && x.IdWellSectionType == dto.IdWellSectionType); - if (processMapData is not null) + var dto = new ProcessMapReportParamsDto(); + if (intervalProcessMap.Any()) { - row.PressureDrop = new ProcessMapReportParamsDto - { - SetPointPlan = processMapData.Pressure.Plan - }; - row.Load = new ProcessMapReportParamsDto - { - SetPointPlan = processMapData.AxialLoad.Plan - }; - row.TopDriveTorque = new ProcessMapReportParamsDto - { - SetPointPlan = processMapData.TopDriveTorque.Plan - }; - row.SpeedL​imit = new ProcessMapReportParamsDto - { - - }; + dto.SetPointPlan = intervalProcessMap.First().Pressure.Plan; } - - if (idCategory == WellOperationCategory.IdSlide) - dto.Slide = row; - else - dto.Rotor = row; + throw new NotImplementedException(); } - private static DateTimeOffset GetDate(double depth, double deptStart, double deptEnd, DateTimeOffset dateStart, DateTimeOffset dateEnd) + private static double CalcDeltaDepth((double min, double max) depthInterval, IEnumerable intervalOperations) { - var a = depth - deptStart; - var b = deptEnd - deptStart; - var c = dateEnd - dateStart; - var result = dateStart + (a / b) * c; - return result; + 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 GetHourse(double depth, double deptStart, double deptEnd, double hourse) + private static double CalcHours((double min, double max) depthInterval, IEnumerable intervalOperations) { - var a = depth - deptStart; - var b = deptEnd - deptStart; - var result = 0 + (a / b) * hourse; - return result; + 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 diff --git a/AsbCloudInfrastructure/Services/ProcessMap/ProcessTelemetrySaubStat.cs b/AsbCloudInfrastructure/Services/ProcessMap/ProcessTelemetrySaubStat.cs new file mode 100644 index 00000000..9c09d2ea --- /dev/null +++ b/AsbCloudInfrastructure/Services/ProcessMap/ProcessTelemetrySaubStat.cs @@ -0,0 +1,35 @@ +using System; + +namespace AsbCloudInfrastructure.Services.ProcessMap +{ +#nullable enable + class ProcessTelemetrySaubStat { + public int Count { get; set; } + public DateTime DateMin { get; set; } + public DateTime DateMax { get; set; } + + public float WellDepthMin { get; set; } + public float WellDepthMax { get; set; } + + public float Pressure { get; set; } + public float PressureSp { get; set; } + public float PressureIdle { get; set; } + public float PressureSpRotor { get; set; } + public float PressureSpSlide { get; set; } + public float PressureDeltaLimitMax { get; set; } + + public float AxialLoad { get; set; } + public float AxialLoadSp { get; set; } + public float AxialLoadLimitMax { get; set; } + + public float RotorTorque { get; set; } + public float RotorTorqueSp { get; set; } + public float RotorTorqueLimitMax { get; set; } + + public float BlockSpeed { get; set; } + public float BlockSpeedSp { get; set; } + public float BlockSpeedSpRotor { get; set; } + public float BlockSpeedSpSlide { get; set; } + } +#nullable disable +} diff --git a/AsbCloudInfrastructure/Services/WellOperationService/WellOperationImportService.cs b/AsbCloudInfrastructure/Services/WellOperationService/WellOperationImportService.cs index 81771165..e9ed3d11 100644 --- a/AsbCloudInfrastructure/Services/WellOperationService/WellOperationImportService.cs +++ b/AsbCloudInfrastructure/Services/WellOperationService/WellOperationImportService.cs @@ -65,6 +65,7 @@ namespace AsbCloudInfrastructure.Services.WellOperationService } } + // TODO: use WellOperationRepository instead of DB public WellOperationImportService(IAsbCloudDbContext db, IWellService wellService) { this.db = db; diff --git a/ConsoleApp1/Program.cs b/ConsoleApp1/Program.cs index 19720e16..af1ac385 100644 --- a/ConsoleApp1/Program.cs +++ b/ConsoleApp1/Program.cs @@ -4,6 +4,7 @@ using AsbCloudInfrastructure; using AsbCloudInfrastructure.Repository; using Microsoft.Extensions.Caching.Memory; using System; +using System.Linq; using System.Threading; namespace ConsoleApp1 @@ -18,12 +19,51 @@ namespace ConsoleApp1 DependencyInjection.MapsterSetup(); var sw = System.Diagnostics.Stopwatch.StartNew(); - var repo = new WellOperationRepository(db, new MemoryCache(new MemoryCacheOptions()), ServiceFactory.MakeWellService()); - var req = new WellOperationRequest { IdWell = 88, OperationType = 1, Skip = 70}; - var res = repo.GetPageAsync(req, CancellationToken.None).Result; + var idTelemetry = 5; + 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 + { + Count = g.Count(), + + DateMin = g.Min(t => t.DateTime), + DateMax = g.Max(t => t.DateTime), + + WellDepthMin = g.Min(t => t.WellDepth), + WellDepthMax = g.Max(t => t.WellDepth), + + Pressure = g.Average(t => t.Pressure), + PressureSp = g.Average(t => t.PressureSp), + PressureSpRotor = g.Average(t => t.PressureSpRotor), + PressureSpSlide = g.Average(t => t.PressureSpSlide), + PressureIdle = g.Average(t => t.PressureIdle), + PressureDeltaLimitMax = g.Average(t => t.PressureDeltaLimitMax), + + AxialLoad = g.Average(t => t.AxialLoad), + AxialLoadSp = g.Average(t => t.AxialLoadSp), + AxialLoadLimitMax = g.Average(t => t.AxialLoadLimitMax), + + RotorTorque = g.Average(t => t.RotorTorque), + RotorTorqueSp = g.Average(t => t.RotorTorqueSp), + RotorTorqueIdle = g.Average(t => t.RotorTorqueIdle), + + BlockSpeed = g.Average(t => t.BlockSpeed), + BlockSpeedSp = g.Average(t => t.BlockSpeedSp), + BlockSpeedSpRotor = g.Average(t => t.BlockSpeedSpRotor), + BlockSpeedSpSlide = g.Average(t => t.BlockSpeedSpSlide), + }) + .Where(s => s.WellDepthMin != s.WellDepthMax) + .Where(s => s.Count > 3) + .OrderBy(t => t.DateMin); + var data = query.ToArray(); sw.Stop(); Console.WriteLine($"total time: {sw.ElapsedMilliseconds} ms"); + var count = data.Length; Console.ReadLine(); } }