forked from ddrilling/AsbCloudServer
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:
commit
b5b313d459
@ -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>
|
||||||
/// Удалить операции
|
/// Удалить операции
|
||||||
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
|
||||||
}
|
}
|
@ -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)
|
||||||
|
@ -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;
|
||||||
|
@ -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>();
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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 };
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user