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

381 lines
15 KiB
C#
Raw Normal View History

using AsbCloudApp.Data.ProcessMap;
using AsbCloudApp.Data.SAUB;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
2022-12-14 08:41:19 +05:00
using AsbCloudApp.Services;
using System;
2022-12-14 08:41:19 +05:00
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services.ProcessMap
{
2022-12-27 14:30:52 +05:00
#nullable enable
public partial class ProcessMapReportService : IProcessMapReportService
2022-12-14 08:41:19 +05:00
{
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)
2022-12-14 08:41:19 +05:00
{
this.wellService = wellService;
this.wellOperationRepository = wellOperationRepository;
this.processMapPlanRepository = processMapPlanRepository;
this.telemetryDataSaubService = telemetryDataSaubService;
}
/// <inheritdoc/>
2023-04-06 10:25:51 +05:00
public async Task<IEnumerable<ProcessMapReportDto>> GetProcessMapReportAsync(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)
{
2023-04-06 10:25:51 +05:00
var processMapIntervals = CalcDepthIntervals(processMapPlan);
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);
2023-04-06 10:25:51 +05:00
if (telemetryIndexEnd < 0)
telemetryIndexEnd = telemetryDataStat.Length - 1;
var telemetryDataInterval = telemetryDataStat.AsSpan(telemetryIndexStart, telemetryIndexEnd - telemetryIndexStart);
IEnumerable<ProcessMapReportDto> subIntervalsResult = CalcSubIntervals(interval, processMapPlanInterval, telemetryDataInterval, sectionTypes);
result.AddRange(subIntervalsResult);
telemetryIndexStart = telemetryIndexEnd;
}
return result;
}
2023-04-06 10:25:51 +05:00
private static IEnumerable<(double DepthStart, double DepthEnd)> CalcDepthIntervals(IEnumerable<ProcessMapPlanDto> processMapPlan)
{
if(!processMapPlan.Any())
yield break;
var intervalStarts = processMapPlan
.OrderBy(i => i.DepthStart)
.Select(p => p.DepthStart)
.Distinct()
.ToArray();
for (var i = 1; i < intervalStarts.Length; i++)
yield return (intervalStarts[i - 1], intervalStarts[i]);
yield return (intervalStarts[^1], processMapPlan.Max(p=>p.DepthEnd));
}
private static 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;
2023-04-06 10:25:51 +05:00
for (var i = telemetryIndexStart + 1; i < telemetryDataIntervalLength; i++)
{
2023-04-06 10:25:51 +05:00
if (IsDifferent(telemetryDataInterval[telemetryIndexStart], telemetryDataInterval[i]))
{
2023-04-06 10:25:51 +05:00
subInterval.DepthEnd = telemetryDataInterval[i - 1].WellDepthMax;
var telemetryRowSpan = telemetryDataInterval[telemetryIndexStart..(i - 1)];
if (!telemetryRowSpan.IsEmpty)
{
var intervalReportRow = CalcSubIntervalReportRow(subInterval, processMapPlanInterval, telemetryRowSpan, 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;
}
2023-04-06 10:25:51 +05:00
private static 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,
2023-04-06 10:25:51 +05:00
PressureDiff = telemetryStat.Pressure.MakeParams(processMapByMode?.Pressure.Plan, telemetryStat.SpUsageDepthTotal),
AxialLoad = telemetryStat.AxialLoad.MakeParams(processMapByMode?.AxialLoad.Plan, telemetryStat.SpUsageDepthTotal),
TopDriveTorque = telemetryStat.RotorTorque.MakeParams(processMapByMode?.TopDriveTorque.Plan, telemetryStat.SpUsageDepthTotal),
SpeedLimit = telemetryStat.BlockSpeed.MakeParams(processMapByMode?.RopPlan, telemetryStat.SpUsageDepthTotal),
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;
2022-12-14 08:41:19 +05:00
}
private static (int IdMode, string ModeName) GetIdMode(Span<TelemetryDataSaubStatDto> telemetryRowSpan)
2022-12-14 08:41:19 +05:00
{
/// Режим работы САУБ в телеметрии:
/// 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, "Ручной");
2022-12-27 14:30:52 +05:00
}
2023-04-06 10:25:51 +05:00
private static bool IsDifferent(TelemetryDataSaubStatDto intervalStart, TelemetryDataSaubStatDto current)
2022-12-27 14:30:52 +05:00
{
2023-04-06 10:25:51 +05:00
if (intervalStart.WellDepthMin > current.WellDepthMin)
return true;
if (intervalStart.IdMode != current.IdMode)
return true;
2023-04-06 10:25:51 +05:00
if (Math.Abs(intervalStart.PressureSp - current.PressureSp) > 5d)
return true;
2023-04-06 10:25:51 +05:00
if (Math.Abs(intervalStart.AxialLoadSp - current.AxialLoadSp) > 1d)
return true;
2023-04-06 10:25:51 +05:00
if (Math.Abs(intervalStart.RotorTorqueSp - current.RotorTorqueSp) > 5d)
return true;
2023-04-06 10:25:51 +05:00
var blockSpeedSpDiff = Math.Abs(intervalStart.BlockSpeedSp - current.BlockSpeedSp);
if (blockSpeedSpDiff > 5d)
{
2023-04-06 10:25:51 +05:00
if (intervalStart.BlockSpeedSp <= 30)
return true;
else if (intervalStart.BlockSpeedSp > 30 && blockSpeedSpDiff > 15d)
return true;
else if (intervalStart.BlockSpeedSp > 80 && blockSpeedSpDiff > 20d)
return true;
}
2023-04-06 10:25:51 +05:00
return false;
2022-12-14 08:41:19 +05:00
}
2022-12-14 08:41:19 +05:00
}
class ParamStat
{
private double spWSum;
private double pvWSum;
2023-04-06 10:25:51 +05:00
private double limitMaxWSum;
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;
2023-04-06 10:25:51 +05:00
private TelemetryDataSaubStatDto? previous;
2023-04-06 10:25:51 +05:00
public double SpUsageDepth { get; private set; }
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;
2023-04-06 10:25:51 +05:00
spWSum += CalculateWeight(getterSp);
pvWSum += CalculateWeight(getterPv);
if(getterLimitMax is not null)
2023-04-06 10:25:51 +05:00
limitMaxWSum += CalculateWeight(getterLimitMax!);
if (current.IdFeedRegulator is not null)
{
if(current.IdFeedRegulator == idFeedRegulator)
SpUsageDepth += deltaDepth;
}
else
{
var pvErr = (getterSp(current) - getterPv(current)) / getterSp(current);
if (pvErr < 0.03d) //3%
SpUsageDepth += deltaDepth;
}
deltaDepthSum += deltaDepth;
}
}
previous = current;
}
2023-04-06 10:25:51 +05:00
public ProcessMapReportParamsDto MakeParams(double? spPlan, double SpUsageDepthTotal)
=> new ProcessMapReportParamsDto
{
SetpointPlan = spPlan,
2023-04-06 10:25:51 +05:00
SetpointFact = DivideValByDepth(spWSum),
Fact = DivideValByDepth(pvWSum),
Limit = (getterLimitMax is not null) ? DivideValByDepth(limitMaxWSum) : null,
SetpointUsage = deltaDepthSum > 0d ? SpUsageDepth / SpUsageDepthTotal : null,
};
2023-04-06 10:25:51 +05:00
private double? DivideValByDepth(double? val)
{
if(val is null || val == 0d || deltaDepthSum == 0d)
return null;
return val / deltaDepthSum;
}
}
class TelemetryStat {
public ParamStat Pressure { get; }
public ParamStat AxialLoad {get; }
public ParamStat RotorTorque {get; }
public ParamStat BlockSpeed {get; }
2023-04-06 10:25:51 +05:00
public double SpUsageDepthTotal => Pressure.SpUsageDepth + AxialLoad.SpUsageDepth + RotorTorque.SpUsageDepth + BlockSpeed.SpUsageDepth;
private TelemetryDataSaubStatDto? previous;
private double depthSum = 0d;
private double hoursSum = 0d;
2023-04-06 10:25:51 +05:00
public double? Rop => hoursSum == 0d ? null : 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);
}
};
2022-12-27 14:30:52 +05:00
#nullable disable
}