forked from ddrilling/AsbCloudServer
319 lines
16 KiB
C#
319 lines
16 KiB
C#
|
using AsbCloudApp.Data;
|
|||
|
using AsbCloudApp.Data.ProcessMapPlan;
|
|||
|
using AsbCloudApp.Data.ProcessMaps.Report;
|
|||
|
using AsbCloudApp.Exceptions;
|
|||
|
using AsbCloudApp.Repositories;
|
|||
|
using AsbCloudApp.Requests;
|
|||
|
using AsbCloudApp.Services;
|
|||
|
using AsbCloudApp.Services.ProcessMaps.WellDrilling;
|
|||
|
using AsbCloudDb.Model;
|
|||
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Linq;
|
|||
|
using System.Threading;
|
|||
|
using System.Threading.Tasks;
|
|||
|
|
|||
|
namespace AsbCloudInfrastructure.Services.ProcessMaps.Report
|
|||
|
{
|
|||
|
public class ProcessMapReportDataSaubStatService : IProcessMapReportDataSaubStatService
|
|||
|
{
|
|||
|
private readonly IWellService wellService;
|
|||
|
private readonly IChangeLogRepository<ProcessMapPlanDrillingDto, ProcessMapPlanBaseRequestWithWell> processMapPlanBaseRepository;
|
|||
|
private readonly IDataSaubStatRepository dataSaubStatRepository;
|
|||
|
private readonly IWellOperationRepository wellOperationRepository;
|
|||
|
|
|||
|
public ProcessMapReportDataSaubStatService(IWellService wellService,
|
|||
|
IChangeLogRepository<ProcessMapPlanDrillingDto, ProcessMapPlanBaseRequestWithWell> processMapPlanBaseRepository,
|
|||
|
IDataSaubStatRepository dataSaubStatRepository,
|
|||
|
IWellOperationRepository wellOperationRepository
|
|||
|
)
|
|||
|
{
|
|||
|
this.wellService = wellService;
|
|||
|
this.processMapPlanBaseRepository = processMapPlanBaseRepository;
|
|||
|
this.dataSaubStatRepository = dataSaubStatRepository;
|
|||
|
this.wellOperationRepository = wellOperationRepository;
|
|||
|
}
|
|||
|
|
|||
|
public async Task<IEnumerable<ProcessMapReportDataSaubStatDto>> GetAsync(int idWell, DataSaubStatRequest request, CancellationToken token)
|
|||
|
{
|
|||
|
var well = await wellService.GetOrDefaultAsync(idWell, token)
|
|||
|
?? throw new ArgumentInvalidException(nameof(idWell), $"Скважина с Id: {idWell} не найдена");
|
|||
|
|
|||
|
if (!well.IdTelemetry.HasValue)
|
|||
|
return Enumerable.Empty<ProcessMapReportDataSaubStatDto>();
|
|||
|
|
|||
|
var requestProcessMapPlan = new ProcessMapPlanBaseRequestWithWell(idWell);
|
|||
|
var processMapPlanWellDrillings = await processMapPlanBaseRepository.Get(requestProcessMapPlan, token);
|
|||
|
|
|||
|
if (!processMapPlanWellDrillings.Any())
|
|||
|
return Enumerable.Empty<ProcessMapReportDataSaubStatDto>();
|
|||
|
|
|||
|
var dataSaubStats =
|
|||
|
(await dataSaubStatRepository.GetAsync(well.IdTelemetry.Value, token)).ToArray();
|
|||
|
|
|||
|
if (!dataSaubStats.Any())
|
|||
|
return Enumerable.Empty<ProcessMapReportDataSaubStatDto>();
|
|||
|
|
|||
|
var requestWellOperationFact = new WellOperationRequest()
|
|||
|
{
|
|||
|
IdWell = idWell,
|
|||
|
OperationType = WellOperation.IdOperationTypeFact
|
|||
|
};
|
|||
|
var wellOperations = await wellOperationRepository
|
|||
|
.GetAsync(requestWellOperationFact, token);
|
|||
|
if (!wellOperations.Any())
|
|||
|
return Enumerable.Empty<ProcessMapReportDataSaubStatDto>();
|
|||
|
|
|||
|
var timeZone = TimeSpan.FromHours(wellService.GetTimezone(idWell).Hours);
|
|||
|
var result = CalcByIntervals(request, processMapPlanWellDrillings, dataSaubStats, wellOperations, timeZone);
|
|||
|
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
private IEnumerable<ProcessMapReportDataSaubStatDto> CalcByIntervals(
|
|||
|
DataSaubStatRequest request,
|
|||
|
IEnumerable<ProcessMapPlanDrillingDto> processMapPlanWellDrillings,
|
|||
|
Span<DataSaubStatDto> dataSaubStats,
|
|||
|
IEnumerable<WellOperationDto> wellOperations,
|
|||
|
TimeSpan timeZone
|
|||
|
)
|
|||
|
{
|
|||
|
var list = new List<ProcessMapReportDataSaubStatDto>();
|
|||
|
var firstElemInInterval = dataSaubStats[0];
|
|||
|
|
|||
|
var indexStart = 0;
|
|||
|
for (var i = 1; i < dataSaubStats.Length; i++)
|
|||
|
{
|
|||
|
var currentElem = dataSaubStats[i];
|
|||
|
if (IsNewInterval(currentElem, firstElemInInterval, request) || i == dataSaubStats.Length - 1)
|
|||
|
{
|
|||
|
var length = i - indexStart;
|
|||
|
var elem = CalcStat(processMapPlanWellDrillings, dataSaubStats, indexStart, length, wellOperations, timeZone);
|
|||
|
if (elem != null)
|
|||
|
list.Add(elem);
|
|||
|
|
|||
|
indexStart = i;
|
|||
|
firstElemInInterval = currentElem;
|
|||
|
}
|
|||
|
}
|
|||
|
return list;
|
|||
|
}
|
|||
|
|
|||
|
private ProcessMapReportDataSaubStatDto? CalcStat(
|
|||
|
IEnumerable<ProcessMapPlanDrillingDto> processMapPlanDrillingDtos,
|
|||
|
Span<DataSaubStatDto> dataSaubStats,
|
|||
|
int indexStart,
|
|||
|
int length,
|
|||
|
IEnumerable<WellOperationDto> wellOperations,
|
|||
|
TimeSpan timeZone
|
|||
|
)
|
|||
|
{
|
|||
|
var span = dataSaubStats.Slice(indexStart, length);
|
|||
|
var firstElemInInterval = span[0];
|
|||
|
var lastElemInInterval = span[^1];
|
|||
|
|
|||
|
var nearestOperation = wellOperations?.MinBy(o => firstElemInInterval.DateStart - o.DateStart);
|
|||
|
if (nearestOperation is null)
|
|||
|
return null;
|
|||
|
|
|||
|
var processMapPlanFilteredByDepth = processMapPlanDrillingDtos
|
|||
|
.Where(x => x.IdWellSectionType == nearestOperation.IdWellSectionType)
|
|||
|
.Where(x => x.DepthStart >= firstElemInInterval.DepthStart)
|
|||
|
.Where(x => x.DepthEnd <= lastElemInInterval.DepthEnd)
|
|||
|
.ToArray();
|
|||
|
if (!processMapPlanFilteredByDepth.Any())
|
|||
|
return null;
|
|||
|
|
|||
|
var deltaDepth = lastElemInInterval.DepthEnd - firstElemInInterval.DepthStart;
|
|||
|
var drilledTime = (lastElemInInterval.DateEnd - firstElemInInterval.DateStart).TotalHours;
|
|||
|
|
|||
|
var aggregatedValues = CalcAggregate(span);
|
|||
|
|
|||
|
return new ProcessMapReportDataSaubStatDto()
|
|||
|
{
|
|||
|
DateStart = firstElemInInterval.DateStart.ToOffset(timeZone).DateTime,
|
|||
|
WellSectionTypeName = nearestOperation.WellSectionTypeName ?? string.Empty,
|
|||
|
DepthStart = firstElemInInterval.DepthStart,
|
|||
|
DepthEnd = lastElemInInterval.DepthEnd,
|
|||
|
DeltaDepth = deltaDepth,
|
|||
|
DrilledTime = drilledTime,
|
|||
|
DrillingMode = nearestOperation.CategoryName ?? string.Empty,
|
|||
|
PressureDiff = new ProcessMapReportDataSaubStatParamsDto()
|
|||
|
{
|
|||
|
SetpointPlanMax = processMapPlanFilteredByDepth.Max(p => p.DeltaPressurePlan),
|
|||
|
SetpointPlanMin = processMapPlanFilteredByDepth.Min(p => p.DeltaPressurePlan),
|
|||
|
SetpointFact = firstElemInInterval.PressureSp - firstElemInInterval.PressureIdle,
|
|||
|
FactWavg = aggregatedValues.Pressure,
|
|||
|
Limit = processMapPlanFilteredByDepth.Max(p => p.DeltaPressureLimitMax),
|
|||
|
SetpointUsage = aggregatedValues.SetpointUsagePressure
|
|||
|
},
|
|||
|
AxialLoad = new ProcessMapReportDataSaubStatParamsDto()
|
|||
|
{
|
|||
|
SetpointPlanMax = processMapPlanFilteredByDepth.Max(p => p.AxialLoadPlan),
|
|||
|
SetpointPlanMin = processMapPlanFilteredByDepth.Min(p => p.AxialLoadPlan),
|
|||
|
SetpointFact = aggregatedValues.AxialLoadSp,
|
|||
|
FactWavg = aggregatedValues.AxialLoad,
|
|||
|
Limit = processMapPlanFilteredByDepth.Max(p => p.AxialLoadLimitMax),
|
|||
|
SetpointUsage = aggregatedValues.SetpointUsageAxialLoad
|
|||
|
},
|
|||
|
TopDriveTorque = new ProcessMapReportDataSaubStatParamsDto()
|
|||
|
{
|
|||
|
SetpointPlanMax = processMapPlanFilteredByDepth.Max(p => p.TopDriveTorquePlan),
|
|||
|
SetpointPlanMin = processMapPlanFilteredByDepth.Min(p => p.TopDriveTorquePlan),
|
|||
|
SetpointFact = aggregatedValues.RotorTorqueSp,
|
|||
|
FactWavg = aggregatedValues.RotorTorque,
|
|||
|
FactMax = aggregatedValues.RotorTorqueMax,
|
|||
|
Limit = processMapPlanFilteredByDepth.Max(p => p.TopDriveTorqueLimitMax),
|
|||
|
SetpointUsage = aggregatedValues.SetpointUsageRotorTorque
|
|||
|
},
|
|||
|
SpeedLimit = new ProcessMapReportDataSaubStatParamsDto
|
|||
|
{
|
|||
|
SetpointPlanMax = processMapPlanFilteredByDepth.Max(p => p.RopPlan),
|
|||
|
SetpointPlanMin = processMapPlanFilteredByDepth.Min(p => p.RopPlan),
|
|||
|
SetpointFact = aggregatedValues.BlockSpeedSp,
|
|||
|
FactWavg = deltaDepth / drilledTime,
|
|||
|
SetpointUsage = aggregatedValues.SetpointUsageRopPlan
|
|||
|
},
|
|||
|
Turnover = new ProcessMapReportDataSaubStatParamsDto
|
|||
|
{
|
|||
|
SetpointPlanMax = processMapPlanFilteredByDepth.Max(p => p.TopDriveSpeedPlan),
|
|||
|
SetpointPlanMin = processMapPlanFilteredByDepth.Min(p => p.TopDriveSpeedPlan),
|
|||
|
FactWavg = aggregatedValues.RotorSpeed,
|
|||
|
FactMax = aggregatedValues.RotorSpeedMax
|
|||
|
},
|
|||
|
Flow = new ProcessMapReportDataSaubStatParamsDto
|
|||
|
{
|
|||
|
SetpointPlanMax = processMapPlanFilteredByDepth.Max(p => p.FlowPlan),
|
|||
|
SetpointPlanMin = processMapPlanFilteredByDepth.Min(p => p.FlowPlan),
|
|||
|
FactWavg = aggregatedValues.MaxFlow,
|
|||
|
Limit = processMapPlanFilteredByDepth.Max(p => p.FlowLimitMax),
|
|||
|
},
|
|||
|
Rop = new PlanFactDto<double?>
|
|||
|
{
|
|||
|
Plan = CalcRopPlan(processMapPlanFilteredByDepth),
|
|||
|
Fact = deltaDepth / drilledTime
|
|||
|
},
|
|||
|
};
|
|||
|
}
|
|||
|
|
|||
|
private (
|
|||
|
double Pressure,
|
|||
|
double AxialLoadSp,
|
|||
|
double AxialLoad,
|
|||
|
double RotorTorqueSp,
|
|||
|
double RotorTorque,
|
|||
|
double RotorTorqueMax,
|
|||
|
double BlockSpeedSp,
|
|||
|
double RotorSpeed,
|
|||
|
double RotorSpeedMax,
|
|||
|
double MaxFlow,
|
|||
|
double SetpointUsagePressure,
|
|||
|
double SetpointUsageAxialLoad,
|
|||
|
double SetpointUsageRotorTorque,
|
|||
|
double SetpointUsageRopPlan
|
|||
|
) CalcAggregate(Span<DataSaubStatDto> span)
|
|||
|
{
|
|||
|
var sumPressure = 0.0;
|
|||
|
var sumAxialLoadSp = 0.0;
|
|||
|
var sumAxialLoad = 0.0;
|
|||
|
var sumRotorTorqueSp = 0.0;
|
|||
|
var sumRotorTorque = 0.0;
|
|||
|
var sumBlockSpeedSp = 0.0;
|
|||
|
var sumRotorSpeed = 0.0;
|
|||
|
var maxFlow = 0.0;
|
|||
|
var maxRotorTorque = 0.0;
|
|||
|
var maxRotorSpeed = 0.0;
|
|||
|
var sumDiffDepthByPressure = 0.0;
|
|||
|
var sumDiffDepthByAxialLoad = 0.0;
|
|||
|
var sumDiffDepthByRotorTorque = 0.0;
|
|||
|
var sumDiffDepthByRopPlan = 0.0;
|
|||
|
|
|||
|
var diffDepthTotal = 0.0;
|
|||
|
for (var i = 0; i < span.Length; i++)
|
|||
|
{
|
|||
|
var diffDepth = span[i].DepthEnd - span[i].DepthStart;
|
|||
|
|
|||
|
sumPressure += diffDepth * span[i].Pressure;
|
|||
|
sumAxialLoadSp += diffDepth * (span[i].AxialLoadSp ?? 0);
|
|||
|
sumAxialLoad += diffDepth * span[i].AxialLoad;
|
|||
|
sumRotorTorqueSp += diffDepth * (span[i].RotorTorqueSp ?? 0);
|
|||
|
sumRotorTorque += diffDepth * span[i].RotorTorque;
|
|||
|
sumBlockSpeedSp += diffDepth * (span[i].BlockSpeedSp ?? 0);
|
|||
|
sumRotorSpeed += diffDepth * span[i].RotorSpeed;
|
|||
|
maxFlow = span[i].Flow > maxFlow ? span[i].Flow : maxFlow;
|
|||
|
maxRotorTorque = span[i].RotorTorque > maxRotorTorque ? span[i].RotorTorque : maxRotorTorque;
|
|||
|
maxRotorSpeed = span[i].RotorSpeed > maxRotorSpeed ? span[i].RotorSpeed : maxRotorSpeed;
|
|||
|
|
|||
|
if (span[i].IdFeedRegulator == LimitingParameterDto.Pressure)
|
|||
|
sumDiffDepthByPressure += span[i].DepthEnd - span[i].DepthStart;
|
|||
|
if (span[i].IdFeedRegulator == LimitingParameterDto.AxialLoad)
|
|||
|
sumDiffDepthByAxialLoad += span[i].DepthEnd - span[i].DepthStart;
|
|||
|
if (span[i].IdFeedRegulator == LimitingParameterDto.RotorTorque)
|
|||
|
sumDiffDepthByRotorTorque += span[i].DepthEnd - span[i].DepthStart;
|
|||
|
if (span[i].IdFeedRegulator == LimitingParameterDto.RopPlan)
|
|||
|
sumDiffDepthByRopPlan += span[i].DepthEnd - span[i].DepthStart;
|
|||
|
|
|||
|
diffDepthTotal += diffDepth;
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
return (
|
|||
|
Pressure: sumPressure / diffDepthTotal,
|
|||
|
AxialLoadSp: sumAxialLoadSp / diffDepthTotal,
|
|||
|
AxialLoad: sumAxialLoad / diffDepthTotal,
|
|||
|
RotorTorqueSp: sumRotorTorqueSp / diffDepthTotal,
|
|||
|
RotorTorque: sumRotorTorque / diffDepthTotal,
|
|||
|
RotorTorqueMax: maxRotorTorque,
|
|||
|
BlockSpeedSp: sumBlockSpeedSp / diffDepthTotal,
|
|||
|
RotorSpeed: sumRotorSpeed / diffDepthTotal,
|
|||
|
RotorSpeedMax: maxRotorSpeed,
|
|||
|
MaxFlow: maxFlow,
|
|||
|
SetpointUsagePressure: sumDiffDepthByPressure / diffDepthTotal,
|
|||
|
SetpointUsageAxialLoad: sumDiffDepthByAxialLoad / diffDepthTotal,
|
|||
|
SetpointUsageRotorTorque: sumDiffDepthByRotorTorque / diffDepthTotal,
|
|||
|
SetpointUsageRopPlan: sumDiffDepthByRopPlan / diffDepthTotal
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
private double CalcRopPlan(ProcessMapPlanDrillingDto[] processMapPlanFilteredByDepth)
|
|||
|
{
|
|||
|
var sumRopPlan = 0.0;
|
|||
|
var diffDepthTotal = 0.0;
|
|||
|
|
|||
|
for (var i = 0; i < processMapPlanFilteredByDepth.Length; i++)
|
|||
|
{
|
|||
|
var diffDepth = processMapPlanFilteredByDepth[i].DepthEnd - processMapPlanFilteredByDepth[i].DepthStart;
|
|||
|
sumRopPlan += diffDepth * processMapPlanFilteredByDepth[i].RopPlan;
|
|||
|
diffDepthTotal += diffDepth;
|
|||
|
}
|
|||
|
return sumRopPlan / diffDepthTotal;
|
|||
|
}
|
|||
|
|
|||
|
private bool IsNewInterval(DataSaubStatDto currentElem, DataSaubStatDto firstElem, DataSaubStatRequest request)
|
|||
|
{
|
|||
|
bool isNewElemBySpeed(double currentSpeed, double firstSpeed)
|
|||
|
{
|
|||
|
//2. Изменение уставки скорости подачи от первого значения в начале интервала при условии:
|
|||
|
//скорость > 80 м/ч => изменение уставки на ± 20 м/ч;
|
|||
|
//скорость > 30 м/ч => изменение уставки на ± 15 м/ч;
|
|||
|
//скорость <= 30 м/ч => изменение уставки на ± 5 м/ч;
|
|||
|
if (firstSpeed > 80)
|
|||
|
return Math.Abs(currentSpeed - firstSpeed) >= 20;
|
|||
|
else if (firstSpeed > 30)
|
|||
|
return Math.Abs(currentSpeed - firstSpeed) >= 15;
|
|||
|
else
|
|||
|
return Math.Abs(currentSpeed - firstSpeed) >= 5;
|
|||
|
}
|
|||
|
|
|||
|
var isNewElem = (currentElem.IdCategory != firstElem.IdCategory)
|
|||
|
|| (Math.Abs(currentElem.Pressure - firstElem.Pressure) >= request.DeltaPressure)
|
|||
|
|| (Math.Abs(currentElem.AxialLoad - firstElem.AxialLoad) >= request.DeltaAxialLoad)
|
|||
|
|| (Math.Abs(currentElem.RotorTorque - firstElem.RotorTorque) >= request.DeltaRotorTorque)
|
|||
|
|| (Math.Abs((currentElem.AxialLoadSp ?? 0) - (firstElem.AxialLoadSp ?? 0)) >= request.DeltaAxialLoadSp)
|
|||
|
|| (Math.Abs((currentElem.RotorTorqueSp ?? 0) - (firstElem.RotorTorqueSp ?? 0)) >= request.DeltaRotorTorqueSp)
|
|||
|
|| (isNewElemBySpeed(currentElem.Speed, firstElem.Speed));
|
|||
|
return isNewElem;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|