Merge pull request 'Автоопределение операций' (#292) from feature/detected_operations into dev

Reviewed-on: https://test.digitaldrilling.ru:8443/DDrilling/AsbCloudServer/pulls/292
This commit is contained in:
Никита Фролов 2024-07-26 17:03:32 +05:00
commit b5b313d459
16 changed files with 1130 additions and 1058 deletions

View File

@ -1,11 +1,10 @@
using AsbCloudApp.Data; using AsbCloudApp.Data;
using AsbCloudApp.Data.DetectedOperation; using AsbCloudApp.Data.DetectedOperation;
using AsbCloudApp.Requests; using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsbCloudApp.Services;
namespace AsbCloudApp.Repositories; namespace AsbCloudApp.Repositories;
@ -55,11 +54,11 @@ public interface IDetectedOperationRepository : ITelemetryDataEditorService
Task<PaginationContainer<DetectedOperationDto>> GetPageAsync(DetectedOperationByTelemetryRequest request, CancellationToken token); Task<PaginationContainer<DetectedOperationDto>> GetPageAsync(DetectedOperationByTelemetryRequest request, CancellationToken token);
/// <summary> /// <summary>
/// Получение дат последних определённых операций /// Получение последних авто определённых операций
/// </summary> /// </summary>
/// <param name="token"></param> /// <param name="token"></param>
/// <returns></returns> /// <returns></returns>
Task<IDictionary<int, DateTimeOffset>> GetLastDetectedDatesAsync(CancellationToken token); Task<IDictionary<int, DetectedOperationDto>> GetLastDetectedOperationsAsync(CancellationToken token);
/// <summary> /// <summary>
/// Удалить операции /// Удалить операции

View File

@ -55,5 +55,4 @@ public class TelemetryDataRequest
/// </summary> /// </summary>
[Range(1, MaxTake)] [Range(1, MaxTake)]
public int Take { get; set; } = 1024; public int Take { get; set; } = 1024;
} }

View File

@ -6,13 +6,13 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsbCloudApp.Data.WellOperation; using AsbCloudApp.Data.WellOperation;
namespace AsbCloudApp.Services namespace AsbCloudApp.Services;
/// <summary>
/// Сервис автоматически определенных по телеметрии операций
/// </summary>
public interface IDetectedOperationService
{ {
/// <summary>
/// Сервис автоматически определенных по телеметрии операций
/// </summary>
public interface IDetectedOperationService
{
/// <summary> /// <summary>
/// Добавление операций /// Добавление операций
/// </summary> /// </summary>
@ -79,9 +79,12 @@ namespace AsbCloudApp.Services
/// Определение операций /// Определение операций
/// </summary> /// </summary>
/// <param name="idTelemetry"></param> /// <param name="idTelemetry"></param>
/// <param name="beginDate"></param> /// <param name="request"></param>
/// <param name="lastDetectedOperation"></param>
/// <param name="token"></param> /// <param name="token"></param>
/// <returns></returns> /// <returns></returns>
Task<IEnumerable<DetectedOperationDto>> DetectOperationsAsync(int idTelemetry, DateTimeOffset? beginDate, CancellationToken token); Task<(DateTimeOffset LastDate, IEnumerable<DetectedOperationDto> Items)> DetectOperationsAsync(int idTelemetry,
} TelemetryDataRequest request,
DetectedOperationDto? lastDetectedOperation,
CancellationToken token);
} }

View File

@ -5,13 +5,13 @@ using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace AsbCloudApp.Services namespace AsbCloudApp.Services;
/// <summary>
/// Телеметрия САУБ
/// </summary>
public interface ITelemetryDataSaubService : ITelemetryDataService<TelemetryDataSaubDto>
{ {
/// <summary>
/// Телеметрия САУБ
/// </summary>
public interface ITelemetryDataSaubService : ITelemetryDataService<TelemetryDataSaubDto>
{
/// <summary> /// <summary>
/// Получение телеметрии для РТК статистики /// Получение телеметрии для РТК статистики
/// </summary> /// </summary>
@ -36,7 +36,7 @@ namespace AsbCloudApp.Services
Task<IEnumerable<TelemetryDataSaubStatDto>> GetTelemetryDataStatAsync(int idTelemetry, CancellationToken token); Task<IEnumerable<TelemetryDataSaubStatDto>> GetTelemetryDataStatAsync(int idTelemetry, CancellationToken token);
/// <summary> /// <summary>
/// Получить упакованый csv файл /// Получить упакованный csv файл
/// </summary> /// </summary>
/// <param name="idWell"></param> /// <param name="idWell"></param>
/// <param name="beginDate"></param> /// <param name="beginDate"></param>
@ -44,5 +44,4 @@ namespace AsbCloudApp.Services
/// <param name="token"></param> /// <param name="token"></param>
/// <returns></returns> /// <returns></returns>
Task<Stream> GetZippedCsv(int idWell, DateTime beginDate, DateTime endDate, CancellationToken token); Task<Stream> GetZippedCsv(int idWell, DateTime beginDate, DateTime endDate, CancellationToken token);
}
} }

View File

@ -27,6 +27,23 @@ public class DetectedOperationRepository
this.telemetryService = telemetryService; this.telemetryService = telemetryService;
} }
public async Task<IDictionary<int, DetectedOperationDto>> GetLastDetectedOperationsAsync(CancellationToken token)
{
var entities = await dbContext.Set<DetectedOperation>()
.GroupBy(o => o.IdTelemetry)
.Select(g => new
{
IdTelemetry = g.Key,
LastDetectedOperation = g.OrderBy(o => o.DateEnd).Last()
})
.ToArrayAsync(token);
var dtos = entities.ToDictionary(x => x.IdTelemetry,
x => Convert(x.LastDetectedOperation));
return dtos;
}
public async Task<int> DeleteAsync(DetectedOperationByTelemetryRequest request, CancellationToken token) public async Task<int> DeleteAsync(DetectedOperationByTelemetryRequest request, CancellationToken token)
{ {
var query = BuildQuery(request); var query = BuildQuery(request);
@ -66,16 +83,6 @@ public class DetectedOperationRepository
return paginationContainer; return paginationContainer;
} }
public async Task<IDictionary<int, DateTimeOffset>> GetLastDetectedDatesAsync(CancellationToken token) =>
await dbContext.Set<DetectedOperation>()
.GroupBy(o => o.IdTelemetry)
.Select(g => new
{
IdTelemetry = g.Key,
LastDate = g.Max(o => o.DateEnd)
})
.ToDictionaryAsync(x => x.IdTelemetry, x => x.LastDate, token);
public async Task<IEnumerable<DetectedOperationDto>> Get(DetectedOperationByTelemetryRequest request, CancellationToken token) public async Task<IEnumerable<DetectedOperationDto>> Get(DetectedOperationByTelemetryRequest request, CancellationToken token)
{ {
var query = BuildQuery(request) var query = BuildQuery(request)

View File

@ -1,6 +1,5 @@
using AsbCloudDb.Model; using AsbCloudDb.Model;
using ClosedXML.Excel; using ClosedXML.Excel;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -14,6 +13,7 @@ using AsbCloudApp.Exceptions;
using AsbCloudApp.Services; using AsbCloudApp.Services;
using AsbCloudApp.Data; using AsbCloudApp.Data;
using AsbCloudApp.Data.WellOperation; using AsbCloudApp.Data.WellOperation;
using AsbCloudApp.Requests;
using AsbCloudInfrastructure.Services.DetectOperations.Detectors; using AsbCloudInfrastructure.Services.DetectOperations.Detectors;
namespace AsbCloudInfrastructure.Services.DetectOperations; namespace AsbCloudInfrastructure.Services.DetectOperations;
@ -22,7 +22,7 @@ public class DetectedOperationExportService
{ {
private readonly IWellService wellService; private readonly IWellService wellService;
private readonly IWellOperationCategoryRepository wellOperationCategoryRepository; private readonly IWellOperationCategoryRepository wellOperationCategoryRepository;
private readonly IDetectedOperationService detectedOperationService; private readonly IDetectedOperationRepository detectedOperationRepository;
private const int headerRowsCount = 1; private const int headerRowsCount = 1;
private const string cellDepositName = "B1"; private const string cellDepositName = "B1";
@ -43,11 +43,11 @@ public class DetectedOperationExportService
public DetectedOperationExportService(IWellService wellService, public DetectedOperationExportService(IWellService wellService,
IWellOperationCategoryRepository wellOperationCategoryRepository, IWellOperationCategoryRepository wellOperationCategoryRepository,
IDetectedOperationService detectedOperationService) IDetectedOperationRepository detectedOperationRepository)
{ {
this.wellService = wellService; this.wellService = wellService;
this.wellOperationCategoryRepository = wellOperationCategoryRepository; this.wellOperationCategoryRepository = wellOperationCategoryRepository;
this.detectedOperationService = detectedOperationService; this.detectedOperationRepository = detectedOperationRepository;
} }
/// <summary> /// <summary>
@ -68,12 +68,19 @@ public class DetectedOperationExportService
if (!well.IdTelemetry.HasValue) if (!well.IdTelemetry.HasValue)
throw new ArgumentInvalidException(nameof(idWell), $"Well {idWell} has no telemetry"); throw new ArgumentInvalidException(nameof(idWell), $"Well {idWell} has no telemetry");
var operations = await detectedOperationService.DetectOperationsAsync(well.IdTelemetry.Value, DateTime.UnixEpoch, token); var request = new DetectedOperationByTelemetryRequest
{
IdTelemetry = well.IdTelemetry.Value
};
var operations = await detectedOperationRepository.Get(request, token);
return await GenerateExcelFileStreamAsync(well, host, operations, token); return await GenerateExcelFileStreamAsync(well, host, operations, token);
} }
private async Task<Stream> GenerateExcelFileStreamAsync(WellDto well, string host, IEnumerable<DetectedOperationDto> operationDetectorResults, private async Task<Stream> GenerateExcelFileStreamAsync(WellDto well,
string host,
IEnumerable<DetectedOperationDto> operationDetectorResults,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
using var excelTemplateStream = await GetExcelTemplateStreamAsync(cancellationToken); using var excelTemplateStream = await GetExcelTemplateStreamAsync(cancellationToken);
@ -167,14 +174,15 @@ public class DetectedOperationExportService
var category = wellOperationCategories.FirstOrDefault(o => o.Id == current.IdCategory); var category = wellOperationCategories.FirstOrDefault(o => o.Id == current.IdCategory);
if(category is not null) if (category is not null)
return category.Name; return category.Name;
return $"Операция №{idCategory}"; return $"Операция №{idCategory}";
} }
private static string GetIdReasonOfEnd(int idReasonOfEnd) private static string GetIdReasonOfEnd(int idReasonOfEnd)
=> idReasonOfEnd switch { => idReasonOfEnd switch
{
0 => "Не определена", 0 => "Не определена",
1 => "Не определено начало операции", 1 => "Не определено начало операции",
101 => "Разница глубин забоя и положением долота", 101 => "Разница глубин забоя и положением долота",
@ -182,7 +190,6 @@ public class DetectedOperationExportService
301 => "Высокое давление", 301 => "Высокое давление",
700 => "Изменение глубины долота и осевая нагрузка < веса на крюке", 700 => "Изменение глубины долота и осевая нагрузка < веса на крюке",
_ => idReasonOfEnd.ToString($"Причина № {idReasonOfEnd}"), _ => idReasonOfEnd.ToString($"Причина № {idReasonOfEnd}"),
}; };
private async Task<Stream> GetExcelTemplateStreamAsync(CancellationToken cancellationToken) private async Task<Stream> GetExcelTemplateStreamAsync(CancellationToken cancellationToken)
@ -211,7 +218,8 @@ public class DetectedOperationExportService
if (operation.ExtraData.TryGetValue(DetectorDrilling.ExtraDataKeyAvgRotorSpeed, out object? oAvgRotorSpeed)) if (operation.ExtraData.TryGetValue(DetectorDrilling.ExtraDataKeyAvgRotorSpeed, out object? oAvgRotorSpeed))
comment += $"Средняя скорость оборотов ротора: {oAvgRotorSpeed}\r\n"; comment += $"Средняя скорость оборотов ротора: {oAvgRotorSpeed}\r\n";
if (operation.ExtraData.TryGetValue(DetectorDrilling.ExtraDataKeyDispersionOfNormalizedRotorSpeed, out object? oDispersionOfNormalizedRotorSpeed)) if (operation.ExtraData.TryGetValue(DetectorDrilling.ExtraDataKeyDispersionOfNormalizedRotorSpeed,
out object? oDispersionOfNormalizedRotorSpeed))
comment += $"Дисперсия нормированных оборотов ротора: {oDispersionOfNormalizedRotorSpeed}"; comment += $"Дисперсия нормированных оборотов ротора: {oDispersionOfNormalizedRotorSpeed}";
return comment; return comment;

View File

@ -110,7 +110,7 @@ public class DetectedOperationService : IDetectedOperationService
private async Task<int> GetIdTelemetryByWell(int idWell, CancellationToken token) private async Task<int> GetIdTelemetryByWell(int idWell, CancellationToken token)
{ {
var well = await wellService.GetOrDefaultAsync(idWell, 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 ?? var idTelemetry = well.IdTelemetry ??
throw new ArgumentInvalidException(nameof(idWell), "У скважины отсутствует телеметрия"); throw new ArgumentInvalidException(nameof(idWell), "У скважины отсутствует телеметрия");
@ -177,32 +177,38 @@ public class DetectedOperationService : IDetectedOperationService
return dtos; return dtos;
} }
public async Task<IEnumerable<DetectedOperationDto>> DetectOperationsAsync(int idTelemetry, DateTimeOffset? beginDate, CancellationToken token) public async Task<(DateTimeOffset LastDate, IEnumerable<DetectedOperationDto> Items)> DetectOperationsAsync(int idTelemetry,
TelemetryDataRequest request,
DetectedOperationDto? lastDetectedOperation,
CancellationToken token)
{ {
const int take = 4 * 86_400;
var detectedOperations = new List<DetectedOperationDto>();
DetectedOperationDto? lastDetectedOperation = null;
const int minOperationLength = 5; const int minOperationLength = 5;
const int maxDetectorsInterpolationFrameLength = 30; const int maxDetectorsInterpolationFrameLength = 30;
const int gap = maxDetectorsInterpolationFrameLength + minOperationLength; const int gap = maxDetectorsInterpolationFrameLength + minOperationLength;
var timezone = telemetryService.GetTimezone(idTelemetry);
while (true) var telemetries = await telemetryDataSaubService.GetByTelemetryAsync(idTelemetry, request, token);
{
var request = new TelemetryDataRequest
{
GeDate = beginDate,
Take = take,
Order = 0
};
var dtos = await telemetryDataSaubService.GetByTelemetryAsync(idTelemetry, request, token); var count = telemetries.Count();
var detectableTelemetries = dtos
if (count == 0)
throw new InvalidOperationException("InvalidOperation_EmptyTelemetries");
var timeZone = telemetryService.GetTimezone(idTelemetry);
if (telemetries.Count() <= gap)
{
var lastTelemetry = telemetries.Last();
var lastDateTelemetry = new DateTimeOffset(lastTelemetry.DateTime, timeZone.Offset);
return (lastDateTelemetry, Enumerable.Empty<DetectedOperationDto>());
}
var detectedOperations = new List<DetectedOperationDto>();
var detectableTelemetries = telemetries
.Where(t => t.BlockPosition >= 0) .Where(t => t.BlockPosition >= 0)
.Select(t => new DetectableTelemetry .Select(t => new DetectableTelemetry
{ {
DateTime = new DateTimeOffset(t.DateTime, timezone.Offset), DateTime = new DateTimeOffset(t.DateTime, timeZone.Offset),
IdUser = t.IdUser, IdUser = t.IdUser,
Mode = t.Mode, Mode = t.Mode,
WellDepth = t.WellDepth, WellDepth = t.WellDepth,
@ -214,35 +220,32 @@ public class DetectedOperationService : IDetectedOperationService
AxialLoad = t.AxialLoad, AxialLoad = t.AxialLoad,
}).ToArray(); }).ToArray();
if (detectableTelemetries.Length <= gap)
break;
var isDetected = false;
var positionBegin = 0; var positionBegin = 0;
var positionEnd = detectableTelemetries.Length - gap; var positionEnd = detectableTelemetries.Length - gap;
while (positionEnd > positionBegin) while (positionEnd > positionBegin)
{ {
foreach (var detector in detectors) foreach (var detector in detectors)
{ {
if (!detector.TryDetect(idTelemetry, detectableTelemetries, positionBegin, positionEnd, lastDetectedOperation, out var result)) if (!detector.TryDetect(idTelemetry, detectableTelemetries, positionBegin, positionEnd, lastDetectedOperation,
out var result))
continue; continue;
detectedOperations.Add(result!.Operation); detectedOperations.Add(result!.Operation);
lastDetectedOperation = result.Operation; lastDetectedOperation = result.Operation;
isDetected = true;
positionBegin = result.TelemetryEnd; positionBegin = result.TelemetryEnd;
break; break;
} }
positionBegin += 1; var skip = 1;
while (IsChangingTelemetryInterval(detectableTelemetries[positionBegin], detectableTelemetries[positionBegin + skip]))
skip++;
positionBegin += skip;
} }
beginDate = isDetected return (detectableTelemetries[positionBegin].DateTime, detectedOperations);
? lastDetectedOperation!.DateEnd
: detectableTelemetries[positionEnd].DateTime;
}
return detectedOperations;
} }
public async Task<int> DeleteAsync(DetectedOperationByWellRequest request, CancellationToken token) public async Task<int> DeleteAsync(DetectedOperationByWellRequest request, CancellationToken token)
@ -256,6 +259,23 @@ public class DetectedOperationService : IDetectedOperationService
return result; return result;
} }
private static bool IsChangingTelemetryInterval(DetectableTelemetry telemetryBegin, DetectableTelemetry telemetryEnd)
{
return telemetryBegin.Mode == telemetryEnd.Mode &&
EqualParameter(telemetryBegin.WellDepth, telemetryEnd.WellDepth, 0.01f) &&
EqualParameter(telemetryBegin.Pressure, telemetryEnd.Pressure, 0.1f) &&
EqualParameter(telemetryBegin.HookWeight, telemetryEnd.HookWeight, 0.1f) &&
EqualParameter(telemetryBegin.BlockPosition, telemetryEnd.BlockPosition, 0.01f) &&
EqualParameter(telemetryBegin.BitDepth, telemetryEnd.BitDepth, 0.01f) &&
EqualParameter(telemetryBegin.RotorSpeed, telemetryEnd.RotorSpeed, 0.01f) &&
EqualParameter(telemetryBegin.AxialLoad, telemetryEnd.AxialLoad, 0.1f);
static bool EqualParameter(float value, float origin, float tolerance)
{
return value <= origin + tolerance && value >= origin - tolerance;
}
}
private static IEnumerable<DetectedOperationDrillersStatDto> GetOperationsDrillersStat(IEnumerable<DetectedOperationWithDrillerDto> operations) private static IEnumerable<DetectedOperationDrillersStatDto> GetOperationsDrillersStat(IEnumerable<DetectedOperationWithDrillerDto> operations)
{ {
var groups = operations.GroupBy(o => o.Driller); var groups = operations.GroupBy(o => o.Driller);
@ -305,7 +325,6 @@ public class DetectedOperationService : IDetectedOperationService
}; };
} }
private static DetectedOperationWithDrillerDto Convert(DetectedOperationDto operation, IEnumerable<OperationValueDto> operationValues, IEnumerable<ScheduleDto> schedules) private static DetectedOperationWithDrillerDto Convert(DetectedOperationDto operation, IEnumerable<OperationValueDto> operationValues, IEnumerable<ScheduleDto> schedules)
{ {
var dto = operation.Adapt<DetectedOperationWithDrillerDto>(); var dto = operation.Adapt<DetectedOperationWithDrillerDto>();

View File

@ -2,10 +2,10 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using AsbCloudApp.Data.DetectedOperation; 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_NotDetected = 0;
protected const int IdReasonOfEnd_NotDetectBegin = 1; protected const int IdReasonOfEnd_NotDetectBegin = 1;
@ -30,11 +30,11 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
protected const int IdReasonOfEnd_Drilling = 600; protected const int IdReasonOfEnd_Drilling = 600;
protected const int IdReasonOfEnd_ChangeBithDepthAndAxiloadLessHookWeight = 700; protected const int IdReasonOfEnd_ChangeBitDepthAndAxiLoadLessHookWeight = 700;
protected const int IdReasonOfEnd_DeltaWellDepthAndBithDepthIsLo = 800; protected const int IdReasonOfEnd_DeltaWellDepthAndBitDepthIsLo = 800;
protected const int IdReasonOfEnd_BithDepthIsLo = 900; protected const int IdReasonOfEnd_BitDepthIsLo = 900;
public bool TryDetect(int idTelemetry, DetectableTelemetry[] telemetry, int begin, int end, DetectedOperationDto? previousOperation, public bool TryDetect(int idTelemetry, DetectableTelemetry[] telemetry, int begin, int end, DetectedOperationDto? previousOperation,
out OperationDetectorResult? result) out OperationDetectorResult? result)
@ -52,6 +52,8 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
if (positionEnd > end) if (positionEnd > end)
break; break;
//TODO: поиск провалов телеметрий. Следует обсудить, так как алгоритмы теряют в точности.
idReasonOfEnd = DetectEnd(telemetry, positionEnd, previousOperation); idReasonOfEnd = DetectEnd(telemetry, positionEnd, previousOperation);
if (idReasonOfEnd != IdReasonOfEnd_NotDetected) if (idReasonOfEnd != IdReasonOfEnd_NotDetected)
@ -406,6 +408,4 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
return false; return false;
} }
}
} }

View File

@ -41,9 +41,9 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
if (currentPoint.RotorSpeed <=8) if (currentPoint.RotorSpeed <=8)
return IdReasonOfEnd_RotorSpeedIsHi; return IdReasonOfEnd_RotorSpeedIsHi;
if ((currentPoint.WellDepth - currentPoint.BitDepth) < 0.03d) if ((currentPoint.WellDepth - currentPoint.BitDepth) < 0.03d)
return IdReasonOfEnd_DeltaWellDepthAndBithDepthIsLo; return IdReasonOfEnd_DeltaWellDepthAndBitDepthIsLo;
if (currentPoint.BitDepth < 150) if (currentPoint.BitDepth < 150)
return IdReasonOfEnd_BithDepthIsLo; return IdReasonOfEnd_BitDepthIsLo;
return IdReasonOfEnd_NotDetected; return IdReasonOfEnd_NotDetected;
} }

View File

@ -48,11 +48,11 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
if (currentPoint.Pressure < 10) if (currentPoint.Pressure < 10)
return IdReasonOfEnd_PressureIsLo; return IdReasonOfEnd_PressureIsLo;
if ((currentPoint.WellDepth - currentPoint.BitDepth) < 0.01d) if ((currentPoint.WellDepth - currentPoint.BitDepth) < 0.01d)
return IdReasonOfEnd_DeltaWellDepthAndBithDepthIsLo; return IdReasonOfEnd_DeltaWellDepthAndBitDepthIsLo;
if (currentPoint.RotorSpeed > 8) if (currentPoint.RotorSpeed > 8)
return IdReasonOfEnd_RotorSpeedIsHi; return IdReasonOfEnd_RotorSpeedIsHi;
if (currentPoint.BitDepth < 150) if (currentPoint.BitDepth < 150)
return IdReasonOfEnd_BithDepthIsLo; return IdReasonOfEnd_BitDepthIsLo;
return IdReasonOfEnd_NotDetected; return IdReasonOfEnd_NotDetected;
} }

View File

@ -50,7 +50,7 @@ public class DetectorSlipsTime : DetectorAbstract
var deltaBitDepth = Math.Abs(currentPoint.BitDepth - prevPoint.BitDepth); var deltaBitDepth = Math.Abs(currentPoint.BitDepth - prevPoint.BitDepth);
if (deltaBitDepth > 0.001d && currentPoint.AxialLoad < currentPoint.HookWeight) if (deltaBitDepth > 0.001d && currentPoint.AxialLoad < currentPoint.HookWeight)
return IdReasonOfEnd_ChangeBithDepthAndAxiloadLessHookWeight; return IdReasonOfEnd_ChangeBitDepthAndAxiLoadLessHookWeight;
return IdReasonOfEnd_NotDetected; return IdReasonOfEnd_NotDetected;
} }

View File

@ -1,24 +1,28 @@
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsbCloudApp.Data; using AsbCloudApp.Data;
using AsbCloudApp.Data.SAUB;
using AsbCloudApp.Repositories; using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services; using AsbCloudApp.Services;
using AsbCloudInfrastructure.Background; using AsbCloudInfrastructure.Background;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace AsbCloudInfrastructure.Services.DetectOperations; namespace AsbCloudInfrastructure.Services.DetectOperations;
public class WorkOperationDetection: Work public class WorkOperationDetection : Work
{ {
private static readonly IDictionary<int, DateTimeOffset> CacheOfStartDatesByTelemetryId = new Dictionary<int, DateTimeOffset>();
public WorkOperationDetection() public WorkOperationDetection()
:base("Operation detection") : base("Operation detection")
{ {
Timeout = TimeSpan.FromMinutes(20); Timeout = TimeSpan.FromMinutes(20);
OnErrorAsync = (id, exception, token) => OnErrorAsync = (id, exception, _) =>
{ {
var text = $"work {id}, when {CurrentState?.State}, throw error:{exception.Message}"; var text = $"work {id}, when {CurrentState?.State}, throw error:{exception.Message}";
Trace.TraceWarning(text); Trace.TraceWarning(text);
@ -26,29 +30,62 @@ public class WorkOperationDetection: Work
}; };
} }
protected override async Task Action(string id, IServiceProvider services, Action<string, double?> onProgressCallback, CancellationToken token) protected override async Task Action(string id,
IServiceProvider services,
Action<string, double?> onProgressCallback,
CancellationToken token)
{ {
var telemetryRepository = services.GetRequiredService<ICrudRepository<TelemetryDto>>(); var telemetryRepository = services.GetRequiredService<ICrudRepository<TelemetryDto>>();
var detectedOperationRepository = services.GetRequiredService<IDetectedOperationRepository>(); var detectedOperationRepository = services.GetRequiredService<IDetectedOperationRepository>();
var detectedOperationService = services.GetRequiredService<IDetectedOperationService>(); var detectedOperationService = services.GetRequiredService<IDetectedOperationService>();
var telemetryDataCache = services.GetRequiredService<ITelemetryDataCache<TelemetryDataSaubDto>>();
var telemetryIds = (await telemetryRepository.GetAllAsync(token)) var idsTelemetry = (await telemetryRepository.GetAllAsync(token))
.Select(t => t.Id) .Select(t => t.Id)
.ToArray(); .ToArray();
var lastDetectedDates = await detectedOperationRepository.GetLastDetectedDatesAsync(token); var lastDetectedOperations = await detectedOperationRepository.GetLastDetectedOperationsAsync(token);
for (var i = 0; i < telemetryIds.Length; i++) for (int i = 0; i < idsTelemetry.Length; i++)
{ {
var telemetryId = telemetryIds[i]; var idTelemetry = idsTelemetry[i];
var beginDate = lastDetectedDates.TryGetValue(telemetryId, out var date) ? date : (DateTimeOffset?)null; var telemetryDateRange = telemetryDataCache.GetOrDefaultWellDataDateRange(idTelemetry);
onProgressCallback($"Start detecting telemetry: {telemetryId} from {beginDate}", i / telemetryIds.Length); if(telemetryDateRange == null)
var detectedOperations = await detectedOperationService.DetectOperationsAsync(telemetryId, beginDate, token); continue;
if (detectedOperations.Any()) var dateBegin = telemetryDateRange.From;
await detectedOperationRepository.InsertRangeAsync(detectedOperations, token); var dateEnd = telemetryDateRange.To;
if (lastDetectedOperations.TryGetValue(idTelemetry, out var lastDetectedOperation))
dateBegin = lastDetectedOperation.DateEnd;
if (CacheOfStartDatesByTelemetryId.TryGetValue(idTelemetry, out var dateBeginFromCache))
dateBegin = dateBeginFromCache;
onProgressCallback.Invoke($"Start detecting telemetry: {idTelemetry} from {dateBegin}", i / idsTelemetry.Length);
const int pointsCount = 4 * 86_400;
while (dateBegin < dateEnd)
{
var request = new TelemetryDataRequest
{
GeDate = dateBegin,
Take = pointsCount,
Order = 0
};
var detectedOperations =
await detectedOperationService.DetectOperationsAsync(idTelemetry, request, lastDetectedOperation, token);
dateBegin = detectedOperations.LastDate;
CacheOfStartDatesByTelemetryId[idTelemetry] = dateBegin;
await detectedOperationRepository.InsertRangeAsync(detectedOperations.Items, token);
}
} }
} }
} }

View File

@ -13,12 +13,12 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services.SAUB namespace AsbCloudInfrastructure.Services.SAUB;
{
public abstract class TelemetryDataBaseService<TDto, TEntity> : ITelemetryDataService<TDto> public abstract class TelemetryDataBaseService<TDto, TEntity> : ITelemetryDataService<TDto>
where TDto : AsbCloudApp.Data.ITelemetryData where TDto : AsbCloudApp.Data.ITelemetryData
where TEntity : class, AsbCloudDb.Model.ITelemetryData where TEntity : class, AsbCloudDb.Model.ITelemetryData
{ {
protected readonly IAsbCloudDbContext db; protected readonly IAsbCloudDbContext db;
protected readonly ITelemetryService telemetryService; protected readonly ITelemetryService telemetryService;
protected readonly ITelemetryDataCache<TDto> telemetryDataCache; protected readonly ITelemetryDataCache<TDto> telemetryDataCache;
@ -55,13 +55,13 @@ namespace AsbCloudInfrastructure.Services.SAUB
} }
var telemetry = telemetryService.GetOrCreateTelemetryByUid(uid); var telemetry = telemetryService.GetOrCreateTelemetryByUid(uid);
var timezone = telemetryService.GetTimezone(telemetry.Id); var timeZone = telemetryService.GetTimezone(telemetry.Id);
telemetryDataCache.AddRange(telemetry.Id, dtos); telemetryDataCache.AddRange(telemetry.Id, dtos);
var entities = dtosList.Select(dto => var entities = dtosList.Select(dto =>
{ {
var entity = Convert(dto, timezone.Hours); var entity = Convert(dto, timeZone.Hours);
entity.IdTelemetry = telemetry.Id; entity.IdTelemetry = telemetry.Id;
return entity; return entity;
}); });
@ -158,7 +158,7 @@ namespace AsbCloudInfrastructure.Services.SAUB
public async Task<IEnumerable<TDto>> GetByTelemetryAsync(int idTelemetry, TelemetryDataRequest request, CancellationToken token) public async Task<IEnumerable<TDto>> GetByTelemetryAsync(int idTelemetry, TelemetryDataRequest request, CancellationToken token)
{ {
var timezone = telemetryService.GetTimezone(idTelemetry); var timeZone = telemetryService.GetTimezone(idTelemetry);
var cache = telemetryDataCache.GetOrDefault(idTelemetry, request); var cache = telemetryDataCache.GetOrDefault(idTelemetry, request);
@ -171,7 +171,7 @@ namespace AsbCloudInfrastructure.Services.SAUB
.AsNoTracking() .AsNoTracking()
.ToArrayAsync(token); .ToArrayAsync(token);
var dtos = entities.Select(e => Convert(e, timezone.Hours)); var dtos = entities.Select(e => Convert(e, timeZone.Hours));
return dtos; return dtos;
} }
@ -297,9 +297,9 @@ namespace AsbCloudInfrastructure.Services.SAUB
return telemetryDataCache.GetOrDefaultWellDataDateRange(telemetry.Id); return telemetryDataCache.GetOrDefaultWellDataDateRange(telemetry.Id);
} }
protected abstract TDto Convert(TEntity src, double timezoneOffset); protected abstract TDto Convert(TEntity src, double timeZoneOffset);
protected abstract TEntity Convert(TDto src, double timezoneOffset); protected abstract TEntity Convert(TDto src, double timeZoneOffset);
public async Task<int> DeleteAsync(TelemetryPartDeleteRequest request, CancellationToken token) public async Task<int> DeleteAsync(TelemetryPartDeleteRequest request, CancellationToken token)
{ {
@ -307,5 +307,4 @@ namespace AsbCloudInfrastructure.Services.SAUB
db.Set<TEntity>().RemoveRange(query); db.Set<TEntity>().RemoveRange(query);
return await db.SaveChangesAsync(token); return await db.SaveChangesAsync(token);
} }
}
} }

View File

@ -153,8 +153,8 @@ public class TelemetryDataCache<TDto> : ITelemetryDataCache<TDto> where TDto : A
if (cacheItem.LastData.Count == 0) if (cacheItem.LastData.Count == 0)
return null; return null;
var to = FromDate(cacheItem.FirstByDate.DateTime, cacheItem.TimezoneOffset); var to = FromDate(cacheItem.LastData[^1].DateTime, cacheItem.TimezoneOffset);
var from = FromDate(cacheItem.LastData[^1].DateTime, cacheItem.TimezoneOffset); var from = FromDate(cacheItem.FirstByDate.DateTime, cacheItem.TimezoneOffset);
return new DatesRangeDto { From = from, To = to }; return new DatesRangeDto { From = from, To = to };
} }

View File

@ -13,6 +13,8 @@ using System.Linq;
using System.Text.Csv; using System.Text.Csv;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Requests;
namespace AsbCloudInfrastructure.Services.SAUB; namespace AsbCloudInfrastructure.Services.SAUB;

View File

@ -111,7 +111,7 @@ namespace AsbCloudWebApi.Controllers.SAUB
} }
/// <summary> /// <summary>
/// Получить список автоопределенных операций для редактирования /// Получить список авто определенных операций для редактирования
/// </summary> /// </summary>
/// <param name="idWell"></param> /// <param name="idWell"></param>
/// <param name="request"></param> /// <param name="request"></param>
@ -136,7 +136,7 @@ namespace AsbCloudWebApi.Controllers.SAUB
} }
/// <summary> /// <summary>
/// Получить статистику по автоопределенным операциям /// Получить статистику по авто определенным операциям
/// </summary> /// </summary>
/// <param name="idWell"></param> /// <param name="idWell"></param>
/// <param name="request"></param> /// <param name="request"></param>