forked from ddrilling/AsbCloudServer
spelling
This commit is contained in:
parent
b6b1220561
commit
fad3688640
@ -54,7 +54,7 @@ public interface IDetectedOperationRepository : ITelemetryDataEditorService
|
||||
Task<PaginationContainer<DetectedOperationDto>> GetPageAsync(DetectedOperationByTelemetryRequest request, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получение последних автоопределённых операций
|
||||
/// Получение последних авто определённых операций
|
||||
/// </summary>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
|
@ -6,86 +6,85 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AsbCloudApp.Data.WellOperation;
|
||||
|
||||
namespace AsbCloudApp.Services
|
||||
namespace AsbCloudApp.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Сервис автоматически определенных по телеметрии операций
|
||||
/// </summary>
|
||||
public interface IDetectedOperationService
|
||||
{
|
||||
/// <summary>
|
||||
/// Сервис автоматически определенных по телеметрии операций
|
||||
/// Добавление операций
|
||||
/// </summary>
|
||||
public interface IDetectedOperationService
|
||||
{
|
||||
/// <summary>
|
||||
/// Добавление операций
|
||||
/// </summary>
|
||||
/// <param name="idEditor"></param>
|
||||
/// <param name="idWell"></param>
|
||||
/// <param name="dtos"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> InsertRangeManualAsync(int idEditor, int idWell, IEnumerable<DetectedOperationDto> dtos, CancellationToken token);
|
||||
/// <param name="idEditor"></param>
|
||||
/// <param name="idWell"></param>
|
||||
/// <param name="dtos"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> InsertRangeManualAsync(int idEditor, int idWell, IEnumerable<DetectedOperationDto> dtos, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Редактирование операций
|
||||
/// </summary>
|
||||
/// <param name="idEditor"></param>
|
||||
/// <param name="idWell"></param>
|
||||
/// <param name="dtos"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> UpdateRangeManualAsync(int idEditor, int idWell, IEnumerable<DetectedOperationDto> dtos, CancellationToken token);
|
||||
/// <summary>
|
||||
/// Редактирование операций
|
||||
/// </summary>
|
||||
/// <param name="idEditor"></param>
|
||||
/// <param name="idWell"></param>
|
||||
/// <param name="dtos"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> UpdateRangeManualAsync(int idEditor, int idWell, IEnumerable<DetectedOperationDto> dtos, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Список названий операций.
|
||||
/// Если указан idWell, то возвращается список названий операций найденных на указанной скважине.
|
||||
/// </summary>
|
||||
/// <param name="idWell"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<WellOperationCategoryDto>> GetCategoriesAsync(int? idWell, CancellationToken token);
|
||||
/// <summary>
|
||||
/// Список названий операций.
|
||||
/// Если указан idWell, то возвращается список названий операций найденных на указанной скважине.
|
||||
/// </summary>
|
||||
/// <param name="idWell"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<WellOperationCategoryDto>> GetCategoriesAsync(int? idWell, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получить автоматически определенные по телеметрии операции с анализом по бурильщикам
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<DetectedOperationListDto> GetAsync(DetectedOperationByWellRequest request, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получить автоматически определенные по телеметрии операции
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<DetectedOperationWithDrillerDto>> GetOperationsAsync(DetectedOperationByWellRequest request, CancellationToken token);
|
||||
/// <summary>
|
||||
/// Получить автоматически определенные по телеметрии операции с анализом по бурильщикам
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<DetectedOperationListDto> GetAsync(DetectedOperationByWellRequest request, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получить автоматически определенные по телеметрии операции
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<DetectedOperationWithDrillerDto>> GetOperationsAsync(DetectedOperationByWellRequest request, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Удалить операции
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> DeleteAsync(DetectedOperationByWellRequest request, CancellationToken token);
|
||||
/// <summary>
|
||||
/// Удалить операции
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> DeleteAsync(DetectedOperationByWellRequest request, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Статистика по операциям
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete]
|
||||
Task<IEnumerable<DetectedOperationStatDto>> GetOperationsStatAsync(DetectedOperationByWellRequest request, CancellationToken token);
|
||||
/// <summary>
|
||||
/// Статистика по операциям
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete]
|
||||
Task<IEnumerable<DetectedOperationStatDto>> GetOperationsStatAsync(DetectedOperationByWellRequest request, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Определение операций
|
||||
/// </summary>
|
||||
/// <param name="idTelemetry"></param>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="lastDetectedOperation"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<(DateTimeOffset LastDate, IEnumerable<DetectedOperationDto> Items)> DetectOperationsAsync(int idTelemetry,
|
||||
TelemetryDataRequest request,
|
||||
DetectedOperationDto? lastDetectedOperation,
|
||||
CancellationToken token);
|
||||
}
|
||||
/// <summary>
|
||||
/// Определение операций
|
||||
/// </summary>
|
||||
/// <param name="idTelemetry"></param>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="lastDetectedOperation"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<(DateTimeOffset LastDate, IEnumerable<DetectedOperationDto> Items)> DetectOperationsAsync(int idTelemetry,
|
||||
TelemetryDataRequest request,
|
||||
DetectedOperationDto? lastDetectedOperation,
|
||||
CancellationToken token);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Телеметрия САУБ
|
||||
/// </summary>
|
||||
public interface ITelemetryDataSaubService : ITelemetryDataService<TelemetryDataSaubDto>
|
||||
{
|
||||
/// <summary>
|
||||
/// Телеметрия САУБ
|
||||
/// Получение телеметрии для РТК статистики
|
||||
/// </summary>
|
||||
public interface ITelemetryDataSaubService : ITelemetryDataService<TelemetryDataSaubDto>
|
||||
{
|
||||
/// <summary>
|
||||
/// Получение телеметрии для РТК статистики
|
||||
/// </summary>
|
||||
/// <param name="idTelemetry"></param>
|
||||
/// <param name="isBitOnBottom"></param>
|
||||
/// <param name="geDate"></param>
|
||||
/// <param name="leDate"></param>
|
||||
/// <param name="take"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<TelemetryDataSaubDto>> Get(int idTelemetry, bool isBitOnBottom, DateTimeOffset geDate, DateTimeOffset leDate, int take, CancellationToken token);
|
||||
/// <param name="idTelemetry"></param>
|
||||
/// <param name="isBitOnBottom"></param>
|
||||
/// <param name="geDate"></param>
|
||||
/// <param name="leDate"></param>
|
||||
/// <param name="take"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<TelemetryDataSaubDto>> Get(int idTelemetry, bool isBitOnBottom, DateTimeOffset geDate, DateTimeOffset leDate, int take, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// усредненная статистика по 1м за весь период
|
||||
/// <para>
|
||||
/// МЕДЛЕННЫЙ ЗАПРОС
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="idTelemetry"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<TelemetryDataSaubStatDto>> GetTelemetryDataStatAsync(int idTelemetry, CancellationToken token);
|
||||
/// <summary>
|
||||
/// усредненная статистика по 1м за весь период
|
||||
/// <para>
|
||||
/// МЕДЛЕННЫЙ ЗАПРОС
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="idTelemetry"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<TelemetryDataSaubStatDto>> GetTelemetryDataStatAsync(int idTelemetry, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получить упакованый csv файл
|
||||
/// </summary>
|
||||
/// <param name="idWell"></param>
|
||||
/// <param name="beginDate"></param>
|
||||
/// <param name="endDate"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<Stream> GetZippedCsv(int idWell, DateTime beginDate, DateTime endDate, CancellationToken token);
|
||||
}
|
||||
/// <summary>
|
||||
/// Получить упакованный csv файл
|
||||
/// </summary>
|
||||
/// <param name="idWell"></param>
|
||||
/// <param name="beginDate"></param>
|
||||
/// <param name="endDate"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<Stream> GetZippedCsv(int idWell, DateTime beginDate, DateTime endDate, CancellationToken token);
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
using AsbCloudDb.Model;
|
||||
using ClosedXML.Excel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
@ -110,7 +110,7 @@ public class DetectedOperationService : IDetectedOperationService
|
||||
private async Task<int> 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<DetectedOperationDto>());
|
||||
}
|
||||
|
||||
@ -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<DetectedOperationDrillersStatDto> GetOperationsDrillersStat(IEnumerable<DetectedOperationWithDrillerDto> operations)
|
||||
@ -323,7 +325,6 @@ public class DetectedOperationService : IDetectedOperationService
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private static DetectedOperationWithDrillerDto Convert(DetectedOperationDto operation, IEnumerable<OperationValueDto> operationValues, IEnumerable<ScheduleDto> schedules)
|
||||
{
|
||||
var dto = operation.Adapt<DetectedOperationWithDrillerDto>();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получение информации специфичной для конкретного детектора
|
||||
/// IdCategory - одна из констант WellOperationCategory
|
||||
/// ExtraData - дополнительная информация для отладки алгоритмов авто определения
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected abstract (int IdCategory, IDictionary<string, object> ExtraData) GetSpecificInformation(DetectableTelemetry[] telemetry, int begin, int end);
|
||||
|
||||
/// <summary>
|
||||
/// Расчет ключевого параметра операции
|
||||
/// </summary>
|
||||
/// <param name="telemetry"></param>
|
||||
/// <param name="begin"></param>
|
||||
/// <param name="end"></param>
|
||||
/// <returns></returns>
|
||||
protected abstract double CalcValue(DetectableTelemetry[] telemetry, int begin, int end);
|
||||
|
||||
/// <summary>
|
||||
/// Определение включенных подсистем во время выполнения операции
|
||||
/// </summary>
|
||||
/// <param name="telemetry"></param>
|
||||
/// <param name="begin"></param>
|
||||
/// <param name="end"></param>
|
||||
/// <param name="extraData"></param>
|
||||
/// <returns></returns>
|
||||
private static int DetectEnabledSubsystems(DetectableTelemetry[] telemetry, int begin, int end, IDictionary<string, object> 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);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получение информации специфичной для конкретного детектора
|
||||
/// IdCategory - одна из констант WellOperationCategory
|
||||
/// ExtraData - дополнительная информация для отладки алгоритмов авто определения
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected abstract (int IdCategory, IDictionary<string, object> ExtraData) GetSpecificInformation(DetectableTelemetry[] telemetry, int begin, int end);
|
||||
|
||||
/// <summary>
|
||||
/// Расчет ключевого параметра операции
|
||||
/// </summary>
|
||||
/// <param name="telemetry"></param>
|
||||
/// <param name="begin"></param>
|
||||
/// <param name="end"></param>
|
||||
/// <returns></returns>
|
||||
protected abstract double CalcValue(DetectableTelemetry[] telemetry, int begin, int end);
|
||||
|
||||
/// <summary>
|
||||
/// Определение включенных подсистем во время выполнения операции
|
||||
/// </summary>
|
||||
/// <param name="telemetry"></param>
|
||||
/// <param name="begin"></param>
|
||||
/// <param name="end"></param>
|
||||
/// <param name="extraData"></param>
|
||||
/// <returns></returns>
|
||||
private static int DetectEnabledSubsystems(DetectableTelemetry[] telemetry, int begin, int end, IDictionary<string, object> 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;
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
/// <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;
|
||||
}
|
||||
|
||||
/// <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 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<DetectableTelemetry, double> 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;
|
||||
}
|
||||
|
||||
/// <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 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<DetectableTelemetry, double> getter,
|
||||
int begin,
|
||||
int count,
|
||||
double deviation)
|
||||
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 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;
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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<TDto, TEntity> : ITelemetryDataService<TDto>
|
||||
where TDto : AsbCloudApp.Data.ITelemetryData
|
||||
where TEntity : class, AsbCloudDb.Model.ITelemetryData
|
||||
{
|
||||
public abstract class TelemetryDataBaseService<TDto, TEntity> : ITelemetryDataService<TDto>
|
||||
where TDto : AsbCloudApp.Data.ITelemetryData
|
||||
where TEntity : class, AsbCloudDb.Model.ITelemetryData
|
||||
protected readonly IAsbCloudDbContext db;
|
||||
protected readonly ITelemetryService telemetryService;
|
||||
protected readonly ITelemetryDataCache<TDto> telemetryDataCache;
|
||||
|
||||
protected TelemetryDataBaseService(
|
||||
IAsbCloudDbContext db,
|
||||
ITelemetryService telemetryService,
|
||||
ITelemetryDataCache<TDto> telemetryDataCache)
|
||||
{
|
||||
protected readonly IAsbCloudDbContext db;
|
||||
protected readonly ITelemetryService telemetryService;
|
||||
protected readonly ITelemetryDataCache<TDto> telemetryDataCache;
|
||||
this.db = db;
|
||||
this.telemetryService = telemetryService;
|
||||
this.telemetryDataCache = telemetryDataCache;
|
||||
}
|
||||
|
||||
protected TelemetryDataBaseService(
|
||||
IAsbCloudDbContext db,
|
||||
ITelemetryService telemetryService,
|
||||
ITelemetryDataCache<TDto> telemetryDataCache)
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<int> UpdateDataAsync(string uid, IEnumerable<TDto> 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<TDto>(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);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<int> UpdateDataAsync(string uid, IEnumerable<TDto> 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<TDto>(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<TEntity>();
|
||||
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<TEntity>();
|
||||
try
|
||||
{
|
||||
return await db.Database.ExecInsertOrUpdateAsync(dbSet, entities, token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<IEnumerable<TDto>> 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<TDto>();
|
||||
|
||||
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<TEntity>();
|
||||
|
||||
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<TDto>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<IEnumerable<TDto>> GetByWellAsync(int idWell, TelemetryDataRequest request, CancellationToken token)
|
||||
{
|
||||
var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell);
|
||||
if (telemetry is null)
|
||||
return Enumerable.Empty<TDto>();
|
||||
|
||||
return await GetByTelemetryAsync(telemetry.Id, request, token);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TDto>> 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<TEntity> BuildQuery(int idTelemetry, TelemetryDataRequest request)
|
||||
{
|
||||
var dbSet = db.Set<TEntity>();
|
||||
|
||||
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<TEntity> BuildQuery(TelemetryPartDeleteRequest request)
|
||||
{
|
||||
var query = db.Set<TEntity>()
|
||||
.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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<DatesRangeDto?> 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<TEntity>()
|
||||
.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<int> DeleteAsync(TelemetryPartDeleteRequest request, CancellationToken token)
|
||||
{
|
||||
var query = BuildQuery(request);
|
||||
db.Set<TEntity>().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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<IEnumerable<TDto>> 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<TDto>();
|
||||
|
||||
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<TEntity>();
|
||||
|
||||
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<TDto>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<IEnumerable<TDto>> GetByWellAsync(int idWell, TelemetryDataRequest request, CancellationToken token)
|
||||
{
|
||||
var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell);
|
||||
if (telemetry is null)
|
||||
return Enumerable.Empty<TDto>();
|
||||
|
||||
return await GetByTelemetryAsync(telemetry.Id, request, token);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TDto>> 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<TEntity> BuildQuery(int idTelemetry, TelemetryDataRequest request)
|
||||
{
|
||||
var dbSet = db.Set<TEntity>();
|
||||
|
||||
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<TEntity> BuildQuery(TelemetryPartDeleteRequest request)
|
||||
{
|
||||
var query = db.Set<TEntity>()
|
||||
.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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<DatesRangeDto?> 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<TEntity>()
|
||||
.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<int> DeleteAsync(TelemetryPartDeleteRequest request, CancellationToken token)
|
||||
{
|
||||
var query = BuildQuery(request);
|
||||
db.Set<TEntity>().RemoveRange(query);
|
||||
return await db.SaveChangesAsync(token);
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ namespace AsbCloudWebApi.Controllers.SAUB
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить список автоопределенных операций для редактирования
|
||||
/// Получить список авто определенных операций для редактирования
|
||||
/// </summary>
|
||||
/// <param name="idWell"></param>
|
||||
/// <param name="request"></param>
|
||||
@ -136,7 +136,7 @@ namespace AsbCloudWebApi.Controllers.SAUB
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить статистику по автоопределенным операциям
|
||||
/// Получить статистику по авто определенным операциям
|
||||
/// </summary>
|
||||
/// <param name="idWell"></param>
|
||||
/// <param name="request"></param>
|
||||
|
Loading…
Reference in New Issue
Block a user