DD.WellWorkover.Cloud/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapReportService.cs

340 lines
14 KiB
C#
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 AsbCloudApp.Data.ProcessMap;
using AsbCloudApp.Data.SAUB;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services.ProcessMap
{
#nullable enable
public partial class ProcessMapReportService : IProcessMapReportService
{
private readonly IWellService wellService;
private readonly IWellOperationRepository wellOperationRepository;
private readonly IProcessMapPlanRepository processMapPlanRepository;
private readonly ITelemetryDataSaubService telemetryDataSaubService;
public ProcessMapReportService(
IWellService wellService,
IWellOperationRepository wellOperationRepository,
IProcessMapPlanRepository processMapPlanRepository,
ITelemetryDataSaubService telemetryDataSaubService)
{
this.wellService = wellService;
this.wellOperationRepository = wellOperationRepository;
this.processMapPlanRepository = processMapPlanRepository;
this.telemetryDataSaubService = telemetryDataSaubService;
}
/// <inheritdoc/>
public async Task<IEnumerable<ProcessMapReportDto>> GetProcessMapAsync(int idWell, CancellationToken token)
{
var well = wellService.GetOrDefault(idWell)
?? throw new ArgumentInvalidException("idWell not found", nameof(idWell));
var idTelemetry = well.IdTelemetry
?? throw new ArgumentInvalidException("telemetry by well not found", nameof(idWell));
var processMapPlan = await processMapPlanRepository.GetByIdWellAsync(idWell, token);
if (!processMapPlan.Any())
return Enumerable.Empty<ProcessMapReportDto>();
var telemetryDataStat = (await telemetryDataSaubService.GetTelemetryDataStatAsync(idTelemetry, token)).ToArray();
if (!telemetryDataStat.Any())
return Enumerable.Empty<ProcessMapReportDto>();
var result = CalcByIntervals(processMapPlan, telemetryDataStat);
return result;
}
private IEnumerable<ProcessMapReportDto> CalcByIntervals(IEnumerable<ProcessMapPlanDto> processMapPlan, TelemetryDataSaubStatDto[] telemetryDataStat)
{
var processMapIntervals = processMapPlan
.Select(p => (p.DepthStart, p.DepthEnd))
.Distinct()
.OrderBy(i => i.DepthStart);
var result = new List<ProcessMapReportDto>(processMapIntervals.Count() * 4);
var telemetryIndexStart = Array.FindIndex(telemetryDataStat, t => t.WellDepthMin >= processMapIntervals.First().DepthStart);
if (telemetryIndexStart < 0)
return Enumerable.Empty<ProcessMapReportDto>();
IDictionary<int, string> sectionTypes = wellOperationRepository.GetSectionTypes();
foreach (var interval in processMapIntervals)
{
var processMapPlanInterval = processMapPlan
.Where(p => p.DepthStart >= interval.DepthStart && p.DepthEnd <= interval.DepthEnd);
var telemetryIndexEnd = Array.FindIndex(telemetryDataStat, telemetryIndexStart, t => t.WellDepthMin >= interval.DepthEnd);
var telemetryDataInterval = telemetryDataStat.AsSpan(telemetryIndexStart, telemetryIndexEnd - telemetryIndexStart);
IEnumerable<ProcessMapReportDto> subIntervalsResult = CalcSubIntervals(interval, processMapPlanInterval, telemetryDataInterval, sectionTypes);
result.AddRange(subIntervalsResult);
telemetryIndexStart = telemetryIndexEnd;
}
return result;
}
private IEnumerable<ProcessMapReportDto> CalcSubIntervals(
(double DepthStart, double DepthEnd) interval,
IEnumerable<ProcessMapPlanDto> processMapPlanInterval,
Span<TelemetryDataSaubStatDto> telemetryDataInterval,
IDictionary<int, string> sectionTypes)
{
var telemetryDataIntervalLength = telemetryDataInterval.Length;
if (telemetryDataInterval.Length == 0)
return Enumerable.Empty<ProcessMapReportDto>();
var result = new List<ProcessMapReportDto>();
var telemetryIndexStart = 0;
var subInterval = interval;
for (var i = telemetryIndexStart; i < telemetryDataIntervalLength; i++)
{
if (!IsSimilar(telemetryDataInterval[telemetryIndexStart], telemetryDataInterval[i]))
{
subInterval.DepthEnd = telemetryDataInterval[i].WellDepthMax;
var intervalReportRow = CalcSubIntervalReportRow(subInterval, processMapPlanInterval, telemetryDataInterval[telemetryIndexStart..i], sectionTypes);
result.Add(intervalReportRow);
telemetryIndexStart = i;
subInterval.DepthStart = subInterval.DepthEnd;
}
}
subInterval.DepthEnd = interval.DepthEnd;
var intervalReportRowLast = CalcSubIntervalReportRow(subInterval, processMapPlanInterval, telemetryDataInterval[telemetryIndexStart..telemetryDataIntervalLength], sectionTypes);
result.Add(intervalReportRowLast);
return result;
}
private ProcessMapReportDto CalcSubIntervalReportRow(
(double DepthStart, double DepthEnd) subInterval,
IEnumerable<ProcessMapPlanDto> processMap,
Span<TelemetryDataSaubStatDto> telemetryRowSpan,
IDictionary<int, string> sectionTypes)
{
var telemetryFirst = telemetryRowSpan[0];
var telemetryLast = telemetryRowSpan[^1];
var mode = GetIdMode(telemetryRowSpan);
var processMapByMode = processMap.FirstOrDefault(p => p.IdMode == mode.IdMode);
var processMapFirst = processMap.First();
var idWellSectionType = processMapByMode?.IdWellSectionType ?? processMapFirst.IdWellSectionType;
var telemetryStat = AnalyzeTelemetry(telemetryRowSpan);
var result = new ProcessMapReportDto
{
IdWell = processMapByMode?.IdWell ?? processMapFirst.IdWell,
IdWellSectionType = idWellSectionType,
WellSectionTypeName = sectionTypes[idWellSectionType],
DepthStart = subInterval.DepthStart,
DepthEnd = subInterval.DepthEnd,
DateStart = telemetryFirst.DateMin,
MechDrillingHours = (telemetryLast.DateMax - telemetryFirst.DateMin).TotalHours,
DrillingMode = mode.ModeName,
DeltaDepth = telemetryLast.WellDepthMax - telemetryFirst.WellDepthMin,
PressureDiff = telemetryStat.Pressure.MakeParams(processMapByMode?.Pressure.Plan),
AxialLoad = telemetryStat.AxialLoad.MakeParams(processMapByMode?.AxialLoad.Plan),
TopDriveTorque = telemetryStat.RotorTorque.MakeParams(processMapByMode?.TopDriveTorque.Plan),
SpeedLimit = telemetryStat.BlockSpeed.MakeParams(processMapByMode?.RopPlan),
Rop = telemetryStat.Rop,
Usage = telemetryStat.UsageSaub,
};
return result;
}
private static TelemetryStat AnalyzeTelemetry(Span<TelemetryDataSaubStatDto> telemetry)
{
var stat = new TelemetryStat();
for (int i = 0; i < telemetry.Length; i++)
stat.UpdateStat(telemetry[i]);
return stat;
}
private static (int IdMode, string ModeName) GetIdMode(Span<TelemetryDataSaubStatDto> telemetryRowSpan)
{
/// Режим работы САУБ в телеметрии:
/// 0 - "РУЧНОЙ"
/// 1 - "БУРЕНИЕ В РОТОРЕ"
/// 3 - "БУРЕНИЕ В СЛАЙДЕ"
for (int i = 0; i < telemetryRowSpan.Length; i++)
{
var idMode = telemetryRowSpan[i].IdMode;
if (idMode == 0)
return (0, "Ручной");
if (idMode == 1)
return (1, "Ротор");
if (idMode == 3)
return (2, "Слайд");
}
return (0, "Ручной");
}
private static bool IsSimilar(TelemetryDataSaubStatDto telemetry1, TelemetryDataSaubStatDto telemetry2)
{
if (telemetry1.IdMode != telemetry2.IdMode)
return false;
if (Math.Abs(telemetry1.PressureSp - telemetry2.PressureSp) > 5d)
return false;
if (Math.Abs(telemetry1.AxialLoadSp - telemetry2.AxialLoadSp) > 1d)
return false;
if (Math.Abs(telemetry1.RotorTorqueSp - telemetry2.RotorTorqueSp) > 5d)
return false;
var blockSpeedSpDiff = Math.Abs(telemetry1.BlockSpeedSp - telemetry2.BlockSpeedSp);
if (blockSpeedSpDiff > 5d)
{
if (telemetry1.BlockSpeedSp < 30)
return false;
else if (telemetry1.BlockSpeedSp > 30 && blockSpeedSpDiff > 15d)
return false;
else if (telemetry1.BlockSpeedSp > 80 && blockSpeedSpDiff > 20d)
return false;
}
return true;
}
}
class ParamStat
{
private double spWSum;
private double pvWSum;
private double? limitMaxWSum;
private double spUsageDepth;
private double deltaDepthSum;
private readonly Func<TelemetryDataSaubStatDto, double> getterSp;
private readonly Func<TelemetryDataSaubStatDto, double> getterPv;
private readonly Func<TelemetryDataSaubStatDto, double>? getterLimitMax;
private readonly int idFeedRegulator;
private TelemetryDataSaubStatDto? previous;
public ParamStat(Func<TelemetryDataSaubStatDto, double> getterSp,
Func<TelemetryDataSaubStatDto, double> getterPv,
Func<TelemetryDataSaubStatDto, double>? getterLimitMax,
int idFeedRegulator)
{
this.getterSp = getterSp;
this.getterPv = getterPv;
this.getterLimitMax = getterLimitMax;
this.idFeedRegulator = idFeedRegulator;
}
public void UpdateStat(TelemetryDataSaubStatDto current)
{
if(previous is not null)
{
var deltaDepth = current.WellDepthMin - previous.WellDepthMin;
if (deltaDepth > 0)
{
var deltaDepthHalf = deltaDepth / 2;
double CalculateWeight(Func<TelemetryDataSaubStatDto, double> getter) => (getter(previous!) + getter(current)) * deltaDepthHalf;
spWSum = CalculateWeight(getterSp);
pvWSum = CalculateWeight(getterPv);
if(getterLimitMax is not null)
limitMaxWSum = CalculateWeight(getterLimitMax!);
if (current.IdFeedRegulator == idFeedRegulator)
spUsageDepth += deltaDepth;
deltaDepthSum += deltaDepth;
}
}
previous = current;
}
public ProcessMapReportParamsDto MakeParams(double? spPlan)
=> new ProcessMapReportParamsDto
{
SetpointPlan = spPlan,
SetpointFact = spWSum / deltaDepthSum,
Fact = pvWSum / deltaDepthSum,
Limit = limitMaxWSum / deltaDepthSum,
PercDrillingBySetpoint = spUsageDepth / deltaDepthSum,
};
}
class TelemetryStat {
public ParamStat Pressure { get; }
public ParamStat AxialLoad {get; }
public ParamStat RotorTorque {get; }
public ParamStat BlockSpeed {get; }
private TelemetryDataSaubStatDto? previous;
private double depthSum = 0d;
private double hoursSum = 0d;
public double Rop => depthSum / hoursSum;
private double depthWithSaub = 0d;
public double UsageSaub => depthWithSaub / depthSum;
public TelemetryStat()
{
BlockSpeed = new(t => t.BlockSpeedSp, t => t.BlockSpeed, null, 1);
Pressure = new(t => t.PressureSp, t => t.Pressure, t=>t.PressureDeltaLimitMax, 2);
RotorTorque = new(t => t.RotorTorqueSp, t => t.RotorTorque, t=>t.RotorTorqueLimitMax, 3);
AxialLoad = new(t => t.AxialLoadSp, t => t.AxialLoad, t=>t.AxialLoadLimitMax, 4);
}
public void UpdateStat(TelemetryDataSaubStatDto current)
{
if(previous is not null)
{
var deltaDepth = current.WellDepthMin - previous.WellDepthMin;
if(deltaDepth > 0)
{
var deltaHours = (current.DateMin - previous.DateMax).TotalHours;
depthSum += deltaDepth;
hoursSum += deltaHours;
if(current.IdMode == 1 || current.IdMode == 3)
depthWithSaub += deltaDepth;
}
}
previous = current;
Pressure.UpdateStat(current);
AxialLoad.UpdateStat(current);
RotorTorque.UpdateStat(current);
BlockSpeed.UpdateStat(current);
}
};
#nullable disable
}