diff --git a/AsbCloudApp/Repositories/IDetectedOperationRepository.cs b/AsbCloudApp/Repositories/IDetectedOperationRepository.cs index 6b6e43f4..6e4eadaf 100644 --- a/AsbCloudApp/Repositories/IDetectedOperationRepository.cs +++ b/AsbCloudApp/Repositories/IDetectedOperationRepository.cs @@ -54,7 +54,7 @@ public interface IDetectedOperationRepository : ITelemetryDataEditorService Task> GetPageAsync(DetectedOperationByTelemetryRequest request, CancellationToken token); /// - /// Получение последних автоопределённых операций + /// Получение последних авто определённых операций /// /// /// diff --git a/AsbCloudApp/Services/IDetectedOperationService.cs b/AsbCloudApp/Services/IDetectedOperationService.cs index 3b555f37..631046c5 100644 --- a/AsbCloudApp/Services/IDetectedOperationService.cs +++ b/AsbCloudApp/Services/IDetectedOperationService.cs @@ -6,86 +6,85 @@ using System.Threading; using System.Threading.Tasks; using AsbCloudApp.Data.WellOperation; -namespace AsbCloudApp.Services +namespace AsbCloudApp.Services; + +/// +/// Сервис автоматически определенных по телеметрии операций +/// +public interface IDetectedOperationService { /// - /// Сервис автоматически определенных по телеметрии операций + /// Добавление операций /// - public interface IDetectedOperationService - { - /// - /// Добавление операций - /// - /// - /// - /// - /// - /// - Task InsertRangeManualAsync(int idEditor, int idWell, IEnumerable dtos, CancellationToken token); + /// + /// + /// + /// + /// + Task InsertRangeManualAsync(int idEditor, int idWell, IEnumerable dtos, CancellationToken token); - /// - /// Редактирование операций - /// - /// - /// - /// - /// - /// - Task UpdateRangeManualAsync(int idEditor, int idWell, IEnumerable dtos, CancellationToken token); + /// + /// Редактирование операций + /// + /// + /// + /// + /// + /// + Task UpdateRangeManualAsync(int idEditor, int idWell, IEnumerable dtos, CancellationToken token); - /// - /// Список названий операций. - /// Если указан idWell, то возвращается список названий операций найденных на указанной скважине. - /// - /// - /// - /// - Task> GetCategoriesAsync(int? idWell, CancellationToken token); + /// + /// Список названий операций. + /// Если указан idWell, то возвращается список названий операций найденных на указанной скважине. + /// + /// + /// + /// + Task> GetCategoriesAsync(int? idWell, CancellationToken token); - /// - /// Получить автоматически определенные по телеметрии операции с анализом по бурильщикам - /// - /// - /// - /// - Task GetAsync(DetectedOperationByWellRequest request, CancellationToken token); - - /// - /// Получить автоматически определенные по телеметрии операции - /// - /// - /// - /// - Task> GetOperationsAsync(DetectedOperationByWellRequest request, CancellationToken token); + /// + /// Получить автоматически определенные по телеметрии операции с анализом по бурильщикам + /// + /// + /// + /// + Task GetAsync(DetectedOperationByWellRequest request, CancellationToken token); + + /// + /// Получить автоматически определенные по телеметрии операции + /// + /// + /// + /// + Task> GetOperationsAsync(DetectedOperationByWellRequest request, CancellationToken token); - /// - /// Удалить операции - /// - /// - /// - /// - Task DeleteAsync(DetectedOperationByWellRequest request, CancellationToken token); + /// + /// Удалить операции + /// + /// + /// + /// + Task DeleteAsync(DetectedOperationByWellRequest request, CancellationToken token); - /// - /// Статистика по операциям - /// - /// - /// - /// - [Obsolete] - Task> GetOperationsStatAsync(DetectedOperationByWellRequest request, CancellationToken token); + /// + /// Статистика по операциям + /// + /// + /// + /// + [Obsolete] + Task> GetOperationsStatAsync(DetectedOperationByWellRequest request, CancellationToken token); - /// - /// Определение операций - /// - /// - /// - /// - /// - /// - Task<(DateTimeOffset LastDate, IEnumerable Items)> DetectOperationsAsync(int idTelemetry, - TelemetryDataRequest request, - DetectedOperationDto? lastDetectedOperation, - CancellationToken token); - } + /// + /// Определение операций + /// + /// + /// + /// + /// + /// + Task<(DateTimeOffset LastDate, IEnumerable Items)> DetectOperationsAsync(int idTelemetry, + TelemetryDataRequest request, + DetectedOperationDto? lastDetectedOperation, + CancellationToken token); } diff --git a/AsbCloudApp/Services/ITelemetryDataSaubService.cs b/AsbCloudApp/Services/ITelemetryDataSaubService.cs index da32ef50..3823788a 100644 --- a/AsbCloudApp/Services/ITelemetryDataSaubService.cs +++ b/AsbCloudApp/Services/ITelemetryDataSaubService.cs @@ -4,46 +4,44 @@ using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; -using AsbCloudApp.Data; -namespace AsbCloudApp.Services +namespace AsbCloudApp.Services; + +/// +/// Телеметрия САУБ +/// +public interface ITelemetryDataSaubService : ITelemetryDataService { /// - /// Телеметрия САУБ + /// Получение телеметрии для РТК статистики /// - public interface ITelemetryDataSaubService : ITelemetryDataService - { - /// - /// Получение телеметрии для РТК статистики - /// - /// - /// - /// - /// - /// - /// - /// - Task> Get(int idTelemetry, bool isBitOnBottom, DateTimeOffset geDate, DateTimeOffset leDate, int take, CancellationToken token); + /// + /// + /// + /// + /// + /// + /// + Task> Get(int idTelemetry, bool isBitOnBottom, DateTimeOffset geDate, DateTimeOffset leDate, int take, CancellationToken token); - /// - /// усредненная статистика по 1м за весь период - /// - /// МЕДЛЕННЫЙ ЗАПРОС - /// - /// - /// - /// - /// - Task> GetTelemetryDataStatAsync(int idTelemetry, CancellationToken token); + /// + /// усредненная статистика по 1м за весь период + /// + /// МЕДЛЕННЫЙ ЗАПРОС + /// + /// + /// + /// + /// + Task> GetTelemetryDataStatAsync(int idTelemetry, CancellationToken token); - /// - /// Получить упакованый csv файл - /// - /// - /// - /// - /// - /// - Task GetZippedCsv(int idWell, DateTime beginDate, DateTime endDate, CancellationToken token); - } + /// + /// Получить упакованный csv файл + /// + /// + /// + /// + /// + /// + Task GetZippedCsv(int idWell, DateTime beginDate, DateTime endDate, CancellationToken token); } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationExportService.cs b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationExportService.cs index 98a9f298..daffc4e3 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationExportService.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationExportService.cs @@ -1,6 +1,5 @@ using AsbCloudDb.Model; using ClosedXML.Excel; -using System; using System.Collections.Generic; using System.IO; using System.Linq; diff --git a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs index 4b21ca96..aacfe853 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs @@ -110,7 +110,7 @@ public class DetectedOperationService : IDetectedOperationService private async Task GetIdTelemetryByWell(int idWell, CancellationToken token) { var well = await wellService.GetOrDefaultAsync(idWell, token) ?? - throw new ArgumentInvalidException(nameof(idWell), "Well doesn`t exist"); + throw new ArgumentInvalidException(nameof(idWell), "Well doesn't exist"); var idTelemetry = well.IdTelemetry ?? throw new ArgumentInvalidException(nameof(idWell), "У скважины отсутствует телеметрия"); @@ -193,12 +193,12 @@ public class DetectedOperationService : IDetectedOperationService if (count == 0) throw new InvalidOperationException("InvalidOperation_EmptyTelemetries"); - var timezone = telemetryService.GetTimezone(idTelemetry); + var timeZone = telemetryService.GetTimezone(idTelemetry); if (telemetries.Count() <= gap) { var lastTelemetry = telemetries.Last(); - var lastDateTelemetry = new DateTimeOffset(lastTelemetry.DateTime, timezone.Offset); + var lastDateTelemetry = new DateTimeOffset(lastTelemetry.DateTime, timeZone.Offset); return (lastDateTelemetry, Enumerable.Empty()); } @@ -208,7 +208,7 @@ public class DetectedOperationService : IDetectedOperationService .Where(t => t.BlockPosition >= 0) .Select(t => new DetectableTelemetry { - DateTime = new DateTimeOffset(t.DateTime, timezone.Offset), + DateTime = new DateTimeOffset(t.DateTime, timeZone.Offset), IdUser = t.IdUser, Mode = t.Mode, WellDepth = t.WellDepth, @@ -270,8 +270,10 @@ public class DetectedOperationService : IDetectedOperationService EqualParameter(telemetryBegin.RotorSpeed, telemetryEnd.RotorSpeed, 0.01f) && EqualParameter(telemetryBegin.AxialLoad, telemetryEnd.AxialLoad, 0.1f); - bool EqualParameter(float value, float origin, float tolerance) => - value <= origin + tolerance && value >= origin - tolerance; + static bool EqualParameter(float value, float origin, float tolerance) + { + return value <= origin + tolerance && value >= origin - tolerance; + } } private static IEnumerable GetOperationsDrillersStat(IEnumerable operations) @@ -323,7 +325,6 @@ public class DetectedOperationService : IDetectedOperationService }; } - private static DetectedOperationWithDrillerDto Convert(DetectedOperationDto operation, IEnumerable operationValues, IEnumerable schedules) { var dto = operation.Adapt(); diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorAbstract.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorAbstract.cs index 623608d8..5f17a248 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorAbstract.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorAbstract.cs @@ -2,412 +2,410 @@ using System; using System.Collections.Generic; using AsbCloudApp.Data.DetectedOperation; -namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors +namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors; + +public abstract class DetectorAbstract { - public abstract class DetectorAbstract + protected const int IdReasonOfEnd_NotDetected = 0; + protected const int IdReasonOfEnd_NotDetectBegin = 1; + + protected const int IdReasonOfEnd_DeltaDepthIsLo = 100; + protected const int IdReasonOfEnd_DeltaDepthIsHi = 101; + protected const int IdReasonOfEnd_DeltaDepthOutOfRange = 102; + protected const int IdReasonOfEnd_WellDepthDeviates = 200; + + protected const int IdReasonOfEnd_PressureIsLo = 300; + protected const int IdReasonOfEnd_PressureIsHi = 301; + protected const int IdReasonOfEnd_PressureOutOfRange = 302; + protected const int IdReasonOfEnd_PressureIsRising = 303; + + protected const int IdReasonOfEnd_RotorSpeedIsLo = 400; + protected const int IdReasonOfEnd_RotorSpeedIsHi = 401; + protected const int IdReasonOfEnd_AvgRotorSpeedIsHi = 402; + protected const int IdReasonOfEnd_AvgRotorSpeedIsLo = 403; + + protected const int IdReasonOfEnd_BlockPositionIsLo = 500; + protected const int IdReasonOfEnd_BlockPositionIsHi = 501; + protected const int IdReasonOfEnd_BlockPositionDeviates = 502; + + protected const int IdReasonOfEnd_Drilling = 600; + + protected const int IdReasonOfEnd_ChangeBitDepthAndAxiLoadLessHookWeight = 700; + + protected const int IdReasonOfEnd_DeltaWellDepthAndBitDepthIsLo = 800; + + protected const int IdReasonOfEnd_BitDepthIsLo = 900; + + public bool TryDetect(int idTelemetry, DetectableTelemetry[] telemetry, int begin, int end, DetectedOperationDto? previousOperation, + out OperationDetectorResult? result) { - protected const int IdReasonOfEnd_NotDetected = 0; - protected const int IdReasonOfEnd_NotDetectBegin = 1; - - protected const int IdReasonOfEnd_DeltaDepthIsLo = 100; - protected const int IdReasonOfEnd_DeltaDepthIsHi = 101; - protected const int IdReasonOfEnd_DeltaDepthOutOfRange = 102; - protected const int IdReasonOfEnd_WellDepthDeviates = 200; - - protected const int IdReasonOfEnd_PressureIsLo = 300; - protected const int IdReasonOfEnd_PressureIsHi = 301; - protected const int IdReasonOfEnd_PressureOutOfRange = 302; - protected const int IdReasonOfEnd_PressureIsRising = 303; - - protected const int IdReasonOfEnd_RotorSpeedIsLo = 400; - protected const int IdReasonOfEnd_RotorSpeedIsHi = 401; - protected const int IdReasonOfEnd_AvgRotorSpeedIsHi = 402; - protected const int IdReasonOfEnd_AvgRotorSpeedIsLo = 403; - - protected const int IdReasonOfEnd_BlockPositionIsLo = 500; - protected const int IdReasonOfEnd_BlockPositionIsHi = 501; - protected const int IdReasonOfEnd_BlockPositionDeviates = 502; - - protected const int IdReasonOfEnd_Drilling = 600; - - protected const int IdReasonOfEnd_ChangeBithDepthAndAxiloadLessHookWeight = 700; - - protected const int IdReasonOfEnd_DeltaWellDepthAndBithDepthIsLo = 800; - - protected const int IdReasonOfEnd_BithDepthIsLo = 900; - - public bool TryDetect(int idTelemetry, DetectableTelemetry[] telemetry, int begin, int end, DetectedOperationDto? previousOperation, - out OperationDetectorResult? result) + // Проверка соответствия критерию начала операции + if (DetectBegin(telemetry, begin, previousOperation)) { - // Проверка соответствия критерию начала операции - if (DetectBegin(telemetry, begin, previousOperation)) - { - // Поиск окончания соответствия критерию - int idReasonOfEnd = 0; - var positionEnd = begin; - - while (positionEnd < end) - { - positionEnd += 1; - if (positionEnd > end) - break; - - //TODO: поиск провалов телеметрий. Следует обсудить, так как алгоритмы теряют в точности. - - idReasonOfEnd = DetectEnd(telemetry, positionEnd, previousOperation); - - if (idReasonOfEnd != IdReasonOfEnd_NotDetected) - break; - } - - var (Begin, End) = RefineEdges(telemetry, begin, positionEnd); - - result = MakeOperationDetectorResult(idTelemetry, telemetry, Begin, End, idReasonOfEnd); - - return IsValidOperationDetectorResult(result); - } - - result = null; - return false; - } - - 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, DetectedOperationDto? previousOperation); - - protected virtual int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperationDto? previousOperation) - => DetectBegin(telemetry, position, previousOperation) - ? IdReasonOfEnd_NotDetected - : IdReasonOfEnd_NotDetectBegin; - - private OperationDetectorResult MakeOperationDetectorResult( - int idTelemetry, - DetectableTelemetry[] telemetry, - int begin, - int end, - int idReasonOfEnd) - { - var operation = MakeDetectedOperation(idTelemetry, telemetry, begin, end); - - operation.ExtraData["IdReasonOfEnd"] = idReasonOfEnd; - - var result = new OperationDetectorResult - { - TelemetryBegin = begin, - TelemetryEnd = end, - Operation = operation, - }; - - return result; - } - - private DetectedOperationDto MakeDetectedOperation(int idTelemetry, DetectableTelemetry[] telemetry, int begin, int end) - { - var pBegin = telemetry[begin]; - var pEnd = telemetry[end]; - var (IdCategory, ExtraData) = GetSpecificInformation(telemetry, begin, end); - var operation = new DetectedOperationDto - { - IdCategory = IdCategory, - IdTelemetry = idTelemetry, - IdUserAtStart = pBegin.IdUser ?? -1, - DateStart = pBegin.DateTime, - DateEnd = pEnd.DateTime, - DepthStart = (double)pBegin.WellDepth, - DepthEnd = (double)pEnd.WellDepth, - ExtraData = ExtraData, - Value = CalcValue(telemetry, begin, end), - EnabledSubsystems = DetectEnabledSubsystems(telemetry, begin, end, ExtraData) - }; - - return operation; - } - - /// - /// Получение информации специфичной для конкретного детектора - /// IdCategory - одна из констант WellOperationCategory - /// ExtraData - дополнительная информация для отладки алгоритмов авто определения - /// - /// - protected abstract (int IdCategory, IDictionary ExtraData) GetSpecificInformation(DetectableTelemetry[] telemetry, int begin, int end); - - /// - /// Расчет ключевого параметра операции - /// - /// - /// - /// - /// - protected abstract double CalcValue(DetectableTelemetry[] telemetry, int begin, int end); - - /// - /// Определение включенных подсистем во время выполнения операции - /// - /// - /// - /// - /// - /// - private static int DetectEnabledSubsystems(DetectableTelemetry[] telemetry, int begin, int end, IDictionary extraData) - { - var enabledSubsystems = 0; - - if (extraData.TryGetValue(DetectorDrilling.ExtraDataKeyHasOscillation, out var hasOscillation) - && hasOscillation is true) - enabledSubsystems |= (int)EnabledSubsystemsFlags.AutoOscillation; + // Поиск окончания соответствия критерию + int idReasonOfEnd = 0; + var positionEnd = begin; - for (var i = begin; i < end; i += 2) + while (positionEnd < end) { - var mode = telemetry[i].Mode; + positionEnd += 1; + if (positionEnd > end) + break; + + //TODO: поиск провалов телеметрий. Следует обсудить, так как алгоритмы теряют в точности. - if(mode == 1) - enabledSubsystems |= (int)EnabledSubsystemsFlags.AutoRotor; + idReasonOfEnd = DetectEnd(telemetry, positionEnd, previousOperation); - if (mode == 3) - enabledSubsystems |= (int)EnabledSubsystemsFlags.AutoSlide; - - if (mode == 2) - enabledSubsystems |= (int)EnabledSubsystemsFlags.AutoConditionig; - - if (mode == 4) - enabledSubsystems |= (int)EnabledSubsystemsFlags.AutoSinking; - - if (mode == 5) - enabledSubsystems |= (int)EnabledSubsystemsFlags.AutoLifting; - - if (mode == 6) - enabledSubsystems |= (int)EnabledSubsystemsFlags.AutoLiftingWithConditionig; - - if (mode == 10) - enabledSubsystems |= (int)EnabledSubsystemsFlags.AutoBlocknig; + if (idReasonOfEnd != IdReasonOfEnd_NotDetected) + break; } + + var (Begin, End) = RefineEdges(telemetry, begin, positionEnd); + + result = MakeOperationDetectorResult(idTelemetry, telemetry, Begin, End, idReasonOfEnd); - return enabledSubsystems; + return IsValidOperationDetectorResult(result); } - /// - /// расчет продолжительности операции - /// - /// - /// - /// - /// - protected static double CalcDeltaMinutes(DetectableTelemetry[] telemetry, int begin, int end) + result = null; + return false; + } + + 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, DetectedOperationDto? previousOperation); + + protected virtual int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperationDto? previousOperation) + => DetectBegin(telemetry, position, previousOperation) + ? IdReasonOfEnd_NotDetected + : IdReasonOfEnd_NotDetectBegin; + + private OperationDetectorResult MakeOperationDetectorResult( + int idTelemetry, + DetectableTelemetry[] telemetry, + int begin, + int end, + int idReasonOfEnd) + { + var operation = MakeDetectedOperation(idTelemetry, telemetry, begin, end); + + operation.ExtraData["IdReasonOfEnd"] = idReasonOfEnd; + + var result = new OperationDetectorResult { - var pBegin = telemetry[begin]; - var pEnd = telemetry[end]; - var result = (pEnd.DateTime - pBegin.DateTime).TotalMinutes; - return result; + TelemetryBegin = begin, + TelemetryEnd = end, + Operation = operation, + }; + + return result; + } + + private DetectedOperationDto MakeDetectedOperation(int idTelemetry, DetectableTelemetry[] telemetry, int begin, int end) + { + var pBegin = telemetry[begin]; + var pEnd = telemetry[end]; + var (IdCategory, ExtraData) = GetSpecificInformation(telemetry, begin, end); + var operation = new DetectedOperationDto + { + IdCategory = IdCategory, + IdTelemetry = idTelemetry, + IdUserAtStart = pBegin.IdUser ?? -1, + DateStart = pBegin.DateTime, + DateEnd = pEnd.DateTime, + DepthStart = (double)pBegin.WellDepth, + DepthEnd = (double)pEnd.WellDepth, + ExtraData = ExtraData, + Value = CalcValue(telemetry, begin, end), + EnabledSubsystems = DetectEnabledSubsystems(telemetry, begin, end, ExtraData) + }; + + return operation; + } + + /// + /// Получение информации специфичной для конкретного детектора + /// IdCategory - одна из констант WellOperationCategory + /// ExtraData - дополнительная информация для отладки алгоритмов авто определения + /// + /// + protected abstract (int IdCategory, IDictionary ExtraData) GetSpecificInformation(DetectableTelemetry[] telemetry, int begin, int end); + + /// + /// Расчет ключевого параметра операции + /// + /// + /// + /// + /// + protected abstract double CalcValue(DetectableTelemetry[] telemetry, int begin, int end); + + /// + /// Определение включенных подсистем во время выполнения операции + /// + /// + /// + /// + /// + /// + private static int DetectEnabledSubsystems(DetectableTelemetry[] telemetry, int begin, int end, IDictionary extraData) + { + var enabledSubsystems = 0; + + if (extraData.TryGetValue(DetectorDrilling.ExtraDataKeyHasOscillation, out var hasOscillation) + && hasOscillation is true) + enabledSubsystems |= (int)EnabledSubsystemsFlags.AutoOscillation; + + for (var i = begin; i < end; i += 2) + { + var mode = telemetry[i].Mode; + + if(mode == 1) + enabledSubsystems |= (int)EnabledSubsystemsFlags.AutoRotor; + + if (mode == 3) + enabledSubsystems |= (int)EnabledSubsystemsFlags.AutoSlide; + + if (mode == 2) + enabledSubsystems |= (int)EnabledSubsystemsFlags.AutoConditionig; + + if (mode == 4) + enabledSubsystems |= (int)EnabledSubsystemsFlags.AutoSinking; + + if (mode == 5) + enabledSubsystems |= (int)EnabledSubsystemsFlags.AutoLifting; + + if (mode == 6) + enabledSubsystems |= (int)EnabledSubsystemsFlags.AutoLiftingWithConditionig; + + if (mode == 10) + enabledSubsystems |= (int)EnabledSubsystemsFlags.AutoBlocknig; } - /// - /// часто используемый предикат для определения отсутствия изменения глубины ствола скважины - /// - /// - /// - /// - /// - protected static bool IsValidByWellDepthDoesNotChange(DetectableTelemetry[] telemetry, int begin, int end) - { - var pBegin = telemetry[begin]; - var pEnd = telemetry[end]; - if (Math.Abs((double)(pBegin.WellDepth - pEnd.WellDepth)) > 0.01) - return false; - return true; - } + return enabledSubsystems; + } - protected static bool IsValidByWellDepthIncreasing(DetectableTelemetry[] telemetry, int begin, int end) - { - var pBegin = telemetry[begin]; - var pEnd = telemetry[end]; - if (pBegin.WellDepth >= pEnd.WellDepth) - return false; - return true; - } - - protected static double CalcRop(DetectableTelemetry[] telemetry, int begin, int end) - { - var pBegin = telemetry[begin]; - var pEnd = telemetry[end]; - var result = (double)(pEnd.WellDepth - pBegin.WellDepth) / (pEnd.DateTime - pBegin.DateTime).TotalHours; - return result; - } - - /// - /// Расчет статистики по массиву данных за интервал - /// - /// - /// - /// - /// - /// - protected static (double min, double max, double sum, int count) CalcStat( - DetectableTelemetry[] telemetry, - Func getter, - int begin, - int count) - { - var sum = 0d; - var min = double.MaxValue; - var max = double.MinValue; - var end = begin + count; - end = end < telemetry.Length ? end : telemetry.Length; - - for (var i = begin; i < end; i++) - { - var item = telemetry[i]; - var itemValue = getter(item); - if (min > itemValue) - min = itemValue; - if (max < itemValue) - max = itemValue; - sum += itemValue; - } - - return (min, max, sum, end - begin); - } - - /// - /// Максимальное отклонение от среднего за интервал - /// - /// - /// - /// - /// - /// - protected static double CalcMaxDeviation( - DetectableTelemetry[] telemetry, - Func getter, - int begin, - int count) - { - var stat = CalcStat(telemetry, getter, begin, count); - var avg = stat.sum / stat.count; - var dev1 = avg - stat.min; - var dev2 = stat.max - avg; - var dev = dev1 > dev2 ? dev1 : dev2; - return dev; - } - - /// - /// Определяет наличие разброса значений в интервале большего указанного значения. - /// - /// - /// - /// - /// - /// - /// - protected static bool ContainsDeviation( - DetectableTelemetry[] telemetry, - Func getter, - int begin, - int count, - double deviation) - { - var min = double.MaxValue; - var max = double.MinValue; - var end = begin + count; - end = end < telemetry.Length ? end : telemetry.Length; - - for (var i = begin; i < end; i++) - { - var item = telemetry[i]; - var itemValue = getter(item); - if (min > itemValue) - min = itemValue; - if (max < itemValue) - max = itemValue; - if (max - min > deviation) - return true; - } + /// + /// расчет продолжительности операции + /// + /// + /// + /// + /// + protected static double CalcDeltaMinutes(DetectableTelemetry[] telemetry, int begin, int end) + { + var pBegin = telemetry[begin]; + var pEnd = telemetry[end]; + var result = (pEnd.DateTime - pBegin.DateTime).TotalMinutes; + return result; + } + /// + /// часто используемый предикат для определения отсутствия изменения глубины ствола скважины + /// + /// + /// + /// + /// + protected static bool IsValidByWellDepthDoesNotChange(DetectableTelemetry[] telemetry, int begin, int end) + { + var pBegin = telemetry[begin]; + var pEnd = telemetry[end]; + if (Math.Abs((double)(pBegin.WellDepth - pEnd.WellDepth)) > 0.01) return false; - } - - /// - /// Определяет наличие разброса значений в интервале большего указанного значения. По нескольким значениям из интервала. - /// - /// - /// - /// - /// - /// - /// - protected static bool ContainsDeviationApprox( - DetectableTelemetry[] telemetry, - Func getter, - int begin, - int count, - double deviation) - { - var min = double.MaxValue; - var max = double.MinValue; - var end = begin + count; - end = end < telemetry.Length ? end : telemetry.Length; - var step = count > 15 ? count / 5 : count > 3 ? 3 : 1; - for (var i = begin; i < end; i += step) - { - var item = telemetry[i]; - var itemValue = getter(item); - if (min > itemValue) - min = itemValue; - if (max < itemValue) - max = itemValue; - if (max - min > deviation) - return true; - } + return true; + } + protected static bool IsValidByWellDepthIncreasing(DetectableTelemetry[] telemetry, int begin, int end) + { + var pBegin = telemetry[begin]; + var pEnd = telemetry[end]; + if (pBegin.WellDepth >= pEnd.WellDepth) return false; - } + return true; + } - protected static bool DeviatesFromBegin( - DetectableTelemetry[] telemetry, - Func getter, - int begin, - int count, - double deviation) + protected static double CalcRop(DetectableTelemetry[] telemetry, int begin, int end) + { + var pBegin = telemetry[begin]; + var pEnd = telemetry[end]; + var result = (double)(pEnd.WellDepth - pBegin.WellDepth) / (pEnd.DateTime - pBegin.DateTime).TotalHours; + return result; + } + + /// + /// Расчет статистики по массиву данных за интервал + /// + /// + /// + /// + /// + /// + protected static (double min, double max, double sum, int count) CalcStat( + DetectableTelemetry[] telemetry, + Func getter, + int begin, + int count) + { + var sum = 0d; + var min = double.MaxValue; + var max = double.MinValue; + var end = begin + count; + end = end < telemetry.Length ? end : telemetry.Length; + + for (var i = begin; i < end; i++) { - var beginPointValue = getter(telemetry[begin]); - var end = begin + count; - end = end < telemetry.Length ? end : telemetry.Length; - var step = count > 15 ? count / 5 : count > 3 ? 3 : 1; - for (var i = begin; i < end; i += step) - { - var item = telemetry[i]; - var itemValue = getter(item); - if (Math.Abs(beginPointValue - itemValue) > deviation) - return true; - } - - return false; + var item = telemetry[i]; + var itemValue = getter(item); + if (min > itemValue) + min = itemValue; + if (max < itemValue) + max = itemValue; + sum += itemValue; } - protected static bool RisesFromBegin( - DetectableTelemetry[] telemetry, - Func getter, - int begin, - int count, - double deviation) + return (min, max, sum, end - begin); + } + + /// + /// Максимальное отклонение от среднего за интервал + /// + /// + /// + /// + /// + /// + protected static double CalcMaxDeviation( + DetectableTelemetry[] telemetry, + Func getter, + int begin, + int count) + { + var stat = CalcStat(telemetry, getter, begin, count); + var avg = stat.sum / stat.count; + var dev1 = avg - stat.min; + var dev2 = stat.max - avg; + var dev = dev1 > dev2 ? dev1 : dev2; + return dev; + } + + /// + /// Определяет наличие разброса значений в интервале большего указанного значения. + /// + /// + /// + /// + /// + /// + /// + protected static bool ContainsDeviation( + DetectableTelemetry[] telemetry, + Func getter, + int begin, + int count, + double deviation) + { + var min = double.MaxValue; + var max = double.MinValue; + var end = begin + count; + end = end < telemetry.Length ? end : telemetry.Length; + + for (var i = begin; i < end; i++) { - var beginPointValue = getter(telemetry[begin]); - var end = begin + count; - end = end < telemetry.Length ? end : telemetry.Length; - var step = count > 15 ? count / 5 : count > 3 ? 3 : 1; - for (var i = begin; i < end; i += step) - { - var item = telemetry[i]; - var itemValue = getter(item); - if (itemValue - beginPointValue > deviation) - return true; - } - - return false; + var item = telemetry[i]; + var itemValue = getter(item); + if (min > itemValue) + min = itemValue; + if (max < itemValue) + max = itemValue; + if (max - min > deviation) + return true; } + + return false; + } + + /// + /// Определяет наличие разброса значений в интервале большего указанного значения. По нескольким значениям из интервала. + /// + /// + /// + /// + /// + /// + /// + protected static bool ContainsDeviationApprox( + DetectableTelemetry[] telemetry, + Func getter, + int begin, + int count, + double deviation) + { + var min = double.MaxValue; + var max = double.MinValue; + var end = begin + count; + end = end < telemetry.Length ? end : telemetry.Length; + var step = count > 15 ? count / 5 : count > 3 ? 3 : 1; + for (var i = begin; i < end; i += step) + { + var item = telemetry[i]; + var itemValue = getter(item); + if (min > itemValue) + min = itemValue; + if (max < itemValue) + max = itemValue; + if (max - min > deviation) + return true; + } + + return false; + } + + protected static bool DeviatesFromBegin( + DetectableTelemetry[] telemetry, + Func getter, + int begin, + int count, + double deviation) + { + var beginPointValue = getter(telemetry[begin]); + var end = begin + count; + end = end < telemetry.Length ? end : telemetry.Length; + var step = count > 15 ? count / 5 : count > 3 ? 3 : 1; + for (var i = begin; i < end; i += step) + { + var item = telemetry[i]; + var itemValue = getter(item); + if (Math.Abs(beginPointValue - itemValue) > deviation) + return true; + } + + return false; + } + + protected static bool RisesFromBegin( + DetectableTelemetry[] telemetry, + Func getter, + int begin, + int count, + double deviation) + { + var beginPointValue = getter(telemetry[begin]); + var end = begin + count; + end = end < telemetry.Length ? end : telemetry.Length; + var step = count > 15 ? count / 5 : count > 3 ? 3 : 1; + for (var i = begin; i < end; i += step) + { + var item = telemetry[i]; + var itemValue = getter(item); + if (itemValue - beginPointValue > deviation) + return true; + } + + return false; } } - diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorConditioning.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorConditioning.cs index 1a5142e3..6ef413ef 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorConditioning.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorConditioning.cs @@ -41,9 +41,9 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors if (currentPoint.RotorSpeed <=8) return IdReasonOfEnd_RotorSpeedIsHi; if ((currentPoint.WellDepth - currentPoint.BitDepth) < 0.03d) - return IdReasonOfEnd_DeltaWellDepthAndBithDepthIsLo; + return IdReasonOfEnd_DeltaWellDepthAndBitDepthIsLo; if (currentPoint.BitDepth < 150) - return IdReasonOfEnd_BithDepthIsLo; + return IdReasonOfEnd_BitDepthIsLo; return IdReasonOfEnd_NotDetected; } diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorFlashing.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorFlashing.cs index 24b69219..f300f1c5 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorFlashing.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorFlashing.cs @@ -48,11 +48,11 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors if (currentPoint.Pressure < 10) return IdReasonOfEnd_PressureIsLo; if ((currentPoint.WellDepth - currentPoint.BitDepth) < 0.01d) - return IdReasonOfEnd_DeltaWellDepthAndBithDepthIsLo; + return IdReasonOfEnd_DeltaWellDepthAndBitDepthIsLo; if (currentPoint.RotorSpeed > 8) return IdReasonOfEnd_RotorSpeedIsHi; if (currentPoint.BitDepth < 150) - return IdReasonOfEnd_BithDepthIsLo; + return IdReasonOfEnd_BitDepthIsLo; return IdReasonOfEnd_NotDetected; } diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlipsTime.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlipsTime.cs index 47208183..af2e3db0 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlipsTime.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlipsTime.cs @@ -50,7 +50,7 @@ public class DetectorSlipsTime : DetectorAbstract var deltaBitDepth = Math.Abs(currentPoint.BitDepth - prevPoint.BitDepth); if (deltaBitDepth > 0.001d && currentPoint.AxialLoad < currentPoint.HookWeight) - return IdReasonOfEnd_ChangeBithDepthAndAxiloadLessHookWeight; + return IdReasonOfEnd_ChangeBitDepthAndAxiLoadLessHookWeight; return IdReasonOfEnd_NotDetected; } diff --git a/AsbCloudInfrastructure/Services/DetectOperations/WorkOperationDetection.cs b/AsbCloudInfrastructure/Services/DetectOperations/WorkOperationDetection.cs index e4ae3eb4..93a8b9c3 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/WorkOperationDetection.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/WorkOperationDetection.cs @@ -61,8 +61,8 @@ public class WorkOperationDetection : Work if (lastDetectedOperations.TryGetValue(idTelemetry, out var lastDetectedOperation)) dateBegin = lastDetectedOperation.DateEnd; - if (CacheOfStartDatesByTelemetryId.TryGetValue(idTelemetry, out var dateBeginFromCahce)) - dateBegin = dateBeginFromCahce; + if (CacheOfStartDatesByTelemetryId.TryGetValue(idTelemetry, out var dateBeginFromCache)) + dateBegin = dateBeginFromCache; onProgressCallback.Invoke($"Start detecting telemetry: {idTelemetry} from {dateBegin}", i / idsTelemetry.Length); diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs index 3dbb2e58..31bf9a3f 100644 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs +++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs @@ -13,299 +13,298 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -namespace AsbCloudInfrastructure.Services.SAUB +namespace AsbCloudInfrastructure.Services.SAUB; + +public abstract class TelemetryDataBaseService : ITelemetryDataService + where TDto : AsbCloudApp.Data.ITelemetryData + where TEntity : class, AsbCloudDb.Model.ITelemetryData { - public abstract class TelemetryDataBaseService : ITelemetryDataService - where TDto : AsbCloudApp.Data.ITelemetryData - where TEntity : class, AsbCloudDb.Model.ITelemetryData + protected readonly IAsbCloudDbContext db; + protected readonly ITelemetryService telemetryService; + protected readonly ITelemetryDataCache telemetryDataCache; + + protected TelemetryDataBaseService( + IAsbCloudDbContext db, + ITelemetryService telemetryService, + ITelemetryDataCache telemetryDataCache) { - protected readonly IAsbCloudDbContext db; - protected readonly ITelemetryService telemetryService; - protected readonly ITelemetryDataCache telemetryDataCache; + this.db = db; + this.telemetryService = telemetryService; + this.telemetryDataCache = telemetryDataCache; + } - protected TelemetryDataBaseService( - IAsbCloudDbContext db, - ITelemetryService telemetryService, - ITelemetryDataCache telemetryDataCache) + /// + public virtual async Task UpdateDataAsync(string uid, IEnumerable dtos, CancellationToken token = default) + { + if (dtos == default || !dtos.Any()) + return 0; + + var dtosList = dtos.OrderBy(d => d.DateTime).ToList(); + + var dtoMinDate = dtosList.First().DateTime; + var dtoMaxDate = dtosList.Last().DateTime; + + if (dtosList.Count > 1) { - this.db = db; - this.telemetryService = telemetryService; - this.telemetryDataCache = telemetryDataCache; + var duplicates = new List(8); + for (int i = 1; i < dtosList.Count; i++) + if (dtosList[i].DateTime - dtosList[i - 1].DateTime < TimeSpan.FromMilliseconds(100)) + duplicates.Add(dtosList[i - 1]); + foreach (var duplicate in duplicates) + dtosList.Remove(duplicate); } - /// - public virtual async Task UpdateDataAsync(string uid, IEnumerable dtos, CancellationToken token = default) + var telemetry = telemetryService.GetOrCreateTelemetryByUid(uid); + var timeZone = telemetryService.GetTimezone(telemetry.Id); + + telemetryDataCache.AddRange(telemetry.Id, dtos); + + var entities = dtosList.Select(dto => { - if (dtos == default || !dtos.Any()) - return 0; + var entity = Convert(dto, timeZone.Hours); + entity.IdTelemetry = telemetry.Id; + return entity; + }); - var dtosList = dtos.OrderBy(d => d.DateTime).ToList(); - - var dtoMinDate = dtosList.First().DateTime; - var dtoMaxDate = dtosList.Last().DateTime; - - if (dtosList.Count > 1) - { - var duplicates = new List(8); - for (int i = 1; i < dtosList.Count; i++) - if (dtosList[i].DateTime - dtosList[i - 1].DateTime < TimeSpan.FromMilliseconds(100)) - duplicates.Add(dtosList[i - 1]); - foreach (var duplicate in duplicates) - dtosList.Remove(duplicate); - } - - var telemetry = telemetryService.GetOrCreateTelemetryByUid(uid); - var timezone = telemetryService.GetTimezone(telemetry.Id); - - telemetryDataCache.AddRange(telemetry.Id, dtos); - - var entities = dtosList.Select(dto => - { - var entity = Convert(dto, timezone.Hours); - entity.IdTelemetry = telemetry.Id; - return entity; - }); - - var stopwatch = Stopwatch.StartNew(); - var dbSet = db.Set(); - try - { - return await db.Database.ExecInsertOrUpdateAsync(dbSet, entities, token).ConfigureAwait(false); - } - catch (Exception ex) - { - stopwatch.Stop(); - Trace.WriteLine($"Fail to save data telemetry " + - $"uid: {uid}, " + - $"idTelemetry {telemetry.Id}, " + - $"count: {entities.Count()}, " + - $"dataDate: {entities.FirstOrDefault()?.DateTime}, " + - $"dbSaveDurationTime:{stopwatch.ElapsedMilliseconds}ms. " + - $"Message: {ex.Message}"); - return 0; - } + var stopwatch = Stopwatch.StartNew(); + var dbSet = db.Set(); + try + { + return await db.Database.ExecInsertOrUpdateAsync(dbSet, entities, token).ConfigureAwait(false); } - - /// - public virtual async Task> GetByWellAsync(int idWell, - DateTime dateBegin = default, double intervalSec = 600d, - int approxPointsCount = 1024, CancellationToken token = default) + catch (Exception ex) { - var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell); - if (telemetry is null) - return Enumerable.Empty(); - - var timezone = telemetryService.GetTimezone(telemetry.Id); - - var filterByDateEnd = dateBegin != default; - DateTimeOffset dateBeginUtc; - if (dateBegin == default) - { - var dateRange = telemetryDataCache.GetOrDefaultWellDataDateRange(telemetry.Id); - dateBeginUtc = (dateRange?.To.ToUniversalTime() ?? DateTimeOffset.UtcNow) - .AddSeconds(-intervalSec); - } - else - { - dateBeginUtc = dateBegin.ToUtcDateTimeOffset(timezone.Hours); - } - - var cacheData = telemetryDataCache.GetOrDefault(telemetry.Id, dateBeginUtc.ToRemoteDateTime(timezone.Hours), intervalSec, approxPointsCount); - if (cacheData is not null) - return cacheData; - - var dateEnd = dateBeginUtc.AddSeconds(intervalSec); - var dbSet = db.Set(); - - var query = dbSet - .Where(d => d.IdTelemetry == telemetry.Id - && d.DateTime >= dateBeginUtc); - - if (filterByDateEnd) - query = query.Where(d => d.DateTime <= dateEnd); - - var fullDataCount = await query.CountAsync(token) - .ConfigureAwait(false); - - if (fullDataCount == 0) - return Enumerable.Empty(); - - if (fullDataCount > 1.75 * approxPointsCount) - { - var m = (int)Math.Round(1d * fullDataCount / approxPointsCount); - if (m > 1) - query = query.Where((d) => (((d.DateTime.DayOfYear * 24 + d.DateTime.Hour) * 60 + d.DateTime.Minute) * 60 + d.DateTime.Second) % m == 0); - } - - var entities = await query - .AsNoTracking() - .ToArrayAsync(token); - - var dtos = entities.Select(e => Convert(e, timezone.Hours)); - - return dtos; - } - - /// - public virtual async Task> GetByWellAsync(int idWell, TelemetryDataRequest request, CancellationToken token) - { - var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell); - if (telemetry is null) - return Enumerable.Empty(); - - return await GetByTelemetryAsync(telemetry.Id, request, token); - } - - public async Task> GetByTelemetryAsync(int idTelemetry, TelemetryDataRequest request, CancellationToken token) - { - var timezone = telemetryService.GetTimezone(idTelemetry); - - var cache = telemetryDataCache.GetOrDefault(idTelemetry, request); - - if (cache is not null) - return cache; - - var query = BuildQuery(idTelemetry, request); - - var entities = await query - .AsNoTracking() - .ToArrayAsync(token); - - var dtos = entities.Select(e => Convert(e, timezone.Hours)); - - return dtos; - } - - private IQueryable BuildQuery(int idTelemetry, TelemetryDataRequest request) - { - var dbSet = db.Set(); - - var query = dbSet - .Where(d => d.IdTelemetry == idTelemetry); - - if (request.GeDate.HasValue) - { - var geDate = request.GeDate.Value.UtcDateTime; - query = query.Where(d => d.DateTime >= geDate); - } - - if (request.LeDate.HasValue) - { - var leDate = request.LeDate.Value.UtcDateTime; - query = query.Where(d => d.DateTime <= leDate); - } - - if (request.Divider > 1) - query = query.Where((d) => (((d.DateTime.DayOfYear * 24 + d.DateTime.Hour) * 60 + d.DateTime.Minute) * 60 + d.DateTime.Second) % request.Divider == 0); - - switch (request.Order) - { - case 1:// Поздние вперед - query = query - .OrderByDescending(d => d.DateTime) - .Skip(request.Skip) - .Take(request.Take) - .OrderBy(d => d.DateTime); - break; - default:// Ранние вперед - query = query - .OrderBy(d => d.DateTime) - .Skip(request.Skip) - .Take(request.Take); - break; - } - - return query; - } - - private IQueryable BuildQuery(TelemetryPartDeleteRequest request) - { - var query = db.Set() - .Where(o => o.IdTelemetry == request.IdTelemetry); - - if (request.GeDate is not null) - { - var geDate = request.GeDate.Value.ToUniversalTime(); - query = query.Where(o => o.DateTime <= geDate); - } - - if (request.LeDate is not null) - { - var leDate = request.LeDate.Value.ToUniversalTime(); - query = query.Where(o => o.DateTime >= leDate); - } - - return query; - } - - /// - public async Task GetRangeAsync(int idWell, DateTimeOffset geDate, DateTimeOffset? leDate, CancellationToken token) - { - var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell) - ?? throw new ArgumentInvalidException(nameof(idWell), $"По скважине id:{idWell} нет телеметрии"); - - if ((DateTimeOffset.UtcNow - geDate) < TimeSpan.FromHours(12)) - { - // пробуем обойтись кэшем - var cachedRange = telemetryDataCache.GetOrDefaultCachedDataDateRange(telemetry.Id); - if (cachedRange is not null) - { - var datesRange = new DatesRangeDto { From = cachedRange.From, To = cachedRange.To }; - if (geDate >= cachedRange.From) - datesRange.From = geDate.ToOffset(cachedRange.From.Offset); - - if (leDate.HasValue && leDate <= cachedRange.To) - datesRange.To = leDate.Value.ToOffset(cachedRange.To.Offset); - - return datesRange; - } - } - - var query = db.Set() - .Where(entity => entity.IdTelemetry == telemetry.Id) - .Where(entity => entity.DateTime >= geDate.ToUniversalTime()); - - if (leDate.HasValue) - query = query.Where(entity => entity.DateTime <= leDate.Value.ToUniversalTime()); - - var groupQuery = query - .GroupBy(entity => entity.IdTelemetry) - .Select(group => new - { - MinDate = group.Min(entity => entity.DateTime), - MaxDate = group.Max(entity => entity.DateTime), - }); - - var result = await groupQuery.FirstOrDefaultAsync(token); - if (result is null) - return null; - - var range = new DatesRangeDto - { - From = result.MinDate.ToOffset(telemetry.TimeZone!.Offset), - To = result.MaxDate.ToOffset(telemetry.TimeZone!.Offset), - }; - return range; - } - - public DatesRangeDto? GetRange(int idWell) - { - var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell); - if (telemetry is null) - return default; - - return telemetryDataCache.GetOrDefaultWellDataDateRange(telemetry.Id); - } - - protected abstract TDto Convert(TEntity src, double timezoneOffset); - - protected abstract TEntity Convert(TDto src, double timezoneOffset); - - public async Task DeleteAsync(TelemetryPartDeleteRequest request, CancellationToken token) - { - var query = BuildQuery(request); - db.Set().RemoveRange(query); - return await db.SaveChangesAsync(token); + stopwatch.Stop(); + Trace.WriteLine($"Fail to save data telemetry " + + $"uid: {uid}, " + + $"idTelemetry {telemetry.Id}, " + + $"count: {entities.Count()}, " + + $"dataDate: {entities.FirstOrDefault()?.DateTime}, " + + $"dbSaveDurationTime:{stopwatch.ElapsedMilliseconds}ms. " + + $"Message: {ex.Message}"); + return 0; } } + + /// + public virtual async Task> GetByWellAsync(int idWell, + DateTime dateBegin = default, double intervalSec = 600d, + int approxPointsCount = 1024, CancellationToken token = default) + { + var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell); + if (telemetry is null) + return Enumerable.Empty(); + + var timezone = telemetryService.GetTimezone(telemetry.Id); + + var filterByDateEnd = dateBegin != default; + DateTimeOffset dateBeginUtc; + if (dateBegin == default) + { + var dateRange = telemetryDataCache.GetOrDefaultWellDataDateRange(telemetry.Id); + dateBeginUtc = (dateRange?.To.ToUniversalTime() ?? DateTimeOffset.UtcNow) + .AddSeconds(-intervalSec); + } + else + { + dateBeginUtc = dateBegin.ToUtcDateTimeOffset(timezone.Hours); + } + + var cacheData = telemetryDataCache.GetOrDefault(telemetry.Id, dateBeginUtc.ToRemoteDateTime(timezone.Hours), intervalSec, approxPointsCount); + if (cacheData is not null) + return cacheData; + + var dateEnd = dateBeginUtc.AddSeconds(intervalSec); + var dbSet = db.Set(); + + var query = dbSet + .Where(d => d.IdTelemetry == telemetry.Id + && d.DateTime >= dateBeginUtc); + + if (filterByDateEnd) + query = query.Where(d => d.DateTime <= dateEnd); + + var fullDataCount = await query.CountAsync(token) + .ConfigureAwait(false); + + if (fullDataCount == 0) + return Enumerable.Empty(); + + if (fullDataCount > 1.75 * approxPointsCount) + { + var m = (int)Math.Round(1d * fullDataCount / approxPointsCount); + if (m > 1) + query = query.Where((d) => (((d.DateTime.DayOfYear * 24 + d.DateTime.Hour) * 60 + d.DateTime.Minute) * 60 + d.DateTime.Second) % m == 0); + } + + var entities = await query + .AsNoTracking() + .ToArrayAsync(token); + + var dtos = entities.Select(e => Convert(e, timezone.Hours)); + + return dtos; + } + + /// + public virtual async Task> GetByWellAsync(int idWell, TelemetryDataRequest request, CancellationToken token) + { + var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell); + if (telemetry is null) + return Enumerable.Empty(); + + return await GetByTelemetryAsync(telemetry.Id, request, token); + } + + public async Task> GetByTelemetryAsync(int idTelemetry, TelemetryDataRequest request, CancellationToken token) + { + var timeZone = telemetryService.GetTimezone(idTelemetry); + + var cache = telemetryDataCache.GetOrDefault(idTelemetry, request); + + if (cache is not null) + return cache; + + var query = BuildQuery(idTelemetry, request); + + var entities = await query + .AsNoTracking() + .ToArrayAsync(token); + + var dtos = entities.Select(e => Convert(e, timeZone.Hours)); + + return dtos; + } + + private IQueryable BuildQuery(int idTelemetry, TelemetryDataRequest request) + { + var dbSet = db.Set(); + + var query = dbSet + .Where(d => d.IdTelemetry == idTelemetry); + + if (request.GeDate.HasValue) + { + var geDate = request.GeDate.Value.UtcDateTime; + query = query.Where(d => d.DateTime >= geDate); + } + + if (request.LeDate.HasValue) + { + var leDate = request.LeDate.Value.UtcDateTime; + query = query.Where(d => d.DateTime <= leDate); + } + + if (request.Divider > 1) + query = query.Where((d) => (((d.DateTime.DayOfYear * 24 + d.DateTime.Hour) * 60 + d.DateTime.Minute) * 60 + d.DateTime.Second) % request.Divider == 0); + + switch (request.Order) + { + case 1:// Поздние вперед + query = query + .OrderByDescending(d => d.DateTime) + .Skip(request.Skip) + .Take(request.Take) + .OrderBy(d => d.DateTime); + break; + default:// Ранние вперед + query = query + .OrderBy(d => d.DateTime) + .Skip(request.Skip) + .Take(request.Take); + break; + } + + return query; + } + + private IQueryable BuildQuery(TelemetryPartDeleteRequest request) + { + var query = db.Set() + .Where(o => o.IdTelemetry == request.IdTelemetry); + + if (request.GeDate is not null) + { + var geDate = request.GeDate.Value.ToUniversalTime(); + query = query.Where(o => o.DateTime <= geDate); + } + + if (request.LeDate is not null) + { + var leDate = request.LeDate.Value.ToUniversalTime(); + query = query.Where(o => o.DateTime >= leDate); + } + + return query; + } + + /// + public async Task GetRangeAsync(int idWell, DateTimeOffset geDate, DateTimeOffset? leDate, CancellationToken token) + { + var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell) + ?? throw new ArgumentInvalidException(nameof(idWell), $"По скважине id:{idWell} нет телеметрии"); + + if ((DateTimeOffset.UtcNow - geDate) < TimeSpan.FromHours(12)) + { + // пробуем обойтись кэшем + var cachedRange = telemetryDataCache.GetOrDefaultCachedDataDateRange(telemetry.Id); + if (cachedRange is not null) + { + var datesRange = new DatesRangeDto { From = cachedRange.From, To = cachedRange.To }; + if (geDate >= cachedRange.From) + datesRange.From = geDate.ToOffset(cachedRange.From.Offset); + + if (leDate.HasValue && leDate <= cachedRange.To) + datesRange.To = leDate.Value.ToOffset(cachedRange.To.Offset); + + return datesRange; + } + } + + var query = db.Set() + .Where(entity => entity.IdTelemetry == telemetry.Id) + .Where(entity => entity.DateTime >= geDate.ToUniversalTime()); + + if (leDate.HasValue) + query = query.Where(entity => entity.DateTime <= leDate.Value.ToUniversalTime()); + + var groupQuery = query + .GroupBy(entity => entity.IdTelemetry) + .Select(group => new + { + MinDate = group.Min(entity => entity.DateTime), + MaxDate = group.Max(entity => entity.DateTime), + }); + + var result = await groupQuery.FirstOrDefaultAsync(token); + if (result is null) + return null; + + var range = new DatesRangeDto + { + From = result.MinDate.ToOffset(telemetry.TimeZone!.Offset), + To = result.MaxDate.ToOffset(telemetry.TimeZone!.Offset), + }; + return range; + } + + public DatesRangeDto? GetRange(int idWell) + { + var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell); + if (telemetry is null) + return default; + + return telemetryDataCache.GetOrDefaultWellDataDateRange(telemetry.Id); + } + + protected abstract TDto Convert(TEntity src, double timeZoneOffset); + + protected abstract TEntity Convert(TDto src, double timeZoneOffset); + + public async Task DeleteAsync(TelemetryPartDeleteRequest request, CancellationToken token) + { + var query = BuildQuery(request); + db.Set().RemoveRange(query); + return await db.SaveChangesAsync(token); + } } diff --git a/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs b/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs index 5011c8d3..d4171f25 100644 --- a/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs +++ b/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs @@ -111,7 +111,7 @@ namespace AsbCloudWebApi.Controllers.SAUB } /// - /// Получить список автоопределенных операций для редактирования + /// Получить список авто определенных операций для редактирования /// /// /// @@ -136,7 +136,7 @@ namespace AsbCloudWebApi.Controllers.SAUB } /// - /// Получить статистику по автоопределенным операциям + /// Получить статистику по авто определенным операциям /// /// ///