From 948002955deefdbf7ed088f0b04402cf4eea9e51 Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Mon, 4 Dec 2023 17:36:00 +0500 Subject: [PATCH] DetectorDrilling. Extract RefineEdges method from GetIdOperation. Move detectedOperation helper data to ExtraData property --- AsbCloudDb/Model/DetectedOperation.cs | 5 +- .../DetectedOperationExportService.cs | 98 ++++++++++++------- .../Detectors/DetectorAbstract.cs | 75 +++++++++----- .../Detectors/DetectorDrilling.cs | 77 +++++++++------ .../Detectors/DetectorSlipsTime.cs | 10 +- 5 files changed, 171 insertions(+), 94 deletions(-) diff --git a/AsbCloudDb/Model/DetectedOperation.cs b/AsbCloudDb/Model/DetectedOperation.cs index 8d862de4..27f4f928 100644 --- a/AsbCloudDb/Model/DetectedOperation.cs +++ b/AsbCloudDb/Model/DetectedOperation.cs @@ -1,5 +1,6 @@ using Microsoft.EntityFrameworkCore; using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json.Serialization; @@ -40,8 +41,8 @@ namespace AsbCloudDb.Model [Column("value"), Comment("Ключевой показатель операции")] public double Value { get; set; } - [Column("id_reason_of_end"), Comment("Код признака окончания операции")] - public int IdReasonOfEnd { get; set; } + [Column("extra_data", TypeName = "jsonb"), Comment("доп. инфо по операции")] + public IDictionary ExtraData { get; set; } = null!; [JsonIgnore] [ForeignKey(nameof(IdTelemetry))] diff --git a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationExportService.cs b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationExportService.cs index dab037d5..0d882c58 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationExportService.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationExportService.cs @@ -10,6 +10,8 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using AsbCloudInfrastructure.Services.DetectOperations.Detectors; +using AsbCloudApp.Repositories; +using AsbCloudInfrastructure.Repository; namespace AsbCloudInfrastructure.Services.DetectOperations; @@ -46,11 +48,13 @@ public class DetectedOperationExportService private const int columnComment = 10; private readonly IAsbCloudDbContext dbContext; + private readonly IWellOperationRepository wellOperationRepository; - public DetectedOperationExportService(IAsbCloudDbContext dbContext) + public DetectedOperationExportService(IAsbCloudDbContext dbContext, IWellOperationRepository wellOperationRepository) { this.dbContext = dbContext; - } + this.wellOperationRepository = wellOperationRepository; + } public async Task ExportAsync(int idWell, int idDomain, CancellationToken cancellationToken) { @@ -112,31 +116,26 @@ public class DetectedOperationExportService sheet.Cell(cellWellName).Value = well.Caption; sheet.Cell(cellDeltaDate).Value = operationDetectorResults.Max(o => o.Operation.DateEnd) - operationDetectorResults.Min(o => o.Operation.DateStart); + var detectedOperations = operationDetectorResults.Select(o => o.Operation).ToArray(); + for (int i = 0; i < operationDetectorResults.Count; i++) { - var detectedOperations = operationDetectorResults.Select(o => o.Operation).ToArray(); - - var dateStart = detectedOperations[i].DateStart.ToRemoteDateTime(well.Timezone.Hours); - var dateEnd = detectedOperations[i].DateEnd.ToRemoteDateTime(well.Timezone.Hours); + var current = detectedOperations[i]; + var dateStart = current.DateStart.ToRemoteDateTime(well.Timezone.Hours); + var dateEnd = current.DateEnd.ToRemoteDateTime(well.Timezone.Hours); var row = sheet.Row(5 + i + headerRowsCount); - row.Cell(columnOperationName).Value = detectedOperations[i].IdCategory == 12000 - ? "Бурение в слайде с осцилляцией" - : wellOperationCategories.Single(o => o.Id == detectedOperations[i].IdCategory).Name; + row.Cell(columnOperationName).Value = GetCategoryName(wellOperationCategories, current); row.Cell(columnDateEnd).Value = dateEnd; row.Cell(columnDuration).Value = (dateEnd - dateStart).TotalMinutes; - row.Cell(columnDepthStart).Value = detectedOperations[i].DepthStart; - row.Cell(columnDepthEnd).Value = detectedOperations[i].DepthEnd; - row.Cell(columnDepth).Value = detectedOperations[i].DepthEnd - detectedOperations[i].DepthStart; - row.Cell(columnIdReasonOfEnd).Value = detectedOperations[i].IdReasonOfEnd switch - { - 0 => "Не определена", - 1 => "Не определено начало операции", - 101 => "Разница глубин забоя и положением долота", - 300 => "Низкое давление", - _ => detectedOperations[i].IdReasonOfEnd - }; + row.Cell(columnDepthStart).Value = current.DepthStart; + row.Cell(columnDepthEnd).Value = current.DepthEnd; + row.Cell(columnDepth).Value = current.DepthEnd - current.DepthStart; + + if (current.ExtraData.TryGetValue("IdReasonOfEnd", out object? idReasonOfEndObject) + && idReasonOfEndObject is int idReasonOfEnd) + row.Cell(columnIdReasonOfEnd).Value = GetIdReasonOfEnd(idReasonOfEnd); var link = $"{domains[idDomain]}/well/{well.Id}/telemetry/monitoring?end={Uri.EscapeDataString(dateStart.AddSeconds(1800 * 0.9).ToString("yyyy-MM-ddTHH:mm:ss.fff"))}&range=1800"; @@ -145,14 +144,40 @@ public class DetectedOperationExportService row.Cell(columnDateStart).SetHyperlink(new XLHyperlink(link)); row.Cell(columnDeltaDepth).Value = i > 0 && i + 1 < detectedOperations.Length - ? detectedOperations[i].DepthStart - detectedOperations[i - 1].DepthEnd + ? current.DepthStart - detectedOperations[i - 1].DepthEnd : 0; row.Cell(columnComment).Value = CreateComment(operationDetectorResults[i]); } } - - private async Task GetExcelTemplateStreamAsync(CancellationToken cancellationToken) + + private static string GetCategoryName(IEnumerable wellOperationCategories, DetectedOperation current) + { + var idCategory = current.IdCategory; + if (idCategory == WellOperationCategory.IdSlide + && current.ExtraData[DetectorDrilling.ExtraDataKeyHasOscillation] is bool hasOscillation + && hasOscillation) + return "Бурение в слайде с осцилляцией"; + + var category = wellOperationCategories.FirstOrDefault(o => o.Id == current.IdCategory); + + if(category is not null) + return category.Name; + + return $"Операция №{idCategory}"; + } + + private static string GetIdReasonOfEnd(int idReasonOfEnd) + => idReasonOfEnd switch { + 0 => "Не определена", + 1 => "Не определено начало операции", + 101 => "Разница глубин забоя и положением долота", + 300 => "Низкое давление", + _ => idReasonOfEnd.ToString($"Причина № {idReasonOfEnd}"), + + }; + + private async Task GetExcelTemplateStreamAsync(CancellationToken cancellationToken) { string resourceName = Assembly.GetExecutingAssembly() .GetManifestResourceNames() @@ -170,22 +195,23 @@ public class DetectedOperationExportService private string? CreateComment(OperationDetectorResult operationDetectorResult) { - switch (operationDetectorResult.Operation.IdCategory) + var operation = operationDetectorResult.Operation; + switch (operation.IdCategory) { - case 12000: - case 5002: - case 5003: + case WellOperationCategory.IdRotor: + case WellOperationCategory.IdSlide: { - var detectorDrilling = detectors.FirstOrDefault(d => d is DetectorDrilling) as DetectorDrilling; - var avgRotorSpeed = Math.Round(detectorDrilling.AvgRotorSpeedDict[(operationDetectorResult.TelemetryBegin, operationDetectorResult.TelemetryEnd)], 2); - var despersion = Math.Round(detectorDrilling.DespersionDict[(operationDetectorResult.TelemetryBegin, operationDetectorResult.TelemetryEnd)], 2); + var avgRotorSpeed = operation.ExtraData[DetectorDrilling.ExtraDataKeyAvgRotorSpeed]; + var dispersionOfNormalizedRotorSpeed = operation.ExtraData[DetectorDrilling.ExtraDataKeyDispersionOfNormalizedRotorSpeed]; + var isAfbEnabledObject = operation.ExtraData[DetectorDrilling.ExtraDataKeyIsAfbEnabled]; + var AfbState = ""; + if (isAfbEnabledObject is bool isAfbEnabled && isAfbEnabled) + AfbState = "АКБ - вкл"; - var stringBuilder = new StringBuilder(); - - stringBuilder.AppendLine($"Средняя скорость оборотов ротора: {avgRotorSpeed}"); - stringBuilder.AppendLine($"Нормированные обороты ротора: {despersion}"); - - return stringBuilder.ToString(); + var comment = $"Средняя скорость оборотов ротора: {avgRotorSpeed}\r\n" + + $"Дисперсия нормированных оборотов ротора: {dispersionOfNormalizedRotorSpeed} \r\n" + + $"{AfbState}"; + return comment; } default: return null; diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorAbstract.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorAbstract.cs index bacebad9..8a85b86f 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorAbstract.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorAbstract.cs @@ -1,5 +1,6 @@ using AsbCloudDb.Model; using System; +using System.Collections.Generic; using System.Linq; namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors @@ -55,16 +56,31 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors break; } - result = MakeOperation(idTelemetry, telemetry, begin, positionEnd, idReasonOfEnd); - return result is not null; + var (Begin, End) = RefineEdges(telemetry, begin, positionEnd); + + if (!IsValidTelemetryRange(telemetry, Begin, End)) + { + result = null; + return false; + } + + result = MakeOperationDetectorResult(idTelemetry, telemetry, Begin, End, idReasonOfEnd); + + return IsValidOperationDetectorResult(result); } result = null; return false; } - protected virtual bool IsValidOperationDetectorResult(OperationDetectorResult operationDetectorResult) => - operationDetectorResult.Operation.IdReasonOfEnd != IdReasonOfEnd_NotDetected; + protected virtual bool IsValidTelemetryRange(DetectableTelemetry[] telemetry, int begin, int end) + => end - begin > 1; + + protected virtual (int Begin, int End) RefineEdges(DetectableTelemetry[] telemetry, int begin, int end) + => (begin, end); + + protected virtual bool IsValidOperationDetectorResult(OperationDetectorResult operationDetectorResult) + => operationDetectorResult.Operation.DateEnd - operationDetectorResult.Operation.DateStart > TimeSpan.FromSeconds(3); protected abstract bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation); @@ -73,36 +89,51 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors ? IdReasonOfEnd_NotDetected : IdReasonOfEnd_NotDetectBegin; - private OperationDetectorResult? MakeOperation(int idTelemetry, DetectableTelemetry[] telemetry, int begin, int end, + private OperationDetectorResult MakeOperationDetectorResult( + int idTelemetry, + DetectableTelemetry[] telemetry, + int begin, + int end, int idReasonOfEnd) { - var pBegin = telemetry[begin]; - var pEnd = telemetry[end]; + var operation = MakeOperation(idTelemetry, telemetry, begin, end); + + operation.ExtraData["IdReasonOfEnd"] = idReasonOfEnd; + var result = new OperationDetectorResult { TelemetryBegin = begin, TelemetryEnd = end, - Operation = new DetectedOperation - { - IdTelemetry = idTelemetry, - IdCategory = GetIdOperation(telemetry, begin, end), - IdUsersAtStart = pBegin.IdUser ?? -1, - DateStart = pBegin.DateTime, - DateEnd = pEnd.DateTime, - DepthStart = (double)pBegin.WellDepth, - DepthEnd = (double)pEnd.WellDepth, - Value = CalcValue(telemetry, begin, end), - IdReasonOfEnd = idReasonOfEnd, - }, + Operation = operation, }; - return !IsValidOperationDetectorResult(result) ? null : result; + return result; } - protected abstract int GetIdOperation(DetectableTelemetry[] telemetry, int begin, int end); + protected abstract DetectedOperation MakeOperation(int idTelemetry, DetectableTelemetry[] telemetry, int begin, int end); protected abstract double CalcValue(DetectableTelemetry[] telemetry, int begin, int end); + protected DetectedOperation MakeDetectedOperationBlank(int idTelemetry, DetectableTelemetry[] telemetry, int begin, int end) + { + var pBegin = telemetry[begin]; + var pEnd = telemetry[end]; + + var operation = new DetectedOperation + { + IdTelemetry = idTelemetry, + IdUsersAtStart = pBegin.IdUser ?? -1, + DateStart = pBegin.DateTime, + DateEnd = pEnd.DateTime, + DepthStart = (double)pBegin.WellDepth, + DepthEnd = (double)pEnd.WellDepth, + ExtraData = new Dictionary(), + Value = CalcValue(telemetry, begin, end), + }; + + return operation; + } + /// /// Среднее арифметическое /// @@ -111,7 +142,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors /// /// /// - public static double CalcAvgAppr( + protected static double CalcAvgAppr( Func yGetter, DetectableTelemetry[] telemetry, int begin, diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrilling.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrilling.cs index 80889677..2120b271 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrilling.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrilling.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using AsbCloudDb.Model; @@ -7,10 +6,13 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors; public class DetectorDrilling : DetectorAbstract { - public IDictionary<(int, int), float> AvgRotorSpeedDict { get; } = new Dictionary<(int, int), float>(); - public IDictionary<(int, int), double> DespersionDict { get; } = new Dictionary<(int, int), double>(); + private const double dispersionOfNormalizedRotorSpeedThreshold = 0.2d; + public const string ExtraDataKeyHasOscillation = "hasOscillation"; + public const string ExtraDataKeyDispersionOfNormalizedRotorSpeed = "dispersionOfNormalizedRotorSpeed"; + public const string ExtraDataKeyAvgRotorSpeed = "avgRotorSpeed"; + public const string ExtraDataKeyIsAfbEnabled = "isAfbEnabled"; - protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) + protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) { var point0 = telemetry[position]; var delta = point0.WellDepth - point0.BitDepth; @@ -41,36 +43,49 @@ public class DetectorDrilling : DetectorAbstract => CalcRop(telemetry, begin, end); protected override bool IsValidOperationDetectorResult(OperationDetectorResult operationDetectorResult) => - operationDetectorResult.Operation.IdReasonOfEnd is IdReasonOfEnd_DeltaDepthIsHi or IdReasonOfEnd_PressureIsLo && Math.Abs(operationDetectorResult.Operation.DepthStart - operationDetectorResult.Operation.DepthEnd) > 0.01; - protected override int GetIdOperation(DetectableTelemetry[] telemetry, int begin, int end) + protected override (int Begin, int End) RefineEdges(DetectableTelemetry[] telemetry, int begin, int end) + { + var i = end; + for (; i > begin + 1; i--) + if (telemetry[i].WellDepth - telemetry[i - 1].WellDepth > 0.001d) + break; + + return (begin, i); + } + + protected override DetectedOperation MakeOperation(int idTelemetry, DetectableTelemetry[] telemetry, int begin, int end) + { + var (avgRotorSpeed, dispersionOfNormalizedRotorSpeed, isAfbEnabled) = CalcCriteries(telemetry, begin, end); + var operation = MakeDetectedOperationBlank(idTelemetry, telemetry, begin, end); + operation.IdCategory = GetIdOperation(avgRotorSpeed, dispersionOfNormalizedRotorSpeed); + operation.ExtraData[ExtraDataKeyAvgRotorSpeed] = avgRotorSpeed; + operation.ExtraData[ExtraDataKeyDispersionOfNormalizedRotorSpeed] = dispersionOfNormalizedRotorSpeed; + operation.ExtraData[ExtraDataKeyHasOscillation] = dispersionOfNormalizedRotorSpeed > dispersionOfNormalizedRotorSpeedThreshold; + operation.ExtraData[ExtraDataKeyIsAfbEnabled] = isAfbEnabled; + return operation; + } + + private static (double avgRotorSpeed, double dispersionOfNormalizedRotorSpeed, bool isAfbEnabled) CalcCriteries(DetectableTelemetry[] telemetry, int begin, int end) + { + var telemetryRange = telemetry[begin..end]; + var avgRotorSpeed = telemetryRange.Average(t => t.RotorSpeed); + var dispersion = telemetryRange.Average(t => Math.Pow(t.RotorSpeed / avgRotorSpeed - 1, 2)); + var isAfbEnabled = telemetryRange.Any(t => t.Mode > 0); + return (avgRotorSpeed, dispersion, isAfbEnabled); + } + + private static int GetIdOperation(double avgRotorSpeed, double dispersionOfNormalizedRotorSpeed) { - const int idSlideWithOscillation = 12000; - - var telemetryRange = telemetry[begin.. end] - .OrderBy(x => x.DateTime).ToList(); + const int idSlideWithOscillation = WellOperationCategory.IdSlide; - for (var i = telemetryRange.Count - 1; i >= 0 && telemetryRange.Count > 1; i--) - { - if (Math.Abs(telemetryRange[i].WellDepth - telemetryRange[i - 1].WellDepth) < 0.001d) - { - telemetryRange.RemoveAt(i); - continue; - } - - break; - } + if (avgRotorSpeed < 5) + return WellOperationCategory.IdSlide; - var avgRotorSpeed = telemetryRange.Average(t => t.RotorSpeed); - var despersion = telemetryRange.Average(t => Math.Pow(t.RotorSpeed/avgRotorSpeed - 1, 2)); - - AvgRotorSpeedDict.Add((begin, end), avgRotorSpeed); - DespersionDict.Add((begin, end), despersion); - - if (avgRotorSpeed < 5) - return WellOperationCategory.IdSlide; - - return despersion < 0.2d ? WellOperationCategory.IdRotor : idSlideWithOscillation; - } + if (dispersionOfNormalizedRotorSpeed < dispersionOfNormalizedRotorSpeedThreshold) + return WellOperationCategory.IdRotor; + else + return idSlideWithOscillation; + } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlipsTime.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlipsTime.cs index 7109376d..509970c4 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlipsTime.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlipsTime.cs @@ -27,10 +27,14 @@ public class DetectorSlipsTime : DetectorAbstract return true; } - protected override int GetIdOperation(DetectableTelemetry[] telemetry, int begin, int end) - => WellOperationCategory.IdSlipsTime; - protected override bool IsValidOperationDetectorResult(OperationDetectorResult operationDetectorResult) => Math.Abs(operationDetectorResult.Operation.DepthStart - operationDetectorResult.Operation.DepthEnd) > 0.01; + + protected override DetectedOperation MakeOperation(int idTelemetry, DetectableTelemetry[] telemetry, int begin, int end) + { + var operation = MakeDetectedOperationBlank(idTelemetry, telemetry, begin, end); + operation.IdCategory = WellOperationCategory.IdSlipsTime; + return operation; + } }