Построение РТК-отчета на основании данных t_data_saub_stat

This commit is contained in:
Olga Nemt 2024-02-12 16:55:50 +05:00
parent c33b7d086a
commit 4f2bc9244e
9 changed files with 448 additions and 18 deletions

View File

@ -8,6 +8,30 @@ namespace AsbCloudApp.Data
/// </summary>
public class LimitingParameterDto
{
/// <summary>
/// Нет ограничения
/// </summary>
public static int NoLimit = 0;
/// <summary>
/// МСП
/// </summary>
public static int RopPlan = 1;
/// <summary>
/// Давление
/// </summary>
public static int Pressure = 2;
/// <summary>
/// Осевая нагрузка
/// </summary>
public static int AxialLoad = 3;
/// <summary>
/// Момент
/// </summary>
public static int RotorTorque = 4;
/// <summary>
/// Идентификатор скважины
/// </summary>

View File

@ -10,6 +10,14 @@ namespace AsbCloudApp.Repositories
/// </summary>
public interface IDataSaubStatRepository
{
/// <summary>
/// Получение записей по ключу телеметрии
/// </summary>
/// <param name="idTelemetry">ключ телеметрии</param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<DataSaubStatDto>> GetAsync(int idTelemetry, CancellationToken token);
/// <summary>
/// Получение последних по дате окончания бурения записей в разрезе телеметрий
/// </summary>

View File

@ -0,0 +1,33 @@
namespace AsbCloudApp.Requests
{
/// <summary>
/// Параметры запроса для построения отчёта
/// </summary>
public class DataSaubStatRequest
{
/// <summary>
/// Изменение уставки факт перепада давления от первого значения в начале интервала
/// </summary>
public double DeltaPressure { get; set; }
/// <summary>
/// Изменение уставки факт осевой нагрузки от первого значения в начале интервала
/// </summary>
public double DeltaAxialLoad { get; set; }
/// <summary>
/// Изменение уставки момента от первого значения в начале интервала
/// </summary>
public double DeltaRotorTorque { get; set; }
/// <summary>
/// Изменение ограничения нагрузки от первого значения в начале интервала
/// </summary>
public double DeltaAxialLoadSp => 1.0;
/// <summary>
/// Изменение ограничения момента от первого значения в начале интервала
/// </summary>
public double DeltaRotorTorqueSp => 5.0;
}
}

View File

@ -0,0 +1,26 @@
using AsbCloudApp.Data.ProcessMaps.Report;
using AsbCloudApp.Requests;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudApp.Services.ProcessMaps.WellDrilling
{
/// <summary>
/// Получить РТК-отчет по бурению
/// </summary>
public interface IProcessMapReportDataSaubStatService
{
/// <summary>
/// Получения строк РТК-отчёта
/// </summary>
/// <param name="idWell">ключ скважины</param>
/// <param name="request">параметры запроса</param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<ProcessMapReportDataSaubStatDto>> GetAsync(int idWell, DataSaubStatRequest request, CancellationToken token);
}
}

View File

@ -228,6 +228,8 @@ namespace AsbCloudInfrastructure
IChangeLogRepository<ProcessMapPlanDrillingDto, ProcessMapPlanBaseRequestWithWell>,
ProcessMapPlanBaseRepository<ProcessMapPlanDrillingDto, ProcessMapPlanDrilling>>();
services.AddTransient<IProcessMapReportDataSaubStatService, ProcessMapReportDataSaubStatService>();
services.AddTransient<TrajectoryService>();
services.AddTransient<IGtrRepository, GtrWitsRepository>();

View File

@ -40,6 +40,19 @@ namespace AsbCloudInfrastructure.Repository
return result;
}
public async Task<IEnumerable<DataSaubStatDto>> GetAsync(int idTelemetry, CancellationToken token)
{
var timeSpan = TimeSpan.FromHours(telemetryService.GetTimezone(idTelemetry).Hours);
var stats = await db.Set<DataSaubStat>()
.Where(s => s.IdTelemetry == idTelemetry)
.ToArrayAsync(token);
var result = stats.Select(s => ConvertToDto(s, timeSpan));
return result;
}
public async Task<int> InsertRangeAsync(IEnumerable<DataSaubStatDto> dataSaubStats, CancellationToken token)
{
var entities = dataSaubStats.Select(data => ConvertToEntity(data));

View File

@ -17,11 +17,11 @@ namespace AsbCloudInfrastructure.Services
private readonly IWellService wellService;
private readonly Dictionary<int, string> feedRegulatorData = new ()
{
{ 0, "Нет ограничения" },
{ 1, "МСП" },
{ 2, "Давление" },
{ 3, "Осевая нагрузка" },
{ 4, "Момент" }
{ LimitingParameterDto.NoLimit, "Нет ограничения" },
{ LimitingParameterDto.RopPlan, "МСП" },
{ LimitingParameterDto.Pressure, "Давление" },
{ LimitingParameterDto.AxialLoad, "Осевая нагрузка" },
{ LimitingParameterDto.RotorTorque, "Момент" }
};
public LimitingParameterService(ILimitingParameterRepository limitingParameterRepository,

View File

@ -0,0 +1,318 @@
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;
}
}
}

View File

@ -1,11 +1,8 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Data;
using AsbCloudApp.Data.ProcessMaps;
using AsbCloudApp.Data.ProcessMaps.Report;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudApp.Services.ProcessMaps;
using AsbCloudApp.Services.ProcessMaps.WellDrilling;
@ -14,6 +11,11 @@ using AsbCloudWebApi.SignalR.Clients;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudWebApi.Controllers.ProcessMaps;
@ -23,6 +25,7 @@ namespace AsbCloudWebApi.Controllers.ProcessMaps;
public class ProcessMapWellDrillingController : ProcessMapBaseController<ProcessMapPlanWellDrillingDto>
{
private readonly IProcessMapReportWellDrillingService processMapReportWellDrillingService;
private readonly IProcessMapReportDataSaubStatService processMapReportDataSaubStatService;
private readonly IProcessMapReportWellDrillingExportService processMapReportWellDrillingExportService;
private readonly IProcessMapPlanImportService processMapPlanImportService;
@ -34,6 +37,7 @@ public class ProcessMapWellDrillingController : ProcessMapBaseController<Process
IProcessMapReportWellDrillingExportService processMapReportWellDrillingExportService,
IProcessMapPlanImportService processMapPlanImportService,
IProcessMapReportWellDrillingService processMapReportWellDrillingService,
IProcessMapReportDataSaubStatService processMapReportDataSaubStatService,
ICrudRepository<WellSectionTypeDto> wellSectionRepository,
IHubContext<TelemetryHub, ITelemetryHubClient> telemetryHubContext,
ITelemetryService telemetryService,
@ -42,6 +46,7 @@ public class ProcessMapWellDrillingController : ProcessMapBaseController<Process
{
this.processMapReportWellDrillingExportService = processMapReportWellDrillingExportService;
this.processMapPlanImportService = processMapPlanImportService;
this.processMapReportDataSaubStatService = processMapReportDataSaubStatService;
this.processMapReportWellDrillingService = processMapReportWellDrillingService;
}
@ -49,13 +54,14 @@ public class ProcessMapWellDrillingController : ProcessMapBaseController<Process
/// Получение данных для отчета РТК бурение
/// </summary>
/// <param name="idWell">Id</param>
/// <param name="request">параметры запроса</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpGet("report")]
[ProducesResponseType(typeof(IEnumerable<ProcessMapPlanWellDrillingDto>), StatusCodes.Status200OK)]
public async Task<IActionResult> GetReportAsync(int idWell, CancellationToken cancellationToken)
[ProducesResponseType(typeof(IEnumerable<ProcessMapReportDataSaubStatDto>), StatusCodes.Status200OK)]
public async Task<IActionResult> GetReportAsync(int idWell, DataSaubStatRequest request, CancellationToken cancellationToken)
{
var report = await processMapReportWellDrillingService.GetAsync(idWell, cancellationToken);
var report = await processMapReportDataSaubStatService.GetAsync(idWell, request, cancellationToken);
return Ok(report);
}