From dd99497e75f49de1a4d5c2a297586cd77d82374f Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Tue, 28 Nov 2023 16:31:31 +0500 Subject: [PATCH 1/4] Fix PeriodicBackgroundWorker --- .../Background/PeriodicBackgroundWorker.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/AsbCloudInfrastructure/Background/PeriodicBackgroundWorker.cs b/AsbCloudInfrastructure/Background/PeriodicBackgroundWorker.cs index dedd6d83..edc3aa8e 100644 --- a/AsbCloudInfrastructure/Background/PeriodicBackgroundWorker.cs +++ b/AsbCloudInfrastructure/Background/PeriodicBackgroundWorker.cs @@ -17,8 +17,8 @@ public class PeriodicBackgroundWorker : BackgroundService private readonly TimeSpan executePeriod = TimeSpan.FromSeconds(10); private readonly TimeSpan minDelay = TimeSpan.FromSeconds(1); private readonly IServiceProvider serviceProvider; - private readonly List works = new(8); + private bool isRuning = false; /// /// Список периодических работ @@ -42,6 +42,9 @@ public class PeriodicBackgroundWorker : BackgroundService protected override async Task ExecuteAsync(CancellationToken token) { + if (isRuning) + return; + isRuning = true; Trace.TraceInformation($"{GetType().Name} started"); while (!token.IsCancellationRequested) { @@ -74,6 +77,7 @@ public class PeriodicBackgroundWorker : BackgroundService Trace.TraceError(MainLoopLastException); } } + isRuning = false; } /// From 75e7093af869837989b96b73a8f86dfb983a6c3f Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Tue, 28 Nov 2023 16:57:01 +0500 Subject: [PATCH 2/4] remove ISubsystemService (not used) --- .../Services/Subsystems/ISubsystemService.cs | 23 -------- AsbCloudInfrastructure/DependencyInjection.cs | 6 +-- .../Services/Subsystems/SubsystemService.cs | 54 ------------------- .../SubsystemOperationTimeController.cs | 29 +++------- 4 files changed, 8 insertions(+), 104 deletions(-) delete mode 100644 AsbCloudApp/Services/Subsystems/ISubsystemService.cs delete mode 100644 AsbCloudInfrastructure/Services/Subsystems/SubsystemService.cs diff --git a/AsbCloudApp/Services/Subsystems/ISubsystemService.cs b/AsbCloudApp/Services/Subsystems/ISubsystemService.cs deleted file mode 100644 index b343b055..00000000 --- a/AsbCloudApp/Services/Subsystems/ISubsystemService.cs +++ /dev/null @@ -1,23 +0,0 @@ -using AsbCloudApp.Data.Subsystems; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace AsbCloudApp.Services.Subsystems -{ - // TODO: move this to repositories - - /// - /// репозиторий получения подсистем - /// - public interface ISubsystemService - { - /// - /// получение списка подсистем. Если скважина указана, то получим только использованные в скважине подсистемы. - /// - /// - /// - /// - Task?> GetSubsystemAsync(int? idWell, CancellationToken token); - } -} diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index 62e05164..e9d9c4e7 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -267,11 +267,9 @@ namespace AsbCloudInfrastructure services.AddTransient(); services.AddTransient, CrudCacheRepositoryBase>(); - - // Subsystem service + services.AddTransient, CrudCacheRepositoryBase>(); - services.AddTransient(); - + services.AddTransient, CrudCacheRepositoryBase>(); // TelemetryData services diff --git a/AsbCloudInfrastructure/Services/Subsystems/SubsystemService.cs b/AsbCloudInfrastructure/Services/Subsystems/SubsystemService.cs deleted file mode 100644 index 217e9d26..00000000 --- a/AsbCloudInfrastructure/Services/Subsystems/SubsystemService.cs +++ /dev/null @@ -1,54 +0,0 @@ -using AsbCloudApp.Data.Subsystems; -using AsbCloudApp.Services; -using AsbCloudApp.Services.Subsystems; -using AsbCloudDb.Model; -using AsbCloudDb.Model.Subsystems; -using AsbCloudInfrastructure.Repository; -using Mapster; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Caching.Memory; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace AsbCloudInfrastructure.Services.Subsystems -{ - - internal class SubsystemService : CrudCacheRepositoryBase, ISubsystemService - { - private readonly IWellService wellService; - public SubsystemService(IAsbCloudDbContext dbContext, IMemoryCache memoryCache, IWellService wellService) : base(dbContext, memoryCache) - { - this.wellService = wellService; - } - - private async Task?> GetSubsystemByIdWellAsync(int idWell, CancellationToken token) - { - var well = await wellService.GetOrDefaultAsync(idWell, token); - if (well?.IdTelemetry is null || well.Timezone is null) - return null; - var entities = await dbContext.SubsystemOperationTimes - .Include(e => e.Subsystem) - .AsNoTracking() - .Where(o => o.IdTelemetry == well.IdTelemetry) - .Select(o => o.Subsystem) - .Distinct() - .ToArrayAsync(token); - var dtos = entities.Select(e => e.Adapt()); - return dtos; - } - - public async Task?> GetSubsystemAsync(int? idWell, CancellationToken token) - { - if (idWell.HasValue) - { - var subsystemWell = await GetSubsystemByIdWellAsync(idWell.Value, token); - return subsystemWell; - } - var subsystem = await GetAllAsync(token); - return subsystem; - } - } - -} diff --git a/AsbCloudWebApi/Controllers/Subsystems/SubsystemOperationTimeController.cs b/AsbCloudWebApi/Controllers/Subsystems/SubsystemOperationTimeController.cs index d5bf4aae..47ce3630 100644 --- a/AsbCloudWebApi/Controllers/Subsystems/SubsystemOperationTimeController.cs +++ b/AsbCloudWebApi/Controllers/Subsystems/SubsystemOperationTimeController.cs @@ -24,7 +24,6 @@ namespace AsbCloudWebApi.Controllers.Subsystems private readonly ISubsystemOperationTimeService subsystemOperationTimeService; private readonly ITelemetryDataSaubService telemetryDataSaubService; private readonly IWellService wellService; - private readonly ISubsystemService subsystemService; private readonly Dictionary subsystemNames = new() { @@ -36,12 +35,10 @@ namespace AsbCloudWebApi.Controllers.Subsystems public SubsystemOperationTimeController( ISubsystemOperationTimeService subsystemOperationTimeService, IWellService wellService, - ISubsystemService subsystemService, ITelemetryDataSaubService telemetryDataSaubService) { this.subsystemOperationTimeService = subsystemOperationTimeService; this.wellService = wellService; - this.subsystemService = subsystemService; this.telemetryDataSaubService = telemetryDataSaubService; } /// @@ -52,7 +49,7 @@ namespace AsbCloudWebApi.Controllers.Subsystems [ProducesResponseType(typeof(ValidationProblemDetails), (int)System.Net.HttpStatusCode.BadRequest)] public async Task GetStatAsync([FromQuery] SubsystemOperationTimeRequest request, CancellationToken token) { - if (!await UserHasAccesToWellAsync(request.IdWell, token)) + if (!await UserHasAccessToWellAsync(request.IdWell, token)) return Forbid(); var subsystemResult = await subsystemOperationTimeService.GetStatAsync(request, token); return Ok(subsystemResult); @@ -65,7 +62,7 @@ namespace AsbCloudWebApi.Controllers.Subsystems [ProducesResponseType(typeof(DatesRangeDto), (int)System.Net.HttpStatusCode.OK)] public async Task GetStatDateRangeAsync([FromRoute] int idWell, CancellationToken token) { - if (!await UserHasAccesToWellAsync(idWell, token)) + if (!await UserHasAccessToWellAsync(idWell, token)) return Forbid(); var dateRange = telemetryDataSaubService.GetRange(idWell); @@ -91,20 +88,6 @@ namespace AsbCloudWebApi.Controllers.Subsystems return Ok(subsystemResult); } - /// - /// получить список подсистем общий. - /// - [HttpGet("subsystem")] - [ProducesResponseType(typeof(IEnumerable), (int)System.Net.HttpStatusCode.OK)] - public async Task GetSubsystemAsync([FromQuery] int? idWell, CancellationToken token) - { - if (idWell.HasValue) - if (!await UserHasAccesToWellAsync(idWell.Value, token)) - return Forbid(); - var result = await subsystemService.GetSubsystemAsync(idWell, token); - return Ok(result); - } - /// /// получить доступный диапазон дат наработки подсистемы. /// @@ -112,7 +95,7 @@ namespace AsbCloudWebApi.Controllers.Subsystems [ProducesResponseType(typeof(DatesRangeDto), (int)System.Net.HttpStatusCode.OK)] public async Task GetDateRangeOperationTimeAsync([FromQuery] SubsystemOperationTimeRequest request, CancellationToken token) { - if (!await UserHasAccesToWellAsync(request.IdWell, token)) + if (!await UserHasAccessToWellAsync(request.IdWell, token)) return Forbid(); var result = await subsystemOperationTimeService.GetDateRangeOperationTimeAsync(request, token); return Ok(result); @@ -128,7 +111,7 @@ namespace AsbCloudWebApi.Controllers.Subsystems [FromQuery] SubsystemOperationTimeRequest request, CancellationToken token) { - if (!await UserHasAccesToWellAsync(request.IdWell, token)) + if (!await UserHasAccessToWellAsync(request.IdWell, token)) return Forbid(); var result = await subsystemOperationTimeService.GetOperationTimeAsync(request, token); @@ -149,7 +132,7 @@ namespace AsbCloudWebApi.Controllers.Subsystems [FromQuery] SubsystemOperationTimeRequest request, CancellationToken token) { - if (!await UserHasAccesToWellAsync(request.IdWell, token)) + if (!await UserHasAccessToWellAsync(request.IdWell, token)) return Forbid(); var result = await subsystemOperationTimeService.DeleteAsync(request, token); return Ok(result); @@ -166,7 +149,7 @@ namespace AsbCloudWebApi.Controllers.Subsystems return Ok(subsystemNames); } - protected async Task UserHasAccesToWellAsync(int idWell, CancellationToken token) + protected async Task UserHasAccessToWellAsync(int idWell, CancellationToken token) { var idCompany = User.GetCompanyId(); if (idCompany is not null && From 409e1be983d77c6ebc1989a878aaa690758062ef Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Tue, 28 Nov 2023 17:32:14 +0500 Subject: [PATCH 3/4] Split WorkSubsystemOperationTimeCalc to 2 background work for AKB and Oscillation --- AsbCloudApp/Data/SAUB/TelemetryDataStatDto.cs | 30 ++++ .../Repositories/ITelemetryDataCache.cs | 7 + .../WorkLimitingParameterCalc.cs | 2 +- .../WorkSubsystemAbfOperationTimeCalc.cs | 127 ++++++++++++++ .../WorkSubsystemOperationTimeCalcAbstract.cs | 102 +++++++++++ ...kSubsystemOscillationOperationTimeCalc.cs} | 162 +++--------------- .../WorkToDeleteOldReports.cs | 2 +- .../Services/SAUB/TelemetryDataCache.cs | 12 ++ .../SubsystemOperationTimeService.cs | 1 + AsbCloudInfrastructure/Startup.cs | 5 +- 10 files changed, 310 insertions(+), 140 deletions(-) create mode 100644 AsbCloudApp/Data/SAUB/TelemetryDataStatDto.cs rename AsbCloudInfrastructure/Background/{ => PeriodicWorks}/WorkLimitingParameterCalc.cs (98%) create mode 100644 AsbCloudInfrastructure/Background/PeriodicWorks/WorkSubsystemAbfOperationTimeCalc.cs create mode 100644 AsbCloudInfrastructure/Background/PeriodicWorks/WorkSubsystemOperationTimeCalcAbstract.cs rename AsbCloudInfrastructure/{Services/Subsystems/WorkSubsystemOperationTimeCalc.cs => Background/PeriodicWorks/WorkSubsystemOscillationOperationTimeCalc.cs} (54%) rename AsbCloudInfrastructure/Background/{ => PeriodicWorks}/WorkToDeleteOldReports.cs (95%) diff --git a/AsbCloudApp/Data/SAUB/TelemetryDataStatDto.cs b/AsbCloudApp/Data/SAUB/TelemetryDataStatDto.cs new file mode 100644 index 00000000..c9eab606 --- /dev/null +++ b/AsbCloudApp/Data/SAUB/TelemetryDataStatDto.cs @@ -0,0 +1,30 @@ +using System; + +namespace AsbCloudInfrastructure.Services.SAUB +{ + /// + /// Статистика данных телеметрии + /// + public class TelemetryDataStatDto + { + /// + /// ID в БД + /// + public int IdTelemetry { get; set; } + + /// + /// дата получения первых данных + /// + public DateTimeOffset DateFirst { get; set; } + + /// + /// дата получения последних полученных данных + /// + public DateTimeOffset DateLast { get; set; } + + /// + /// смещение часового пояса + /// + public double TimezoneOffsetHours { get; set; } + } +} \ No newline at end of file diff --git a/AsbCloudApp/Repositories/ITelemetryDataCache.cs b/AsbCloudApp/Repositories/ITelemetryDataCache.cs index 78f7008b..c31a0fbd 100644 --- a/AsbCloudApp/Repositories/ITelemetryDataCache.cs +++ b/AsbCloudApp/Repositories/ITelemetryDataCache.cs @@ -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 /// /// (TDto First, TDto Last)? GetOrDefaultFirstLast(int idTelemetry); + + /// + /// статистика хранимой телеметрии по всем кешированым + /// + /// + IEnumerable GetStat(); } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Background/WorkLimitingParameterCalc.cs b/AsbCloudInfrastructure/Background/PeriodicWorks/WorkLimitingParameterCalc.cs similarity index 98% rename from AsbCloudInfrastructure/Background/WorkLimitingParameterCalc.cs rename to AsbCloudInfrastructure/Background/PeriodicWorks/WorkLimitingParameterCalc.cs index c0d6eb48..9e6f3629 100644 --- a/AsbCloudInfrastructure/Background/WorkLimitingParameterCalc.cs +++ b/AsbCloudInfrastructure/Background/PeriodicWorks/WorkLimitingParameterCalc.cs @@ -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 { diff --git a/AsbCloudInfrastructure/Background/PeriodicWorks/WorkSubsystemAbfOperationTimeCalc.cs b/AsbCloudInfrastructure/Background/PeriodicWorks/WorkSubsystemAbfOperationTimeCalc.cs new file mode 100644 index 00000000..fb16781d --- /dev/null +++ b/AsbCloudInfrastructure/Background/PeriodicWorks/WorkSubsystemAbfOperationTimeCalc.cs @@ -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> 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(); + 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(1); + var state = result.GetFieldValue(3); + + var isAkbRotorEnable = isSubsytemAkbRotor(mode); + var isAkbSlideEnable = isSubsytemAkbSlide(mode); + var isMseEnable = IsSubsystemMse(state); + var date = result.GetFieldValue(0); + var depth = result.GetFieldValue(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 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; + } +} diff --git a/AsbCloudInfrastructure/Background/PeriodicWorks/WorkSubsystemOperationTimeCalcAbstract.cs b/AsbCloudInfrastructure/Background/PeriodicWorks/WorkSubsystemOperationTimeCalcAbstract.cs new file mode 100644 index 00000000..b9fb6d23 --- /dev/null +++ b/AsbCloudInfrastructure/Background/PeriodicWorks/WorkSubsystemOperationTimeCalcAbstract.cs @@ -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 onProgressCallback, CancellationToken token) + { + var db = services.GetRequiredService(); + 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> OperationTimeAsync(int idTelemetry, DateTimeOffset geDate, IAsbCloudDbContext db, CancellationToken token); + + private static async Task> GetTelemetryLastDetectedDates(IServiceProvider services, IAsbCloudDbContext db, CancellationToken token) + { + var telemetryDataCache = services.GetRequiredService>(); + + var updatingTelemetries = telemetryDataCache.GetStat() + .Where(tstat => (DateTimeOffset.Now - tstat.DateLast) < obsoleteTime); + + var telemetryIds = updatingTelemetries + .Select(t => t.IdTelemetry) + .ToArray(); + + IEnumerable 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> 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; } + } +} diff --git a/AsbCloudInfrastructure/Services/Subsystems/WorkSubsystemOperationTimeCalc.cs b/AsbCloudInfrastructure/Background/PeriodicWorks/WorkSubsystemOscillationOperationTimeCalc.cs similarity index 54% rename from AsbCloudInfrastructure/Services/Subsystems/WorkSubsystemOperationTimeCalc.cs rename to AsbCloudInfrastructure/Background/PeriodicWorks/WorkSubsystemOscillationOperationTimeCalc.cs index 87749caf..1135385c 100644 --- a/AsbCloudInfrastructure/Services/Subsystems/WorkSubsystemOperationTimeCalc.cs +++ b/AsbCloudInfrastructure/Background/PeriodicWorks/WorkSubsystemOscillationOperationTimeCalc.cs @@ -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 onProgressCallback, CancellationToken token) - { - using var db = services.GetRequiredService(); - 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 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> 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(); - 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(1); - var state = result.GetFieldValue(3); - - var isAkbRotorEnable = isSubsytemAkbRotor(mode); - var isAkbSlideEnable = isSubsytemAkbSlide(mode); - var isMseEnable = IsSubsystemMse(state); - var date = result.GetFieldValue(0); - var depth = result.GetFieldValue(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> OperationTimeSpinAsync(int idTelemetry, DateTimeOffset begin, IAsbCloudDbContext db, CancellationToken token) + protected override async Task> 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 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); diff --git a/AsbCloudInfrastructure/Background/WorkToDeleteOldReports.cs b/AsbCloudInfrastructure/Background/PeriodicWorks/WorkToDeleteOldReports.cs similarity index 95% rename from AsbCloudInfrastructure/Background/WorkToDeleteOldReports.cs rename to AsbCloudInfrastructure/Background/PeriodicWorks/WorkToDeleteOldReports.cs index fb49caab..adb70d32 100644 --- a/AsbCloudInfrastructure/Background/WorkToDeleteOldReports.cs +++ b/AsbCloudInfrastructure/Background/PeriodicWorks/WorkToDeleteOldReports.cs @@ -4,7 +4,7 @@ using System; using System.Threading; using System.Threading.Tasks; -namespace AsbCloudInfrastructure.Background +namespace AsbCloudInfrastructure.Background.PeriodicWorks { /// /// Задача по удалению загруженных отчетов diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataCache.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataCache.cs index 3a6eac6f..4fefef5a 100644 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataCache.cs +++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataCache.cs @@ -124,6 +124,18 @@ namespace AsbCloudInfrastructure.Services.SAUB return items; } + public IEnumerable 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)) diff --git a/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs b/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs index e40c1a4d..8164da4c 100644 --- a/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs +++ b/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs @@ -18,6 +18,7 @@ using System.Threading.Tasks; namespace AsbCloudInfrastructure.Services.Subsystems; +/// Todo: Выделить репозиторий internal class SubsystemOperationTimeService : ISubsystemOperationTimeService { private readonly IAsbCloudDbContext db; diff --git a/AsbCloudInfrastructure/Startup.cs b/AsbCloudInfrastructure/Startup.cs index 893a3237..115c1f88 100644 --- a/AsbCloudInfrastructure/Startup.cs +++ b/AsbCloudInfrastructure/Startup.cs @@ -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(TimeSpan.FromDays(1)); backgroundWorker.Add(TimeSpan.FromMinutes(30)); backgroundWorker.Add(TimeSpan.FromMinutes(15)); - backgroundWorker.Add(TimeSpan.FromMinutes(30)); + backgroundWorker.Add(TimeSpan.FromMinutes(30)); + backgroundWorker.Add(TimeSpan.FromMinutes(30)); backgroundWorker.Add(TimeSpan.FromMinutes(30)); backgroundWorker.Add(MakeMemoryMonitoringWork(), TimeSpan.FromMinutes(1)); From 15d329999b636a1c2f123a1fb0c9e1354fd72b96 Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Wed, 29 Nov 2023 09:02:26 +0500 Subject: [PATCH 4/4] fix BackgroundWorker single thread --- AsbCloudInfrastructure/Background/BackgroundWorker.cs | 7 ++++++- .../Background/PeriodicBackgroundWorker.cs | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/AsbCloudInfrastructure/Background/BackgroundWorker.cs b/AsbCloudInfrastructure/Background/BackgroundWorker.cs index 825f1b66..1c641019 100644 --- a/AsbCloudInfrastructure/Background/BackgroundWorker.cs +++ b/AsbCloudInfrastructure/Background/BackgroundWorker.cs @@ -31,6 +31,7 @@ public class BackgroundWorker : BackgroundService /// Работа выполняемая в данный момент /// public Work? CurrentWork; + private bool isRuning; /// /// последние 16 завершившиеся с ошибкой @@ -54,6 +55,9 @@ public class BackgroundWorker : BackgroundService protected override async Task ExecuteAsync(CancellationToken token) { + if (isRuning) + return; + isRuning = true; Trace.TraceInformation($"{GetType().Name} started"); while (!token.IsCancellationRequested && works.TryDequeue(out CurrentWork)) { @@ -82,6 +86,7 @@ public class BackgroundWorker : BackgroundService Trace.TraceError(MainLoopLastException); } } + isRuning = false; } /// @@ -96,7 +101,7 @@ public class BackgroundWorker : BackgroundService { works.Enqueue(work); if (ExecuteTask is null || ExecuteTask.IsCompleted) - StartAsync(CancellationToken.None).Wait(); + StartAsync(CancellationToken.None); } /// diff --git a/AsbCloudInfrastructure/Background/PeriodicBackgroundWorker.cs b/AsbCloudInfrastructure/Background/PeriodicBackgroundWorker.cs index edc3aa8e..0a7e96dd 100644 --- a/AsbCloudInfrastructure/Background/PeriodicBackgroundWorker.cs +++ b/AsbCloudInfrastructure/Background/PeriodicBackgroundWorker.cs @@ -103,7 +103,7 @@ public class PeriodicBackgroundWorker : BackgroundService var periodic = new WorkPeriodic(work, period); works.Add(periodic); if (ExecuteTask is null || ExecuteTask.IsCompleted) - StartAsync(CancellationToken.None).Wait(); + StartAsync(CancellationToken.None); } private WorkPeriodic? GetNext()