DD.WellWorkover.Cloud/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs
Степанов Дмитрий bfe4be7f61 Фикс
2024-07-29 10:49:08 +03:00

348 lines
14 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using AsbCloudApp.Data;
using AsbCloudApp.Data.DetectedOperation;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using Mapster;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data.WellOperation;
using AsbCloudApp.Exceptions;
using AsbCloudInfrastructure.Services.DetectOperations.Detectors;
namespace AsbCloudInfrastructure.Services.DetectOperations;
public class DetectedOperationService : IDetectedOperationService
{
private readonly IDetectedOperationRepository operationRepository;
private readonly IWellOperationCategoryRepository wellOperationCategoryRepository;
private readonly IWellService wellService;
private readonly ITelemetryService telemetryService;
private readonly IRepositoryWellRelated<OperationValueDto> operationValueRepository;
private readonly IScheduleRepository scheduleRepository;
private readonly ITelemetryDataSaubService telemetryDataSaubService;
private static readonly DetectorAbstract[] detectors = {
new DetectorDrilling(),
new DetectorSlipsTime(),
new DetectorFlashing(),
new DetectorConditioning(),
};
public DetectedOperationService(
IDetectedOperationRepository operationRepository,
IWellOperationCategoryRepository wellOperationCategoryRepository,
IWellService wellService,
ITelemetryService telemetryService,
IRepositoryWellRelated<OperationValueDto> operationValueRepository,
IScheduleRepository scheduleRepository,
ITelemetryDataSaubService telemetryDataSaubService)
{
this.operationRepository = operationRepository;
this.wellOperationCategoryRepository = wellOperationCategoryRepository;
this.wellService = wellService;
this.telemetryService = telemetryService;
this.operationValueRepository = operationValueRepository;
this.scheduleRepository = scheduleRepository;
this.telemetryDataSaubService = telemetryDataSaubService;
}
public async Task<DetectedOperationListDto> GetAsync(DetectedOperationByWellRequest request, CancellationToken token)
{
var dtos = await GetOperationsAsync(request, token);
if (dtos?.Any() != true)
return new DetectedOperationListDto();
var stats = GetOperationsDrillersStat(dtos);
var result = new DetectedOperationListDto
{
Operations = dtos,
Stats = stats
};
return result;
}
public async Task<IEnumerable<DetectedOperationWithDrillerDto>> GetOperationsAsync(DetectedOperationByWellRequest request, CancellationToken token)
{
var well = await wellService.GetOrDefaultAsync(request.IdWell, token);
if (well?.IdTelemetry is null)
return Enumerable.Empty<DetectedOperationWithDrillerDto>();
var requestByTelemetry = new DetectedOperationByTelemetryRequest(well.IdTelemetry.Value, request);
var data = await operationRepository.Get(requestByTelemetry, token);
var operationValues = await operationValueRepository.GetByIdWellAsync(request.IdWell, token);
var schedules = await scheduleRepository.GetByIdWellAsync(request.IdWell, token);
var dtos = data.Select(o => Convert(o, operationValues, schedules));
return dtos;
}
public async Task<int> InsertRangeManualAsync(int idEditor, int idWell, IEnumerable<DetectedOperationDto> dtos, CancellationToken token)
{
var idTelemetry = await GetIdTelemetryByWell(idWell, token);
foreach (var dto in dtos)
{
dto.IdEditor = idEditor;
dto.IdTelemetry = idTelemetry;
}
return await operationRepository.InsertRangeAsync(dtos, token);
}
public async Task<int> UpdateRangeManualAsync(int idEditor, int idWell, IEnumerable<DetectedOperationDto> dtos, CancellationToken token)
{
var idTelemetry = await GetIdTelemetryByWell(idWell, token);
foreach (var dto in dtos)
{
dto.IdEditor = idEditor;
dto.IdTelemetry = idTelemetry;
}
return await operationRepository.UpdateRangeAsync(dtos, token);
}
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");
var idTelemetry = well.IdTelemetry ??
throw new ArgumentInvalidException(nameof(idWell), "У скважины отсутствует телеметрия");
return idTelemetry;
}
public async Task<IEnumerable<WellOperationCategoryDto>> GetCategoriesAsync(int? idWell, CancellationToken token)
{
if(idWell is null)
{
return wellOperationCategoryRepository.Get(false);
}
else
{
var well = await wellService.GetOrDefaultAsync((int )idWell, token);
if (well?.IdTelemetry is null)
return Enumerable.Empty<WellOperationCategoryDto>();
var request = new DetectedOperationByTelemetryRequest()
{
IdTelemetry = well.IdTelemetry.Value
};
var operations = await operationRepository.Get(request, token);
var categories = operations
.Select(o => o.OperationCategory)
.Distinct();
return categories;
}
}
[Obsolete]
public async Task<IEnumerable<DetectedOperationStatDto>> GetOperationsStatAsync(DetectedOperationByWellRequest request, CancellationToken token)
{
var well = await wellService.GetOrDefaultAsync(request.IdWell, token);
if (well?.IdTelemetry is null || well.Timezone is null)
return Enumerable.Empty<DetectedOperationStatDto>();
var requestByTelemetry = new DetectedOperationByTelemetryRequest(well.IdTelemetry.Value, request);
var operations = await operationRepository.Get(requestByTelemetry, token);
if (!operations.Any())
return Enumerable.Empty<DetectedOperationStatDto>();
var dtos = operations
.GroupBy(o => (o.IdCategory, o.OperationCategory.Name))
.OrderBy(g => g.Key)
.Select(g => new DetectedOperationStatDto
{
IdCategory = g.Key.IdCategory,
Category = g.Key.Name,
Count = g.Count(),
MinutesAverage = g.Average(o => o.DurationMinutes),
MinutesMin = g.Min(o => o.DurationMinutes),
MinutesMax = g.Max(o => o.DurationMinutes),
MinutesTotal = g.Sum(o => o.DurationMinutes),
ValueAverage = g.Average(o => o.Value),
ValueMax = g.Max(o => o.Value),
ValueMin = g.Min(o => o.Value),
});
return dtos;
}
public async Task<(DateTimeOffset LastDate, IEnumerable<DetectedOperationDto> Items)> DetectOperationsAsync(int idTelemetry,
TelemetryDataRequest request,
DetectedOperationDto? lastDetectedOperation,
CancellationToken token)
{
const int minOperationLength = 5;
const int maxDetectorsInterpolationFrameLength = 30;
const int gap = maxDetectorsInterpolationFrameLength + minOperationLength;
var telemetries = await telemetryDataSaubService.GetByTelemetryAsync(idTelemetry, request, token);
var count = telemetries.Count();
if (count == 0)
throw new InvalidOperationException("InvalidOperation_EmptyTelemetries");
var timeZone = telemetryService.GetTimezone(idTelemetry);
var detectedOperations = new List<DetectedOperationDto>();
var detectableTelemetries = telemetries
.Where(t => t.BlockPosition >= 0)
.Select(t => new DetectableTelemetry
{
DateTime = new DateTimeOffset(t.DateTime, timeZone.Offset),
IdUser = t.IdUser,
Mode = t.Mode,
WellDepth = t.WellDepth,
Pressure = t.Pressure,
HookWeight = t.HookWeight,
BlockPosition = t.BlockPosition,
BitDepth = t.BitDepth,
RotorSpeed = t.RotorSpeed,
AxialLoad = t.AxialLoad,
}).ToArray();
if (detectableTelemetries.Length <= gap)
{
var lastTelemetry = telemetries.Last();
var lastDateTelemetry = new DateTimeOffset(lastTelemetry.DateTime, timeZone.Offset);
return (lastDateTelemetry, Enumerable.Empty<DetectedOperationDto>());
}
var positionBegin = 0;
var positionEnd = detectableTelemetries.Length - gap;
while (positionEnd > positionBegin)
{
foreach (var detector in detectors)
{
if (!detector.TryDetect(idTelemetry, detectableTelemetries, positionBegin, positionEnd, lastDetectedOperation,
out var result))
continue;
detectedOperations.Add(result!.Operation);
lastDetectedOperation = result.Operation;
positionBegin = result.TelemetryEnd;
break;
}
var point0 = detectableTelemetries[positionBegin];
while (positionBegin < positionEnd && IsChangingTelemetryInterval(point0, detectableTelemetries[positionBegin]))
positionBegin++;
}
return (detectableTelemetries[positionBegin].DateTime, detectedOperations);
}
public async Task<int> DeleteAsync(DetectedOperationByWellRequest request, CancellationToken token)
{
var well = await wellService.GetOrDefaultAsync(request.IdWell, token);
if (well?.IdTelemetry is null || well.Timezone is null)
return 0;
var requestByTelemetry = new DetectedOperationByTelemetryRequest(well.IdTelemetry.Value, request);
var result = await operationRepository.DeleteAsync(requestByTelemetry, token);
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)
{
var groups = operations.GroupBy(o => o.Driller);
var stats = new List<DetectedOperationDrillersStatDto>(groups.Count());
foreach (var group in groups)
{
var itemsWithTarget = group.Where(i => i.OperationValue is not null);
var stat = new DetectedOperationDrillersStatDto
{
Driller = group.Key,
AverageValue = group.Sum(e => e.Value) / group.Count(),
Count = group.Count(),
};
if (itemsWithTarget.Any())
{
var itemsOutOfTarget = itemsWithTarget.Where(o => !IsTargetOk(o));
stat.AverageTargetValue = itemsWithTarget.Average(e => e.OperationValue?.TargetValue);
stat.Efficiency = 100d * itemsOutOfTarget.Count() / itemsWithTarget.Count();
stat.Loss = itemsOutOfTarget.Sum(DeltaToTarget);
}
stats.Add(stat);
}
return stats;
}
private static bool IsTargetOk(DetectedOperationWithDrillerDto op)
{
return (op.IdCategory) switch
{
WellOperationCategory.IdRotor => op.Value > op.OperationValue?.TargetValue,
WellOperationCategory.IdSlide => op.Value > op.OperationValue?.TargetValue,
WellOperationCategory.IdSlipsTime => op.Value > op.OperationValue?.TargetValue,
_ => op.Value > op.OperationValue?.TargetValue,
};
}
private static double DeltaToTarget(DetectedOperationWithDrillerDto op)
{
return (op.IdCategory) switch
{
WellOperationCategory.IdRotor => 0,
WellOperationCategory.IdSlide => 0,
WellOperationCategory.IdSlipsTime => op.Value - op.OperationValue?.TargetValue??0,
_ => 0,
};
}
private static DetectedOperationWithDrillerDto Convert(DetectedOperationDto operation, IEnumerable<OperationValueDto> operationValues, IEnumerable<ScheduleDto> schedules)
{
var dto = operation.Adapt<DetectedOperationWithDrillerDto>();
dto.OperationValue = operationValues.FirstOrDefault(v => v.IdOperationCategory == dto.IdCategory
&& v.DepthStart <= dto.DepthStart
&& v.DepthEnd > dto.DepthStart);
var dateStart = dto.DateStart.ToUniversalTime();
var timeStart = new TimeDto(dateStart);
var driller = schedules.FirstOrDefault(s =>
s.DrillStart <= dateStart &&
s.DrillEnd > dateStart && (
s.ShiftStart > s.ShiftEnd
) ^ (s.ShiftStart <= timeStart &&
s.ShiftEnd > timeStart
))
?.Driller;
dto.Driller = driller;
return dto;
}
}