using AsbCloudDb.Model; using System; using System.Collections.Generic; namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors { public abstract class DetectorAbstract { private readonly int stepLength = 3; 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_Custom1 = 10_000; public bool TryDetect(int idTelemetry, DetectableTelemetry[] telemetry, int begin, int end, DetectedOperation? previousOperation, out OperationDetectorResult? result) { // Проверка соответствия критерию начала операции if (DetectBegin(telemetry, begin, previousOperation)) { // Поиск окончания соответствия критерию int idReasonOfEnd = 0; var positionEnd = begin; while (positionEnd < end) { positionEnd += stepLength; if (positionEnd > end) break; idReasonOfEnd = DetectEnd(telemetry, positionEnd, previousOperation); if (idReasonOfEnd != IdReasonOfEnd_NotDetected) break; } 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 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); protected virtual int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? 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 DetectedOperation 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 DetectedOperation { IdCategory = IdCategory, IdTelemetry = idTelemetry, IdUsersAtStart = 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)DetectedOperation.EnabledSubsystemsFlags.AutoSpin; return enabledSubsystems; } for (var i = begin; i < end; i += 2) { var mode = telemetry[i].Mode; if(mode == 1) enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoRotor; if (mode == 3) enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoSlide; if (mode == 2) enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoConditionig; if (mode == 4) enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoSinking; if (mode == 5) enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoLifting; if (mode == 6) enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoLiftingWithConditionig; if (mode == 10) enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoBlocknig; } return enabledSubsystems; } /// /// расчет продолжительности операции /// /// /// /// /// 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; 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 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; } 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; } } }