forked from ddrilling/AsbCloudServer
ProcessMapReportService implement all calculations (not tested)
This commit is contained in:
parent
08e3e34162
commit
1f904a7434
@ -100,7 +100,7 @@ namespace AsbCloudApp.Data.SAUB
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Режим САУБ
|
/// Режим САУБ
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public short Mode { get; set; }
|
public short IdMode { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Текущий критерий бурения
|
/// Текущий критерий бурения
|
||||||
|
@ -1,13 +1,8 @@
|
|||||||
using AsbCloudApp.Data;
|
using AsbCloudApp.Data.ProcessMap;
|
||||||
using AsbCloudApp.Data.ProcessMap;
|
|
||||||
using AsbCloudApp.Data.SAUB;
|
using AsbCloudApp.Data.SAUB;
|
||||||
using AsbCloudApp.Exceptions;
|
using AsbCloudApp.Exceptions;
|
||||||
using AsbCloudApp.Repositories;
|
using AsbCloudApp.Repositories;
|
||||||
using AsbCloudApp.Requests;
|
|
||||||
using AsbCloudApp.Services;
|
using AsbCloudApp.Services;
|
||||||
using AsbCloudApp.Services.Subsystems;
|
|
||||||
using AsbCloudDb.Model;
|
|
||||||
using AsbCloudApp.Data.Subsystems;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -23,23 +18,17 @@ namespace AsbCloudInfrastructure.Services.ProcessMap
|
|||||||
private readonly IWellOperationRepository wellOperationRepository;
|
private readonly IWellOperationRepository wellOperationRepository;
|
||||||
private readonly IProcessMapPlanRepository processMapPlanRepository;
|
private readonly IProcessMapPlanRepository processMapPlanRepository;
|
||||||
private readonly ITelemetryDataSaubService telemetryDataSaubService;
|
private readonly ITelemetryDataSaubService telemetryDataSaubService;
|
||||||
private readonly ILimitingParameterRepository limitingParameterRepository;
|
|
||||||
private readonly ISubsystemOperationTimeService subsystemOperationTimeService;
|
|
||||||
|
|
||||||
public ProcessMapReportService(
|
public ProcessMapReportService(
|
||||||
IWellService wellService,
|
IWellService wellService,
|
||||||
IWellOperationRepository wellOperationService,
|
IWellOperationRepository wellOperationRepository,
|
||||||
IProcessMapPlanRepository processMapPlanRepository,
|
IProcessMapPlanRepository processMapPlanRepository,
|
||||||
ITelemetryDataSaubService telemetryDataSaubService,
|
ITelemetryDataSaubService telemetryDataSaubService)
|
||||||
ILimitingParameterRepository limitingParameterRepository,
|
|
||||||
ISubsystemOperationTimeService subsystemOperationTimeService)
|
|
||||||
{
|
{
|
||||||
this.wellService = wellService;
|
this.wellService = wellService;
|
||||||
this.wellOperationRepository = wellOperationService;
|
this.wellOperationRepository = wellOperationRepository;
|
||||||
this.processMapPlanRepository = processMapPlanRepository;
|
this.processMapPlanRepository = processMapPlanRepository;
|
||||||
this.telemetryDataSaubService = telemetryDataSaubService;
|
this.telemetryDataSaubService = telemetryDataSaubService;
|
||||||
this.limitingParameterRepository = limitingParameterRepository;
|
|
||||||
this.subsystemOperationTimeService = subsystemOperationTimeService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@ -53,50 +42,298 @@ namespace AsbCloudInfrastructure.Services.ProcessMap
|
|||||||
|
|
||||||
var processMapPlan = await processMapPlanRepository.GetByIdWellAsync(idWell, token);
|
var processMapPlan = await processMapPlanRepository.GetByIdWellAsync(idWell, token);
|
||||||
|
|
||||||
if(!processMapPlan.Any())
|
if (!processMapPlan.Any())
|
||||||
return Enumerable.Empty<ProcessMapReportDto>();
|
return Enumerable.Empty<ProcessMapReportDto>();
|
||||||
|
|
||||||
var telemetryDataStat = await telemetryDataSaubService.GetTelemetryDataStatAsync(idTelemetry, token);
|
var telemetryDataStat = (await telemetryDataSaubService.GetTelemetryDataStatAsync(idTelemetry, token)).ToArray();
|
||||||
|
if (!telemetryDataStat.Any())
|
||||||
|
return Enumerable.Empty<ProcessMapReportDto>();
|
||||||
|
|
||||||
var result = CalcByIntervals(processMapPlan, telemetryDataStat);
|
var result = CalcByIntervals(processMapPlan, telemetryDataStat);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<ProcessMapReportDto> CalcByIntervals(IEnumerable<ProcessMapPlanDto> processMapPlan, IEnumerable<TelemetryDataSaubStatDto> telemetryDataStat)
|
private IEnumerable<ProcessMapReportDto> CalcByIntervals(IEnumerable<ProcessMapPlanDto> processMapPlan, TelemetryDataSaubStatDto[] telemetryDataStat)
|
||||||
{
|
{
|
||||||
var result = new List<ProcessMapReportDto>(processMapPlan.Count() * 4);
|
var processMapIntervals = processMapPlan
|
||||||
var intervals = GetProcessMapIntervals(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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<(double, double)> GetProcessMapIntervals(IEnumerable<ProcessMapPlanDto> processMapPlan)
|
private IEnumerable<ProcessMapReportDto> CalcSubIntervals(
|
||||||
|
(double DepthStart, double DepthEnd) interval,
|
||||||
|
IEnumerable<ProcessMapPlanDto> processMapPlanInterval,
|
||||||
|
Span<TelemetryDataSaubStatDto> telemetryDataInterval,
|
||||||
|
IDictionary<int, string> sectionTypes)
|
||||||
{
|
{
|
||||||
|
var telemetryDataIntervalLength = telemetryDataInterval.Length;
|
||||||
return Enumerable.Empty<(double, double)>();
|
if (telemetryDataInterval.Length == 0)
|
||||||
}
|
return Enumerable.Empty<ProcessMapReportDto>();
|
||||||
|
|
||||||
private static DateTime GetInterpolatedDate(WellOperationDto operation, double depth)
|
var result = new List<ProcessMapReportDto>();
|
||||||
{
|
var telemetryIndexStart = 0;
|
||||||
var ratio = (depth - operation.DepthStart) / (operation.DepthEnd - operation.DepthStart);
|
var subInterval = interval;
|
||||||
var deltaHours = operation.DurationHours * ratio;
|
|
||||||
var interpolatedDate = operation.DateStart + TimeSpan.FromHours(deltaHours);
|
|
||||||
return interpolatedDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IEnumerable<(double min, double max)> SplitByIntervals(double min, double max)
|
for (var i = telemetryIndexStart; i < telemetryDataIntervalLength; i++)
|
||||||
{
|
|
||||||
const double step = 100;
|
|
||||||
var iMin = min;
|
|
||||||
var iMax = (1 + (int)(min / step)) * step;
|
|
||||||
for (; iMax < max; iMax += step)
|
|
||||||
{
|
{
|
||||||
yield return (iMin, iMax);
|
if (!IsSimilar(telemetryDataInterval[telemetryIndexStart], telemetryDataInterval[i]))
|
||||||
iMin = iMax;
|
{
|
||||||
|
subInterval.DepthEnd = telemetryDataInterval[i].WellDepthMax;
|
||||||
|
|
||||||
|
var intervalReportRow = CalcSubIntervalReportRow(subInterval, processMapPlanInterval, telemetryDataInterval[telemetryIndexStart..i], sectionTypes);
|
||||||
|
result.Add(intervalReportRow);
|
||||||
|
telemetryIndexStart = i;
|
||||||
|
subInterval.DepthStart = subInterval.DepthEnd;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
yield return (iMin, max);
|
|
||||||
|
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
|
#nullable disable
|
||||||
}
|
}
|
||||||
|
@ -42,9 +42,9 @@ namespace AsbCloudInfrastructure.Services.SAUB
|
|||||||
.Where(t => t.IdTelemetry == idTelemetry)
|
.Where(t => t.IdTelemetry == idTelemetry)
|
||||||
.Where(t => t.BlockPosition > 0.0001)
|
.Where(t => t.BlockPosition > 0.0001)
|
||||||
.Where(t => t.WellDepth > 0.0001)
|
.Where(t => t.WellDepth > 0.0001)
|
||||||
.Where(t => t.WellDepth - t.BitDepth < 0.01)
|
|
||||||
.Where(t => t.Mode != null)
|
.Where(t => t.Mode != null)
|
||||||
.Where(t => modes.Contains(t.Mode.Value))
|
.Where(t => modes.Contains(t.Mode.Value))
|
||||||
|
.Where(t => t.WellDepth - t.BitDepth < 0.01)
|
||||||
.GroupBy(t => new {
|
.GroupBy(t => new {
|
||||||
t.DateTime.Hour,
|
t.DateTime.Hour,
|
||||||
WellDepthX10 = Math.Truncate(t.WellDepth!.Value * 10),
|
WellDepthX10 = Math.Truncate(t.WellDepth!.Value * 10),
|
||||||
|
Loading…
Reference in New Issue
Block a user