Split WorkSubsystemOperationTimeCalc to 2 background work for AKB and Oscillation

This commit is contained in:
ngfrolov 2023-11-28 17:32:14 +05:00
parent 75e7093af8
commit 409e1be983
Signed by untrusted user who does not match committer: ng.frolov
GPG Key ID: E99907A0357B29A7
10 changed files with 310 additions and 140 deletions

View File

@ -0,0 +1,30 @@
using System;
namespace AsbCloudInfrastructure.Services.SAUB
{
/// <summary>
/// Статистика данных телеметрии
/// </summary>
public class TelemetryDataStatDto
{
/// <summary>
/// ID в БД
/// </summary>
public int IdTelemetry { get; set; }
/// <summary>
/// дата получения первых данных
/// </summary>
public DateTimeOffset DateFirst { get; set; }
/// <summary>
/// дата получения последних полученных данных
/// </summary>
public DateTimeOffset DateLast { get; set; }
/// <summary>
/// смещение часового пояса
/// </summary>
public double TimezoneOffsetHours { get; set; }
}
}

View File

@ -1,5 +1,6 @@
using AsbCloudApp.Data;
using AsbCloudApp.Requests;
using AsbCloudInfrastructure.Services.SAUB;
using System;
using System.Collections.Generic;
@ -65,5 +66,11 @@ namespace AsbCloudApp.Repositories
/// <param name="idTelemetry"></param>
/// <returns></returns>
(TDto First, TDto Last)? GetOrDefaultFirstLast(int idTelemetry);
/// <summary>
/// статистика хранимой телеметрии по всем кешированым
/// </summary>
/// <returns></returns>
IEnumerable<TelemetryDataStatDto> GetStat();
}
}

View File

@ -9,7 +9,7 @@ using System.Threading.Tasks;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
namespace AsbCloudInfrastructure.Background;
namespace AsbCloudInfrastructure.Background.PeriodicWorks;
public class WorkLimitingParameterCalc : Work
{

View File

@ -0,0 +1,127 @@
using AsbCloudDb.Model;
using AsbCloudDb.Model.Subsystems;
using AsbCloudInfrastructure.Services.Subsystems;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Background.PeriodicWorks;
public class WorkSubsystemAbfOperationTimeCalc : WorkSubsystemOperationTimeCalcAbstract
{
public WorkSubsystemAbfOperationTimeCalc()
: base("Subsystem automated bit feeding operation time calc")
{
Timeout = TimeSpan.FromMinutes(30);
}
protected override async Task<IEnumerable<SubsystemOperationTime>> OperationTimeAsync(int idTelemetry, DateTimeOffset geDate, IAsbCloudDbContext db, CancellationToken token)
{
static bool isSubsytemAkbRotor(short? mode) => mode == 1;
static bool isSubsytemAkbSlide(short? mode) => mode == 3;
static bool IsSubsystemMse(short? state) => (state & 1) > 0;
var query =
$"select tt.date, tt.mode, tt.well_depth, tt.mse_state " +
$"from ( " +
$" select " +
$" date, " +
$" mode, " +
$" mse_state, " +
$" well_depth, " +
$" lag(mode,1) over (order by date) as mode_lag, " +
$" lead(mode,1) over (order by date) as mode_lead " +
$" from t_telemetry_data_saub " +
$" where id_telemetry = {idTelemetry} and well_depth is not null and well_depth > 0 " +
$" order by date ) as tt " +
$"where (tt.mode_lag is null or (tt.mode != tt.mode_lag and tt.mode_lead != tt.mode_lag)) and tt.date >= '{geDate:u}' " +
$"order by tt.date;";
using var result = await ExecuteReaderAsync(db, query, token);
var subsystemsOperationTimes = new List<SubsystemOperationTime>();
var detectorRotor = new SubsystemDetector(idTelemetry, idSubsystemAPDRotor, isSubsytemAkbRotor, IsValid);
var detectorSlide = new SubsystemDetector(idTelemetry, idSubsystemAPDSlide, isSubsytemAkbSlide, IsValid);
var detectorMse = new SubsystemDetector(idTelemetry, idSubsystemMse, IsSubsystemMse, IsValid);
while (result.Read())
{
var mode = result.GetFieldValue<short?>(1);
var state = result.GetFieldValue<short?>(3);
var isAkbRotorEnable = isSubsytemAkbRotor(mode);
var isAkbSlideEnable = isSubsytemAkbSlide(mode);
var isMseEnable = IsSubsystemMse(state);
var date = result.GetFieldValue<DateTimeOffset>(0);
var depth = result.GetFieldValue<float>(2);
if (detectorRotor.TryDetect(mode, date, depth, out var detectedRotor))
subsystemsOperationTimes.Add(detectedRotor!);
if (detectorSlide.TryDetect(mode, date, depth, out var detectedSlide))
subsystemsOperationTimes.Add(detectedSlide!);
if (detectorMse.TryDetect(mode, date, depth, out var detectedMse))
subsystemsOperationTimes.Add(detectedMse!);
}
return subsystemsOperationTimes;
}
private static async Task<DbDataReader> ExecuteReaderAsync(IAsbCloudDbContext db, string query, CancellationToken token)
{
var connection = db.Database.GetDbConnection();
if (
connection?.State is null ||
connection.State == ConnectionState.Broken ||
connection.State == ConnectionState.Closed)
{
await db.Database.OpenConnectionAsync(token);
connection = db.Database.GetDbConnection();
}
using var command = connection.CreateCommand();
command.CommandText = query;
var result = await command.ExecuteReaderAsync(token);
return result;
}
private static bool IsValid(SubsystemOperationTime item)
{
var validateCode = GetValidateErrorCode(item);
if (validateCode != 0)
{
var str = System.Text.Json.JsonSerializer.Serialize(item);
Trace.TraceWarning($"Wrong({validateCode}) SubsystemOperationTime: {str}");
}
return validateCode == 0;
}
private static int GetValidateErrorCode(SubsystemOperationTime item)
{
if (item.DateStart > item.DateEnd)
return -1;
if ((item.DateEnd - item.DateStart).TotalHours > 48)
return -2;
if (item.DepthEnd < item.DepthStart)
return -3;
if (item.DepthEnd - item.DepthStart > 2000d)
return -4;
if (item.DepthEnd < 0d)
return -5;
if (item.DepthStart < 0d)
return -6;
if (item.DepthEnd > 24_0000d)
return -7;
if (item.DepthStart > 24_0000d)
return -8;
return 0;
}
}

View File

@ -0,0 +1,102 @@
using AsbCloudApp.Data.SAUB;
using AsbCloudApp.Repositories;
using AsbCloudDb.Model;
using AsbCloudDb.Model.Subsystems;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Background.PeriodicWorks;
public abstract class WorkSubsystemOperationTimeCalcAbstract : Work
{
protected const int idSubsystemTorqueMaster = 65537;
protected const int idSubsystemSpinMaster = 65536;
protected const int idSubsystemAPDRotor = 11;
protected const int idSubsystemAPDSlide = 12;
protected const int idSubsystemMse = 2;
private static TimeSpan obsoleteTime = TimeSpan.FromDays(365 * 100);
public WorkSubsystemOperationTimeCalcAbstract(string workId)
: base(workId)
{
Timeout = TimeSpan.FromMinutes(30);
}
protected override async Task Action(string id, IServiceProvider services, Action<string, double?> onProgressCallback, CancellationToken token)
{
var db = services.GetRequiredService<IAsbCloudDbContext>();
db.Database.SetCommandTimeout(TimeSpan.FromMinutes(5));
var telemetryLastDetectedDates = await GetTelemetryLastDetectedDates(services, db, token);
var count = telemetryLastDetectedDates.Count();
var i = 0d;
foreach (var item in telemetryLastDetectedDates)
{
onProgressCallback($"Start handling telemetry: {item.IdTelemetry} from {item.DateDetectedLast}", i++ / count);
var newOperationsSaub = await OperationTimeAsync(item.IdTelemetry, item.DateDetectedLast, db, token);
if (newOperationsSaub.Any())
{
db.SubsystemOperationTimes.AddRange(newOperationsSaub);
await db.SaveChangesAsync(token);
}
}
obsoleteTime = TimeSpan.FromDays(3);
}
protected abstract Task<IEnumerable<SubsystemOperationTime>> OperationTimeAsync(int idTelemetry, DateTimeOffset geDate, IAsbCloudDbContext db, CancellationToken token);
private static async Task<IEnumerable<TelemetryDateLast>> GetTelemetryLastDetectedDates(IServiceProvider services, IAsbCloudDbContext db, CancellationToken token)
{
var telemetryDataCache = services.GetRequiredService<ITelemetryDataCache<TelemetryDataSaubDto>>();
var updatingTelemetries = telemetryDataCache.GetStat()
.Where(tstat => (DateTimeOffset.Now - tstat.DateLast) < obsoleteTime);
var telemetryIds = updatingTelemetries
.Select(t => t.IdTelemetry)
.ToArray();
IEnumerable<TelemetryDateLast> lastDetectedDates = await GetLastSubsystemOperationTimeAsync(db, token);
lastDetectedDates = lastDetectedDates
.Where(s => telemetryIds.Contains(s.IdTelemetry));
var result = updatingTelemetries.Select(tstat => new TelemetryDateLast
{
IdTelemetry = tstat.IdTelemetry,
DateDetectedLast = lastDetectedDates.FirstOrDefault(ldd => ldd.IdTelemetry == tstat.IdTelemetry)?.DateDetectedLast
?? DateTimeOffset.UnixEpoch,
DateTelemetryLast = tstat.DateLast
});
return result;
}
private static async Task<IEnumerable<TelemetryDateLast>> GetLastSubsystemOperationTimeAsync(IAsbCloudDbContext db, CancellationToken token)
{
var result = await db.SubsystemOperationTimes
.GroupBy(o => o.IdTelemetry)
.Select(g => new TelemetryDateLast
{
IdTelemetry = g.Key,
DateDetectedLast = g.Max(o => o.DateEnd)
})
.ToArrayAsync(token);
return result;
}
protected class TelemetryDateLast
{
public int IdTelemetry { get; set; }
public DateTimeOffset DateDetectedLast { get; set; }
public DateTimeOffset DateTelemetryLast { get; internal set; }
}
}

View File

@ -1,6 +1,7 @@
using AsbCloudDb.Model;
using AsbCloudDb.Model.Subsystems;
using AsbCloudInfrastructure.Background;
using AsbCloudInfrastructure.Services.Subsystems;
using AsbCloudInfrastructure.Services.Subsystems.Utils;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
@ -13,155 +14,26 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services.Subsystems;
namespace AsbCloudInfrastructure.Background.PeriodicWorks;
public class WorkSubsystemOperationTimeCalc : Work
public class WorkSubsystemOscillationOperationTimeCalc : WorkSubsystemOperationTimeCalcAbstract
{
private const int idSubsytemTorqueMaster = 65537;
private const int idSubsytemSpinMaster = 65536;
private const int idSubsystemAPDRotor = 11;
private const int idSubsystemAPDSlide = 12;
private const int idSubsytemMse = 2;
public WorkSubsystemOperationTimeCalc()
public WorkSubsystemOscillationOperationTimeCalc()
: base("Subsystem operation time calc")
{
Timeout = TimeSpan.FromMinutes(30);
}
protected override async Task Action(string id, IServiceProvider services, Action<string, double?> onProgressCallback, CancellationToken token)
{
using var db = services.GetRequiredService<IAsbCloudDbContext>();
db.Database.SetCommandTimeout(TimeSpan.FromMinutes(5));
var lastDetectedDates = await db.SubsystemOperationTimes
.GroupBy(o => o.IdTelemetry)
.Select(g => new
{
IdTelemetry = g.Key,
LastDate = g.Max(o => o.DateEnd)
})
.ToListAsync(token);
var telemetryIds = await db.Telemetries
.Where(t => t.Info != null && t.TimeZone != null)
.Select(t => t.Id)
.ToListAsync(token);
var telemetryLastDetectedDates = telemetryIds
.GroupJoin(lastDetectedDates,
t => t,
o => o.IdTelemetry,
(outer, inner) => new
{
IdTelemetry = outer,
inner.SingleOrDefault()?.LastDate,
})
.OrderByDescending(i => i.IdTelemetry);
var count = telemetryLastDetectedDates.Count();
var i = 0d;
foreach (var item in telemetryLastDetectedDates)
{
onProgressCallback($"Start handling telemetry: {item.IdTelemetry} from {item.LastDate}", i++ / count);
var newOperationsSaub = await OperationTimeSaubAsync(item.IdTelemetry, item.LastDate ?? DateTimeOffset.MinValue, db, token);
if (newOperationsSaub?.Any() == true)
{
db.SubsystemOperationTimes.AddRange(newOperationsSaub);
await db.SaveChangesAsync(token);
}
var newOperationsSpin = await OperationTimeSpinAsync(item.IdTelemetry, item.LastDate ?? DateTimeOffset.MinValue, db, token);
if (newOperationsSpin?.Any() == true)
{
db.SubsystemOperationTimes.AddRange(newOperationsSpin);
await db.SaveChangesAsync(token);
}
}
}
private static async Task<DbDataReader> ExecuteReaderAsync(IAsbCloudDbContext db, string query, CancellationToken token)
{
var connection = db.Database.GetDbConnection();
if (
connection?.State is null ||
connection.State == ConnectionState.Broken ||
connection.State == ConnectionState.Closed)
{
await db.Database.OpenConnectionAsync(token);
connection = db.Database.GetDbConnection();
}
using var command = connection.CreateCommand();
command.CommandText = query;
var result = await command.ExecuteReaderAsync(token);
return result;
}
private static async Task<IEnumerable<SubsystemOperationTime>> OperationTimeSaubAsync(int idTelemetry, DateTimeOffset begin, IAsbCloudDbContext db, CancellationToken token)
{
static bool isSubsytemAkbRotor(short? mode) => mode == 1;
static bool isSubsytemAkbSlide(short? mode) => mode == 3;
static bool IsSubsystemMse(short? state) => (state & 1) > 0;
var query =
$"select tt.date, tt.mode, tt.well_depth, tt.mse_state " +
$"from ( " +
$" select " +
$" date, " +
$" mode, " +
$" mse_state, " +
$" well_depth, " +
$" lag(mode,1) over (order by date) as mode_lag, " +
$" lead(mode,1) over (order by date) as mode_lead " +
$" from t_telemetry_data_saub " +
$" where id_telemetry = {idTelemetry} and well_depth is not null and well_depth > 0 " +
$" order by date ) as tt " +
$"where (tt.mode_lag is null or (tt.mode != tt.mode_lag and tt.mode_lead != tt.mode_lag)) and tt.date >= '{begin:u}' " +
$"order by tt.date;";
using var result = await ExecuteReaderAsync(db, query, token);
var subsystemsOperationTimes = new List<SubsystemOperationTime>();
var detectorRotor = new SubsystemDetector(idTelemetry, idSubsystemAPDRotor, isSubsytemAkbRotor, IsValid);
var detectorSlide = new SubsystemDetector(idTelemetry, idSubsystemAPDSlide, isSubsytemAkbSlide, IsValid);
var detectorMse = new SubsystemDetector(idTelemetry, idSubsytemMse, IsSubsystemMse, IsValid);
while (result.Read())
{
var mode = result.GetFieldValue<short?>(1);
var state = result.GetFieldValue<short?>(3);
var isAkbRotorEnable = isSubsytemAkbRotor(mode);
var isAkbSlideEnable = isSubsytemAkbSlide(mode);
var isMseEnable = IsSubsystemMse(state);
var date = result.GetFieldValue<DateTimeOffset>(0);
var depth = result.GetFieldValue<float>(2);
if (detectorRotor.TryDetect(mode, date, depth, out var detectedRotor))
subsystemsOperationTimes.Add(detectedRotor!);
if (detectorSlide.TryDetect(mode, date, depth, out var detectedSlide))
subsystemsOperationTimes.Add(detectedSlide!);
if (detectorMse.TryDetect(mode, date, depth, out var detectedMse))
subsystemsOperationTimes.Add(detectedMse!);
}
return subsystemsOperationTimes;
}
private static async Task<IEnumerable<SubsystemOperationTime>> OperationTimeSpinAsync(int idTelemetry, DateTimeOffset begin, IAsbCloudDbContext db, CancellationToken token)
protected override async Task<IEnumerable<SubsystemOperationTime>> OperationTimeAsync(int idTelemetry, DateTimeOffset geDate, IAsbCloudDbContext db, CancellationToken token)
{
static int? GetSubsytemId(short? mode, int? state)
{
// При изменении следующего кода сообщи в Vladimir.Sobolev@nedra.digital
if (state == 7 && (mode & 2) > 0)
return idSubsytemTorqueMaster;// демпфер
return idSubsystemTorqueMaster;// демпфер
if (state != 0 && state != 5 && state != 6 && state != 7)
return idSubsytemSpinMaster;// осцилляция
return idSubsystemSpinMaster;// осцилляция
return null;
}
@ -180,7 +52,7 @@ public class WorkSubsystemOperationTimeCalc : Work
$" state, " +
$" lag(state, 1) over (order by date) as state_lag " +
$" from t_telemetry_data_spin " +
$" where id_telemetry = {idTelemetry} and date >= '{begin:u}'" +
$" where id_telemetry = {idTelemetry} and date >= '{geDate:u}'" +
$" order by date ) as tspin " +
$"where mode_lag is null or state_lag is null or (mode != mode_lag and mode_lead != mode_lag) or state != state_lag " +
$"order by date;";
@ -239,6 +111,24 @@ public class WorkSubsystemOperationTimeCalc : Work
return subsystemsOperationTimes;
}
private static async Task<DbDataReader> ExecuteReaderAsync(IAsbCloudDbContext db, string query, CancellationToken token)
{
var connection = db.Database.GetDbConnection();
if (
connection?.State is null ||
connection.State == ConnectionState.Broken ||
connection.State == ConnectionState.Closed)
{
await db.Database.OpenConnectionAsync(token);
connection = db.Database.GetDbConnection();
}
using var command = connection.CreateCommand();
command.CommandText = query;
var result = await command.ExecuteReaderAsync(token);
return result;
}
private static bool IsValid(SubsystemOperationTime item)
{
var validateCode = GetValidateErrorCode(item);

View File

@ -4,7 +4,7 @@ using System;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Background
namespace AsbCloudInfrastructure.Background.PeriodicWorks
{
/// <summary>
/// Задача по удалению загруженных отчетов

View File

@ -124,6 +124,18 @@ namespace AsbCloudInfrastructure.Services.SAUB
return items;
}
public IEnumerable<TelemetryDataStatDto> GetStat()
{
var result = caches.Select(cacheItem => new TelemetryDataStatDto
{
IdTelemetry = cacheItem.Key,
DateFirst = cacheItem.Value.FirstByDate.DateTime,
DateLast = cacheItem.Value.LastData[^1].DateTime,
TimezoneOffsetHours = cacheItem.Value.TimezoneHours,
});
return result;
}
public virtual TDto? GetLastOrDefault(int idTelemetry)
{
if (!caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? cacheItem))

View File

@ -18,6 +18,7 @@ using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services.Subsystems;
/// Todo: Выделить репозиторий
internal class SubsystemOperationTimeService : ISubsystemOperationTimeService
{
private readonly IAsbCloudDbContext db;

View File

@ -8,9 +8,9 @@ using System.Threading.Tasks;
using System.Threading;
using AsbCloudInfrastructure.Background;
using AsbCloudApp.Data.SAUB;
using AsbCloudInfrastructure.Services.Subsystems;
using AsbCloudDb;
using AsbCloudApp.Repositories;
using AsbCloudInfrastructure.Background.PeriodicWorks;
namespace AsbCloudInfrastructure
{
@ -32,7 +32,8 @@ namespace AsbCloudInfrastructure
backgroundWorker.Add<WorkToDeleteOldReports>(TimeSpan.FromDays(1));
backgroundWorker.Add<WellInfoService.WorkWellInfoUpdate>(TimeSpan.FromMinutes(30));
backgroundWorker.Add<WorkOperationDetection>(TimeSpan.FromMinutes(15));
backgroundWorker.Add<WorkSubsystemOperationTimeCalc>(TimeSpan.FromMinutes(30));
backgroundWorker.Add<WorkSubsystemAbfOperationTimeCalc>(TimeSpan.FromMinutes(30));
backgroundWorker.Add<WorkSubsystemOscillationOperationTimeCalc>(TimeSpan.FromMinutes(30));
backgroundWorker.Add<WorkLimitingParameterCalc>(TimeSpan.FromMinutes(30));
backgroundWorker.Add(MakeMemoryMonitoringWork(), TimeSpan.FromMinutes(1));