DD.WellWorkover.Cloud/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorAbstract.cs
ngfrolov 499f7cc4e2 Edit Detectors for rotor and slide.
Add reason of end detecting operation.
2022-08-09 18:00:22 +05:00

335 lines
13 KiB
C#

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<DetectableTelemetry, double> 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;
}
/// <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;
}
}
#nullable disable
}