using AsbCloudDb.Model; using System; using System.Linq; namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors { #nullable enable internal abstract class DetectorAbstract { private readonly int idOperation; 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_Custom1 = 10_000; protected DetectorAbstract(int idOperation) { this.idOperation = idOperation; } public bool TryDetect(int idTelemetry, DetectableTelemetry[] telemetry, int begin, int end, DetectedOperation? previousOperation, out OperationDetectorResult? result) { // Проверка соответствия критерию начала операции if (DetectBegin(telemetry, begin, previousOperation)) { // Поиск окончания соответствия критерию var positionEnd = begin; while (positionEnd < end) { positionEnd += stepLength; if ((positionEnd > end)) break; var idReasonOfEnd = DetectEnd(telemetry, positionEnd, previousOperation); if (idReasonOfEnd != IdReasonOfEnd_NotDetected) { result = MakeOperation(idTelemetry, telemetry, begin, positionEnd, idReasonOfEnd); return true; } } } result = null; return false; } 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 MakeOperation(int idTelemetry, DetectableTelemetry[] telemetry, int begin, int end, int idReasonOfEnd) { var pBegin = telemetry[begin]; var pEnd = telemetry[end]; var result = new OperationDetectorResult { TelemetryBegin = begin, TelemetryEnd = end, Operation = new DetectedOperation { IdTelemetry = idTelemetry, IdCategory = idOperation, 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, }, }; return result; } protected abstract bool IsValid(DetectableTelemetry[] telemetry, int begin, int end); protected abstract double CalcValue(DetectableTelemetry[] telemetry, int begin, int end); public static InterpolationLine MakeInterpolationLine( Func yGetter, DetectableTelemetry[] telemetry, int begin, int fragmentLength) { DetectableTelemetry[] data; if (fragmentLength > 0 && (begin + fragmentLength) < telemetry.Length) data = telemetry[begin..(begin + fragmentLength)]; else data = telemetry[begin..]; var line = new InterpolationLine(data.Select(d => (yGetter(d), (d.DateTime - telemetry[0].DateTime).TotalHours))); return line; } /// /// расчет продолжительности операции /// /// /// /// /// 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; } } #nullable disable }