forked from ddrilling/AsbCloudServer
8bbaca0d0c
1. Поправел excel шаблон 2. Доработаны алгоритмы определения операций бурения 3. Небольшой рефакторинг DetectorAbstract, добавил метод для валидации 4. Закомментированы неиспользуемые детекторы. 5. Обновлена спецификация определения операций бурения 6. Добавлены тесты для определения операций бурения
357 lines
14 KiB
C#
357 lines
14 KiB
C#
using AsbCloudDb.Model;
|
|
using System;
|
|
using System.Linq;
|
|
|
|
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;
|
|
|
|
protected abstract Func<DetectableTelemetry[], int, int, int> GetIdOperation { get; }
|
|
|
|
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;
|
|
}
|
|
|
|
result = MakeOperation(idTelemetry, telemetry, begin, positionEnd, idReasonOfEnd);
|
|
return result is not null;
|
|
}
|
|
|
|
result = null;
|
|
return false;
|
|
}
|
|
|
|
protected virtual bool IsValidOperationDetectorResult(OperationDetectorResult operationDetectorResult) =>
|
|
operationDetectorResult.Operation.IdReasonOfEnd != IdReasonOfEnd_NotDetected;
|
|
|
|
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 = GetIdOperation.Invoke(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,
|
|
},
|
|
};
|
|
|
|
return !IsValidOperationDetectorResult(result) ? null : result;
|
|
}
|
|
|
|
protected abstract double CalcValue(DetectableTelemetry[] telemetry, int begin, int end);
|
|
|
|
/// <summary>
|
|
/// Среднее арифметическое
|
|
/// </summary>
|
|
/// <param name="yGetter"></param>
|
|
/// <param name="telemetry"></param>
|
|
/// <param name="begin"></param>
|
|
/// <param name="fragmentLength"></param>
|
|
/// <returns></returns>
|
|
public static double CalcAvgAppr(
|
|
Func<DetectableTelemetry, double> yGetter,
|
|
DetectableTelemetry[] telemetry,
|
|
int begin,
|
|
int fragmentLength)
|
|
{
|
|
var end = begin + fragmentLength;
|
|
end = end < telemetry.Length
|
|
? end
|
|
: telemetry.Length;
|
|
var subData = telemetry[begin..end].Select(yGetter);
|
|
if (end - begin > 10)
|
|
{
|
|
var ratio = (end - begin) / 5;
|
|
subData = subData.Where((_, i) => i % ratio > 0);
|
|
}
|
|
|
|
var avg = subData.Average();
|
|
return avg;
|
|
}
|
|
|
|
/// <summary>
|
|
/// расчет продолжительности операции
|
|
/// </summary>
|
|
/// <param name="telemetry"></param>
|
|
/// <param name="begin"></param>
|
|
/// <param name="end"></param>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// часто используемый предикат для определения отсутствия изменения глубины ствола скважины
|
|
/// </summary>
|
|
/// <param name="telemetry"></param>
|
|
/// <param name="begin"></param>
|
|
/// <param name="end"></param>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Расчет статистики по массиву данных за интервал
|
|
/// </summary>
|
|
/// <param name="telemetry"></param>
|
|
/// <param name="getter"></param>
|
|
/// <param name="begin"></param>
|
|
/// <param name="count"></param>
|
|
/// <returns></returns>
|
|
protected static (double min, double max, double sum, int count) CalcStat(
|
|
DetectableTelemetry[] telemetry,
|
|
Func<DetectableTelemetry, double> 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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Максимальное отклонение от среднего за интервал
|
|
/// </summary>
|
|
/// <param name="telemetry"></param>
|
|
/// <param name="getter"></param>
|
|
/// <param name="begin"></param>
|
|
/// <param name="count"></param>
|
|
/// <returns></returns>
|
|
protected static double CalcMaxDeviation(
|
|
DetectableTelemetry[] telemetry,
|
|
Func<DetectableTelemetry, double> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Определяет наличие разброса значений в интервале большего указанного значения.
|
|
/// </summary>
|
|
/// <param name="telemetry"></param>
|
|
/// <param name="getter"></param>
|
|
/// <param name="begin"></param>
|
|
/// <param name="count"></param>
|
|
/// <param name="deviation"></param>
|
|
/// <returns></returns>
|
|
protected static bool ContainsDeviation(
|
|
DetectableTelemetry[] telemetry,
|
|
Func<DetectableTelemetry, double> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Определяет наличие разброса значений в интервале большего указанного значения. По нескольким значениям из интервала.
|
|
/// </summary>
|
|
/// <param name="telemetry"></param>
|
|
/// <param name="getter"></param>
|
|
/// <param name="begin"></param>
|
|
/// <param name="count"></param>
|
|
/// <param name="deviation"></param>
|
|
/// <returns></returns>
|
|
protected static bool ContainsDeviationApprox(
|
|
DetectableTelemetry[] telemetry,
|
|
Func<DetectableTelemetry, double> 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<DetectableTelemetry, double> 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<DetectableTelemetry, double> 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;
|
|
}
|
|
}
|
|
}
|
|
|