DD.WellWorkover.Cloud/AsbCloudInfrastructure/Background/PeriodicWorks/WorkDataSaubStat.cs

230 lines
9.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

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;
using AsbCloudApp.Data.SAUB;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Services.DetectOperations.Detectors;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Background.PeriodicWorks
{
/// <summary>
/// задача по добавлению данных в таблицу DataSaubStat, которая используется дл построения РТК-отчета
/// </summary>
internal class WorkDataSaubStat : Work
{
private int MechanicalDrillingCategoryId = 4001;
private int Gap = 60;
public WorkDataSaubStat() : base("Generate DataSaubStat entries and save them into Db")
{
Timeout = TimeSpan.FromMinutes(10);
}
protected override async Task Action(string id, IServiceProvider services, Action<string, double?> onProgressCallback, CancellationToken token)
{
using var db = services.GetRequiredService<IAsbCloudDbContext>();
var telemetryDataCache = services.GetRequiredService<ITelemetryDataCache<TelemetryDataSaubDto>>();
var cacheRequest = new TelemetryDataRequest()
{
GeDate = DateTime.UtcNow.AddDays(-Gap*100)
};
var idTelemetries = telemetryDataCache.GetIds(cacheRequest).ToArray();
if (!idTelemetries.Any())
return;
var dataSaubStatRepo = services.GetRequiredService<IDataSaubStatRepository>();
var stats = await dataSaubStatRepo.GetLastDatesAsync(idTelemetries, token);
for( var i =0; i < idTelemetries.Length; i++)
{
var idTelemetry = idTelemetries[i];
var lastDate = stats.FirstOrDefault(s => s.IdTelemetry == idTelemetry)?.DateEnd ?? DateTimeOffset.UnixEpoch;
var statsCount = await CreateStatForTelemetryFromDate(db, idTelemetry, lastDate, dataSaubStatRepo, token);
onProgressCallback($"Calculate stat for telemetry: {idTelemetry}; from {lastDate}; results count: {statsCount};", 100*i / idTelemetries.Length);
}
}
private async Task<int> CreateStatForTelemetryFromDate(IAsbCloudDbContext db, int idTelemetry, DateTimeOffset begin, IDataSaubStatRepository dataSaubStatRepo, CancellationToken token)
{
var detectedOperations = await db.Set<DetectedOperation>()
.Where(o => o.IdTelemetry == idTelemetry)
.Where(o => o.DateStart > begin)
.Where(o => o.OperationCategory.IdParent == MechanicalDrillingCategoryId)
.OrderBy(o => o.DateStart)
.Take(250)
.ToArrayAsync(token);
if (!detectedOperations.Any())
return 0;
var minDate = detectedOperations.First().DateStart;
var maxDate = detectedOperations.OrderByDescending(d => d.DateEnd).First().DateEnd;
var telemetryDataSaub = await db.Set<TelemetryDataSaub>()
.Where(t => t.IdTelemetry == idTelemetry)
.Where(t => t.DateTime >= minDate)
.Where(t => t.DateTime <= maxDate)
.Where(t => Math.Abs(t.BitDepth - t.WellDepth) < 0.0001)
.OrderBy(t => t.DateTime)
.Take(100_000)
.ToArrayAsync(token);
if (!telemetryDataSaub.Any())
return 0;
var dataSaubStats = CreateDataSaubStat(detectedOperations, telemetryDataSaub);
return await dataSaubStatRepo.InsertRangeAsync(dataSaubStats, token);
}
private static IEnumerable<DataSaubStatDto> CreateDataSaubStat(IEnumerable<DetectedOperation> detectedOperations, TelemetryDataSaub[] telemetryDataSaub)
{
var indexStart = 0;
var indexEnd = 0;
var result = new List<DataSaubStatDto>();
if (!telemetryDataSaub.Any())
return result;
foreach (var operation in detectedOperations)
{
indexStart = Array.FindIndex(telemetryDataSaub, indexEnd, t => t.DateTime >= operation.DateStart);
if (indexStart < 0)
break;
indexEnd = Array.FindIndex(telemetryDataSaub, indexStart, t => t.DateTime > operation.DateEnd);
if (indexEnd < 0)
indexEnd = telemetryDataSaub.Length - 1;
if (indexEnd == indexStart)
continue;
var length = indexEnd - indexStart;
var subset = telemetryDataSaub.AsSpan(indexStart, length);
var stats = CalcStats(operation, subset);
result.AddRange(stats);
}
return result;
}
private static IEnumerable<DataSaubStatDto> CalcStats(DetectedOperation operation, Span<TelemetryDataSaub> telemetryDataSaub)
{
var result = new List<DataSaubStatDto>();
var indexStart = 0;
for (var i = 1; i < telemetryDataSaub.Length; i++)
{
var previous = telemetryDataSaub[i - 1];
var current = telemetryDataSaub[i];
if (IsNewCacheItem(previous, current) || i == telemetryDataSaub.Length - 1)
{
var length = i - indexStart;
var span = telemetryDataSaub.Slice(indexStart, length);
indexStart = i;
if (length <= 2 || (span[^1].WellDepth - span[0].WellDepth) < 0.001)
continue; // мелкие выборки не учитываем.
var stat = CalcStat(operation, span);
result.Add(stat);
}
}
return result;
}
private static DataSaubStatDto CalcStat(DetectedOperation operation, Span<TelemetryDataSaub> span)
{
var hasOscillation = operation.ExtraData.TryGetValue(DetectorDrilling.ExtraDataKeyHasOscillation, out object? hasOscillationObject)
&& hasOscillationObject is true;
var aggregatedValues = CalcAggregate(span);
var processMapDrillingCacheItem = new DataSaubStatDto
{
DateStart = operation.DateStart,
DateEnd = operation.DateEnd,
DepthStart = operation.DepthStart,
DepthEnd = operation.DepthEnd,
Speed = (operation.DepthEnd - operation.DepthStart) / ((operation.DateEnd - operation.DateStart).TotalHours),
BlockSpeedSp = span[0].BlockSpeedSp,
Pressure = aggregatedValues.Pressure,
PressureIdle = span[0].PressureIdle,
PressureSp = span[0].PressureSp,
AxialLoad = aggregatedValues.AxialLoad,
AxialLoadSp = span[0].AxialLoadSp,
AxialLoadLimitMax = span[0].AxialLoadLimitMax,
RotorTorque = aggregatedValues.RotorTorque,
RotorTorqueSp = span[0].RotorTorqueSp,
RotorTorqueLimitMax = span[0].RotorTorqueLimitMax,
IdFeedRegulator = span[0].IdFeedRegulator,
RotorSpeed = aggregatedValues.RotorSpeed,
IdCategory = operation.IdCategory,
EnabledSubsystems = operation.EnabledSubsystems,
HasOscillation = hasOscillation,
IdTelemetry = operation.IdTelemetry,
Flow = aggregatedValues.Flow
};
return processMapDrillingCacheItem;
}
private static (
double Pressure,
double AxialLoad,
double RotorTorque,
double RotorSpeed,
double Flow
) CalcAggregate(Span<TelemetryDataSaub> span)
{
var sumPressure = 0.0;
var sumAxialLoad = 0.0;
var sumRotorTorque = 0.0;
var sumRotorSpeed = 0.0;
var flow = span[0].Flow ?? 0.0;
var diffDepthTotal = span[^1].WellDepth - span[0].WellDepth;
for (var i = 0; i < span.Length - 1; i++)
{
var diffDepth = span[i + 1].WellDepth - span[i].WellDepth;
sumPressure += diffDepth * span[i].Pressure;
sumAxialLoad += diffDepth * span[i].AxialLoad;
sumRotorTorque += diffDepth * span[i].RotorTorque;
sumRotorSpeed += diffDepth * span[i].RotorSpeed;
flow = span[i + 1].Flow > flow ? span[i + 1].Flow ?? 0.0 : flow;
}
return (
Pressure: sumPressure / diffDepthTotal,
AxialLoad: sumAxialLoad / diffDepthTotal,
RotorTorque: sumRotorTorque / diffDepthTotal,
RotorSpeed: sumRotorSpeed / diffDepthTotal,
Flow: flow
);
}
private static bool IsNewCacheItem(TelemetryDataSaub previous, TelemetryDataSaub current)
{
return !(current.Mode == previous.Mode)
|| !(current.WellDepth >= previous.WellDepth)
|| !(current.BlockSpeedSp == previous.BlockSpeedSp)
|| !(current.PressureIdle == previous.PressureIdle)
|| !(current.PressureSp == previous.PressureSp)
|| !(current.AxialLoadSp == previous.AxialLoadSp)
|| !(current.AxialLoadLimitMax == previous.AxialLoadLimitMax)
|| !(current.HookWeightIdle == previous.HookWeightIdle)
|| !(current.RotorTorqueIdle == previous.RotorTorqueIdle)
|| !(current.RotorTorqueSp == previous.RotorTorqueSp)
|| !(current.RotorTorqueLimitMax == previous.RotorTorqueLimitMax)
|| !(current.IdFeedRegulator == previous.IdFeedRegulator);
}
}
}