diff --git a/AsbCloudInfrastructure/Background/BackgroudWorkDto.cs b/AsbCloudInfrastructure/Background/BackgroudWorkDto.cs new file mode 100644 index 00000000..873217eb --- /dev/null +++ b/AsbCloudInfrastructure/Background/BackgroudWorkDto.cs @@ -0,0 +1,170 @@ +using System; +using System.Diagnostics; + +namespace AsbCloudInfrastructure.Background +{ + public class BackgroudWorkDto + { + /// + /// Идентификатор работы. Должен быть уникальным. Используется в логах и передается в колбэки. + /// + public string Id { get; init; } = null!; + + public class CurrentStateInfo + { + private string state = "start"; + + /// + /// Время последнего запуска + /// + public DateTime Start { get; } = DateTime.Now; + + /// + /// Текущее время выполнения + /// + public TimeSpan ExecutionTime => DateTime.Now - Start; + + /// + /// Текстовое описание того, что происходит в задаче. + /// + public string State + { + get => state; + internal set + { + state = value; + StateUpdate = DateTime.Now; + } + } + + public double Progress { get; internal set; } = 0; + + /// + /// Время последнего запуска + /// + public DateTime StateUpdate { get; private set; } = DateTime.Now; + } + + public class LastErrorInfo: LastCompleteInfo + { + public LastErrorInfo(CurrentStateInfo state, string errorText) + : base(state) + { + ErrorText = errorText; + } + + /// + /// Последняя ошибка + /// + public string ErrorText { get; init; } = null!; + } + + public class LastCompleteInfo + { + /// + /// Дата запуска + /// + public DateTime Start {get; init;} + + /// + /// Дата завершения + /// + public DateTime End { get; init; } + + /// + /// Продолжительность последнего выполнения + /// + public TimeSpan ExecutionTime => End - Start; + + /// + /// Состояние на момент завершения + /// + public string State { get; init; } + + public LastCompleteInfo(CurrentStateInfo state) + { + Start = state.Start; + End = DateTime.Now; + State = state.State; + } + } + + /// + /// Текущее состояние + /// + public CurrentStateInfo? CurrentState { get; private set; } + + /// + /// Последняя ошибка + /// + public LastErrorInfo? LastError { get; private set; } + + /// + /// Последняя завершенная + /// + public LastCompleteInfo? LastComplete { get; private set; } + + /// + /// Кол-во запусков + /// + public int CountStart { get; private set; } + + /// + /// Кол-во завершений + /// + public int CountComplete { get; private set; } + + /// + /// Кол-во ошибок + /// + public int CountErrors { get; private set; } + + /// + /// Максимально допустимое время выполнения работы + /// + public TimeSpan Timeout { get; set; } = TimeSpan.FromMinutes(1); + + private string WorkNameForTrace => $"Backgroud work:\"{Id}\""; + + protected void SetStatusStart() + { + CurrentState = new(); + CountStart++; + Trace.TraceInformation($"{WorkNameForTrace} state: starting"); + } + + protected void UpdateStatus(string newState, double? progress) + { + if (CurrentState is null) + return; + + CurrentState.State = newState; + if (progress.HasValue) + CurrentState.Progress = progress.Value; + + Trace.TraceInformation($"{WorkNameForTrace} state: {newState}"); + } + + protected void SetStatusComplete(System.Threading.Tasks.TaskStatus status) + { + if (CurrentState is null) + return; + + LastComplete = new (CurrentState); + CurrentState = null; + CountComplete++; + Trace.TraceInformation($"{WorkNameForTrace} state: completed"); + } + + protected void SetLastError(string errorMessage) + { + if (CurrentState is null) + return; + + LastError = new LastErrorInfo(CurrentState, errorMessage); + CurrentState = null; + CountErrors++; + Trace.TraceError($"{WorkNameForTrace} throw exception[{CountErrors}]: {errorMessage}"); + } + } +} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Background/BackgroundWorker.cs b/AsbCloudInfrastructure/Background/BackgroundWorker.cs index 4ba3d535..fad61edf 100644 --- a/AsbCloudInfrastructure/Background/BackgroundWorker.cs +++ b/AsbCloudInfrastructure/Background/BackgroundWorker.cs @@ -15,7 +15,7 @@ namespace AsbCloudInfrastructure.Background private static readonly TimeSpan executePeriod = TimeSpan.FromSeconds(10); private static readonly TimeSpan minDelay = TimeSpan.FromSeconds(2); private readonly IServiceProvider serviceProvider; - private readonly WorkQueue workQueue = new WorkQueue(); + private readonly WorkStore workQueue = new WorkStore(); public string? CurrentWorkId; public BackgroundWorker(IServiceProvider serviceProvider) diff --git a/AsbCloudInfrastructure/Background/OrderedList.cs b/AsbCloudInfrastructure/Background/OrderedList.cs new file mode 100644 index 00000000..1f583a82 --- /dev/null +++ b/AsbCloudInfrastructure/Background/OrderedList.cs @@ -0,0 +1,41 @@ +using System.Linq; + +namespace System.Collections.Generic +{ + public class OrderedList: IEnumerable, ICollection + where T : notnull + { + private readonly List list = new List(); + + private readonly Func keySelector; + private readonly bool isDescending = false; + + private IOrderedEnumerable OrdredList => isDescending + ? list.OrderByDescending(keySelector) + : list.OrderBy(keySelector); + + public int Count => list.Count; + + public bool IsReadOnly => false; + + public OrderedList(Func keySelector, bool isDescending = false) + { + this.keySelector = keySelector; + this.isDescending = isDescending; + } + + public void Add(T item) => list.Add(item); + + public void Clear()=> list.Clear(); + + public bool Contains(T item)=> list.Contains(item); + + public void CopyTo(T[] array, int arrayIndex)=> list.CopyTo(array, arrayIndex); + + public bool Remove(T item)=> list.Remove(item); + + public IEnumerator GetEnumerator() => OrdredList.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} diff --git a/AsbCloudInfrastructure/Background/WorkBase.cs b/AsbCloudInfrastructure/Background/WorkBase.cs index ef238a85..e1b83053 100644 --- a/AsbCloudInfrastructure/Background/WorkBase.cs +++ b/AsbCloudInfrastructure/Background/WorkBase.cs @@ -1,6 +1,4 @@ using System; -using System.Diagnostics; -using System.Text; using System.Threading; using System.Threading.Tasks; @@ -10,14 +8,23 @@ namespace AsbCloudInfrastructure.Background /// Класс разовой работы. /// Разовая работа приоритетнее периодической. /// - public class WorkBase + public class WorkBase : BackgroudWorkDto { - /// - /// Идентификатор работы. Должен быть уникальным. Используется в логах и передается в колбэки. - /// - public string Id { get; } + internal Func, CancellationToken, Task> ActionAsync { get; } /// + /// Делегат обработки ошибки. + /// Не должен выполняться долго. + /// + public Func? OnErrorAsync { get; set; } + + public TimeSpan OnErrorHandlerTimeout { get; set; } = TimeSpan.FromSeconds(5); + + /// + /// Базовая работа + /// + /// + /// /// Делегат работы. /// /// Параметры: @@ -31,138 +38,50 @@ namespace AsbCloudInfrastructure.Background /// Поставщик сервисов /// /// + /// Action<string, double?> + /// on progress callback. String - new state text. double? - optional progress 0-100%. + /// + /// /// CancellationToken /// Токен отмены задачи /// /// /// - /// - internal Func ActionAsync { get; } - - /// - /// Делегат обработки ошибки. - /// Не должен выполняться долго. - /// - public Func? OnErrorAsync { get; set; } - - /// - /// максимально допустимое время выполнения работы - /// - public TimeSpan Timeout { get; set; } = TimeSpan.FromMinutes(1); - - /// - /// Продолжительность последнего выполнения - /// - public TimeSpan? LastExecutionTime { get; private set; } - - /// - /// Текущее время выполнения - /// - public TimeSpan? CurrentExecutionTime => CurrentStart.HasValue - ? DateTime.Now - CurrentStart.Value - : null; - - /// - /// Время последнего запуска - /// - public DateTime? CurrentStart { get; private set; } - - /// - /// Текстовое описание того, что происходит в задаче. - /// - public string? CurrentStatus { get; private set; } - - /// - /// Время последнего запуска - /// - public DateTime? CurrentStatusUpdate { get; private set; } - - /// - /// Последняя ошибка - /// - public string? LastErrorMessage { get; private set; } - - /// - /// Дата последнего запуска - /// - public DateTime? LastStart { get; private set; } - - /// - /// Дата последней ошибки - /// - public DateTime? LastError { get; private set; } - - /// - /// Дата завершения последнего выполнения - /// - public DateTime? LastComplete { get; private set; } - - /// - /// Кол-во завершений - /// - public int CountComplete { get; private set; } - - /// - /// Кол-во ошибок - /// - public int CountErrors { get; private set; } - - private string WorkNameForTrace => $"Backgroud work:\"{Id}\""; - - public WorkBase(string id, Func actionAsync) + /// + public WorkBase(string id, Func, CancellationToken, Task> actionAsync) { Id = id; ActionAsync = actionAsync; } - public async Task Start(IServiceProvider services, CancellationToken token) + /// + /// Запустить работу + /// + /// + /// + /// True - susess, False = Fail + public async Task Start(IServiceProvider services, CancellationToken token) { - CurrentStart = DateTime.Now; - LastStart = DateTime.Now; + SetStatusStart(); try { - SetStatus(" start"); - var task = ActionAsync(Id, services, token); + var task = ActionAsync(Id, services, UpdateStatus, token); await task.WaitAsync(Timeout, token); - LastComplete = DateTime.Now; - CountComplete++; - SetStatus($" {task.Status}. ExecutionTime: {CurrentExecutionTime:hh\\:mm\\:ss\\.fff}"); + SetStatusComplete(task.Status); + return true; } catch (Exception exception) { - SetError(exception.Message); + SetLastError(exception.Message); if (OnErrorAsync is not null) { var task = Task.Run( async () => await OnErrorAsync(Id, exception, token), token); - await task.WaitAsync(Timeout, token); + await task.WaitAsync(OnErrorHandlerTimeout, token); } } - - LastExecutionTime = CurrentExecutionTime; - CurrentStart = null; - SetStatus(null); - } - - protected void SetStatus(string? newStatus) - { - CurrentStatus = newStatus; - if (newStatus is not null) - { - CurrentStatusUpdate = DateTime.Now; - Trace.TraceInformation($"{WorkNameForTrace} state: {newStatus}"); - } - else - CurrentStatusUpdate = null; - } - - private void SetError(string? errorMessage) - { - CountErrors++; - LastErrorMessage = errorMessage; - LastError = DateTime.Now; - Trace.TraceError($"{WorkNameForTrace} throw exception[{CountErrors}]: {errorMessage}"); + return false; } } } diff --git a/AsbCloudInfrastructure/Background/WorkPeriodic.cs b/AsbCloudInfrastructure/Background/WorkPeriodic.cs index 5c2b0e23..71d580ed 100644 --- a/AsbCloudInfrastructure/Background/WorkPeriodic.cs +++ b/AsbCloudInfrastructure/Background/WorkPeriodic.cs @@ -18,7 +18,16 @@ namespace AsbCloudInfrastructure.Background /// /// Время следующего запуска /// - public DateTime NextStart => LastStart??DateTime.MinValue + Period; + public DateTime NextStart + { + get + { + var lastStart = LastComplete?.Start ?? DateTime.MinValue; + if (LastError?.Start > lastStart) + lastStart = LastError.Start; + return lastStart + Period; + } + } /// /// Класс периодической работы @@ -26,7 +35,7 @@ namespace AsbCloudInfrastructure.Background /// Идентификатор работы. Должен быть уникальным. Используется в логах и передается в колбэки /// Делегат работы /// Период выполнения задачи - public WorkPeriodic(string id, Func actionAsync, TimeSpan period) + public WorkPeriodic(string id, Func, CancellationToken, Task> actionAsync, TimeSpan period) : base(id, actionAsync) { Period = period; diff --git a/AsbCloudInfrastructure/Background/WorkQueue.cs b/AsbCloudInfrastructure/Background/WorkStore.cs similarity index 70% rename from AsbCloudInfrastructure/Background/WorkQueue.cs rename to AsbCloudInfrastructure/Background/WorkStore.cs index e925916b..590ad8a6 100644 --- a/AsbCloudInfrastructure/Background/WorkQueue.cs +++ b/AsbCloudInfrastructure/Background/WorkStore.cs @@ -10,11 +10,25 @@ namespace AsbCloudInfrastructure.Background /// /// Не периодические задачи будут возвращаться первыми, как самые приоритетные. /// - class WorkQueue + class WorkStore { - private Queue Primary = new(8); private readonly List Periodic = new(8); + /// + /// Работы выполняемые один раз + /// + public Queue RunOnceQueue { get; } = new(8); + + /// + /// Работы выполняемые периодически + /// + public IOrderedEnumerable Periodics => Periodic.OrderBy(work => work.NextStart); + + /// + /// Завершывшиеся с ошибкой + /// + public CyclycArray Falled { get; } = new(16); + /// /// Добавление работы. /// @@ -25,8 +39,8 @@ namespace AsbCloudInfrastructure.Background if (Periodic.Any(w => w.Id == work.Id)) throw new ArgumentException("work.Id is not unique", nameof(work)); - if (Primary.Any(w => w.Id == work.Id)) - throw new ArgumentException("work.Id is not unique", nameof(work)); + //if (Primary.Any(w => w.Id == work.Id)) + // throw new ArgumentException("work.Id is not unique", nameof(work)); if (work is WorkPeriodic workPeriodic) { @@ -34,7 +48,7 @@ namespace AsbCloudInfrastructure.Background return; } - Primary.Enqueue(work); + //Primary.Enqueue(work); } /// @@ -51,19 +65,19 @@ namespace AsbCloudInfrastructure.Background return true; } - var work = Primary.FirstOrDefault(w => w.Id == id); - if (work is not null) - { - Primary = new Queue(Primary.Where(w => w.Id != id)); - return true; - } + //var work = Primary.FirstOrDefault(w => w.Id == id); + //if (work is not null) + //{ + // Primary = new Queue(Primary.Where(w => w.Id != id)); + // return true; + //} return false; } public bool Contains(string id) { - var result = Periodic.Any(w => w.Id == id) || Primary.Any(w => w.Id == id); + var result = false;//Periodic.Any(w => w.Id == id) || Primary.Any(w => w.Id == id); return result; } @@ -82,8 +96,8 @@ namespace AsbCloudInfrastructure.Background /// public WorkBase? Pop() { - if (Primary.Any()) - return Primary.Dequeue(); + //if (Primary.Any()) + // return Primary.Dequeue(); var work = GetNextPeriodic(); if (work is null || work.NextStart > DateTime.Now) diff --git a/AsbCloudInfrastructure/Services/DetectOperations/OperationDetectionWorkFactory.cs b/AsbCloudInfrastructure/Services/DetectOperations/OperationDetectionWorkFactory.cs index da171f13..350ad6b7 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/OperationDetectionWorkFactory.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/OperationDetectionWorkFactory.cs @@ -46,7 +46,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations } // TODO: Разделить этот акшн на более мелкие части И использовать telemetryServiceData<..> вместо прямого обращения к БД. - private static async Task WorkAction(string _, IServiceProvider serviceProvider, CancellationToken token) + private static async Task WorkAction(string _, IServiceProvider serviceProvider, Action onProgress, CancellationToken token) { using var db = serviceProvider.GetRequiredService(); @@ -75,10 +75,14 @@ namespace AsbCloudInfrastructure.Services.DetectOperations }); var affected = 0; + var count = joinedlastDetectedDates.Count(); + var i = 0; foreach (var item in joinedlastDetectedDates) { var stopwatch = Stopwatch.StartNew(); - var newOperations = await DetectOperationsAsync(item.IdTelemetry, item.LastDate ?? DateTimeOffset.MinValue, db, token); + var startDate = item.LastDate ?? DateTimeOffset.MinValue; + onProgress($"start detecting telemetry: {item.IdTelemetry} from {startDate}", i++ / count); + var newOperations = await DetectOperationsAsync(item.IdTelemetry, startDate, db, token); stopwatch.Stop(); if (newOperations.Any()) { diff --git a/AsbCloudInfrastructure/Services/DrillingProgram/DrillingProgramService.cs b/AsbCloudInfrastructure/Services/DrillingProgram/DrillingProgramService.cs index 9dc20155..cf41ab7c 100644 --- a/AsbCloudInfrastructure/Services/DrillingProgram/DrillingProgramService.cs +++ b/AsbCloudInfrastructure/Services/DrillingProgram/DrillingProgramService.cs @@ -519,11 +519,12 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram var resultFileName = $"Программа бурения {well.Cluster} {well.Caption}.pdf"; var convertedFilesDir = Path.Combine(Path.GetTempPath(), "drillingProgram", $"{well.Cluster}_{well.Caption}"); var tempResultFilePath = Path.Combine(convertedFilesDir, resultFileName); - var workAction = async (string workId, IServiceProvider serviceProvider, CancellationToken token) => + var workAction = async (string workId, IServiceProvider serviceProvider, Action onProgress, CancellationToken token) => { var context = serviceProvider.GetRequiredService(); var fileService = serviceProvider.GetRequiredService(); var files = state.Parts.Select(p => fileService.GetUrl(p.File!)); + onProgress($"Start converting {files.Count()} files to PDF.", null); await ConvertToPdf.GetConverteAndMergedFileAsync(files, tempResultFilePath, convertedFilesDir, token); await fileService.MoveAsync(idWell, null, idFileCategoryDrillingProgram, resultFileName, tempResultFilePath, token); }; diff --git a/AsbCloudInfrastructure/Services/Email/EmailNotificationTransportService.cs b/AsbCloudInfrastructure/Services/Email/EmailNotificationTransportService.cs index 26eed2be..412a4354 100644 --- a/AsbCloudInfrastructure/Services/Email/EmailNotificationTransportService.cs +++ b/AsbCloudInfrastructure/Services/Email/EmailNotificationTransportService.cs @@ -10,6 +10,7 @@ using AsbCloudApp.Exceptions; using AsbCloudApp.Repositories; using AsbCloudApp.Services.Notifications; using AsbCloudInfrastructure.Background; +using DocumentFormat.OpenXml.Presentation; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -70,9 +71,9 @@ namespace AsbCloudInfrastructure.Services.Email return Task.WhenAll(tasks); } - private Func MakeEmailSendWorkAction(NotificationDto notification) + private Func, CancellationToken, Task> MakeEmailSendWorkAction(NotificationDto notification) { - return async (_, serviceProvider, token) => + return async (_, serviceProvider, onProgress, token) => { var notificationRepository = serviceProvider.GetRequiredService(); var userRepository = serviceProvider.GetRequiredService(); diff --git a/AsbCloudInfrastructure/Services/LimitingParameterBackgroundService.cs b/AsbCloudInfrastructure/Services/LimitingParameterBackgroundService.cs index 7c875e70..6c4f94c1 100644 --- a/AsbCloudInfrastructure/Services/LimitingParameterBackgroundService.cs +++ b/AsbCloudInfrastructure/Services/LimitingParameterBackgroundService.cs @@ -28,7 +28,7 @@ namespace AsbCloudInfrastructure.Services } // TODO: Разделить этот акшн на более мелкие части И использовать telemetryServiceData<..> вместо прямого обращения к БД. - private static async Task WorkAction(string _, IServiceProvider serviceProvider, CancellationToken token) + private static async Task WorkAction(string _, IServiceProvider serviceProvider, Action onProgress, CancellationToken token) { using var db = serviceProvider.GetRequiredService(); var lastDetectedDates = await db.LimitingParameter @@ -55,8 +55,11 @@ namespace AsbCloudInfrastructure.Services inner.SingleOrDefault()?.LastDate, }); + var count = telemetryLastDetectedDates.Count(); + var i = 0; foreach (var item in telemetryLastDetectedDates) { + onProgress($"Start hanling telemetry: {item.IdTelemetry} from {item.LastDate}", i++/count); var newLimitingParameters = await GetLimitingParameterAsync(item.IdTelemetry, item.LastDate ?? DateTimeOffset.MinValue, db, token); if (newLimitingParameters?.Any() == true) { diff --git a/AsbCloudInfrastructure/Services/ReportService.cs b/AsbCloudInfrastructure/Services/ReportService.cs index eca8f84e..95851c74 100644 --- a/AsbCloudInfrastructure/Services/ReportService.cs +++ b/AsbCloudInfrastructure/Services/ReportService.cs @@ -51,7 +51,7 @@ namespace AsbCloudInfrastructure.Services var workId = $"create report by wellid:{idWell} for userid:{idUser} requested at {DateTime.Now}"; - var workAction = async (string id, IServiceProvider serviceProvider, CancellationToken token) => + var workAction = async (string id, IServiceProvider serviceProvider, Action onProgress, CancellationToken token) => { using var context = serviceProvider.GetRequiredService(); var fileService = serviceProvider.GetRequiredService(); @@ -64,7 +64,9 @@ namespace AsbCloudInfrastructure.Services generator.OnProgress += (s, e) => { - progressHandler.Invoke(e.Adapt(), id); + var arg = e.Adapt(); + onProgress(arg.Operation?? string.Empty, arg.Progress); + progressHandler.Invoke(arg, id); }; generator.Make(reportFileName); diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataCache.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataCache.cs index d1a7fea1..8868fc24 100644 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataCache.cs +++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataCache.cs @@ -48,9 +48,9 @@ namespace AsbCloudInfrastructure.Services.SAUB instance = new TelemetryDataCache(); var worker = provider.GetRequiredService(); var workId = $"Telemetry cache loading from DB {typeof(TEntity).Name}"; - var work = new WorkBase(workId, async (workId, provider, token) => { + var work = new WorkBase(workId, async (workId, provider, onProgress, token) => { var db = provider.GetRequiredService(); - await instance.InitializeCacheFromDBAsync(db, token); + await instance.InitializeCacheFromDBAsync(db, onProgress, token); }); worker.Push(work); @@ -150,7 +150,7 @@ namespace AsbCloudInfrastructure.Services.SAUB return new DatesRangeDto { From = from.Value, To = to }; } - private async Task InitializeCacheFromDBAsync(IAsbCloudDbContext db, CancellationToken token) + private async Task InitializeCacheFromDBAsync(IAsbCloudDbContext db, Action onProgress, CancellationToken token) where TEntity : class, AsbCloudDb.Model.ITelemetryData { if (isLoading) @@ -168,6 +168,8 @@ namespace AsbCloudInfrastructure.Services.SAUB .Where(well => well.IdTelemetry != null) .ToArrayAsync(token); + var count = wells.Count(); + var i = 0; foreach (Well well in wells) { var capacity = well.IdState == 1 @@ -176,7 +178,7 @@ namespace AsbCloudInfrastructure.Services.SAUB var idTelemetry = well.IdTelemetry!.Value; var hoursOffset = well.Timezone.Hours; - + // TODO: remove traces System.Diagnostics.Trace.TraceInformation($"cache<{typeof(TDto).Name}>: Loading for well: {well.Cluster?.Caption}/{well.Caption} (capacity:{capacity}) idTelemetry:{idTelemetry}"); var cacheItem = await GetOrDefaultCacheDataFromDbAsync(db, idTelemetry, capacity, hoursOffset, token); if(cacheItem is not null) diff --git a/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeCalcWorkFactory.cs b/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeCalcWorkFactory.cs index d5b86803..bbbc1fb5 100644 --- a/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeCalcWorkFactory.cs +++ b/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeCalcWorkFactory.cs @@ -36,7 +36,7 @@ namespace AsbCloudInfrastructure.Services.Subsystems } // TODO: Разделить этот акшн на более мелкие части И использовать telemetryServiceData<..> вместо прямого обращения к БД. - private static async Task WorkAction(string _, IServiceProvider serviceProvider, CancellationToken token) + private static async Task WorkAction(string _, IServiceProvider serviceProvider, Action onProgress, CancellationToken token) { using var db = serviceProvider.GetRequiredService(); @@ -64,8 +64,11 @@ namespace AsbCloudInfrastructure.Services.Subsystems inner.SingleOrDefault()?.LastDate, }); + var count = telemetryLastDetectedDates.Count(); + var i = 0; foreach (var item in telemetryLastDetectedDates) { + onProgress($"Start hanling telemetry: {item.IdTelemetry} from {item.LastDate}", i++ / count); var newOperationsSaub = await OperationTimeSaubAsync(item.IdTelemetry, item.LastDate ?? DateTimeOffset.MinValue, db, token); if (newOperationsSaub?.Any() == true) { diff --git a/AsbCloudInfrastructure/Services/WellInfoService.cs b/AsbCloudInfrastructure/Services/WellInfoService.cs index eea573cc..0d6951b2 100644 --- a/AsbCloudInfrastructure/Services/WellInfoService.cs +++ b/AsbCloudInfrastructure/Services/WellInfoService.cs @@ -62,7 +62,7 @@ namespace AsbCloudInfrastructure.Services return workPeriodic; } - private static async Task WorkAction(string workName, IServiceProvider serviceProvider, CancellationToken token) + private static async Task WorkAction(string workName, IServiceProvider serviceProvider, Action onProgress, CancellationToken token) { var wellService = serviceProvider.GetRequiredService(); var operationsStatService = serviceProvider.GetRequiredService(); @@ -90,11 +90,12 @@ namespace AsbCloudInfrastructure.Services var subsystemStat = await subsystemOperationTimeService .GetStatByActiveWells(wellsIds, token); - + var count = wells.Count(); + var i = 0; WellMapInfo = wells.Select(well => { var wellMapInfo = well.Adapt(); wellMapInfo.IdState = well.IdState; - + onProgress($"Start updating info by well({well.Id}): {well.Caption}", i++ / count); double? currentDepth = null; TelemetryDataSaubDto? lastSaubTelemetry = null; diff --git a/AsbCloudInfrastructure/Startup.cs b/AsbCloudInfrastructure/Startup.cs index c1c3cc8f..54d5d080 100644 --- a/AsbCloudInfrastructure/Startup.cs +++ b/AsbCloudInfrastructure/Startup.cs @@ -51,7 +51,7 @@ namespace AsbCloudInfrastructure static WorkPeriodic MakeMemoryMonitoringWork() { var workId = "Memory monitoring"; - var workAction = (string _, IServiceProvider _, CancellationToken _) => { + var workAction = (string _, IServiceProvider _, Action _, CancellationToken _) => { var bytes = GC.GetTotalMemory(false); var bytesString = FromatBytes(bytes); System.Diagnostics.Trace.TraceInformation($"Total memory allocated is {bytesString} bytes. DbContext count is:{AsbCloudDbContext.ReferenceCount}"); diff --git a/AsbCloudWebApi/SignalR/Services/SignalRNotificationTransportService.cs b/AsbCloudWebApi/SignalR/Services/SignalRNotificationTransportService.cs index b418c0b5..9633eb1c 100644 --- a/AsbCloudWebApi/SignalR/Services/SignalRNotificationTransportService.cs +++ b/AsbCloudWebApi/SignalR/Services/SignalRNotificationTransportService.cs @@ -39,9 +39,9 @@ public class SignalRNotificationTransportService : INotificationTransportService return Task.CompletedTask; } - private Func MakeSignalRSendWorkAction(IEnumerable notifications) + private Func, CancellationToken, Task> MakeSignalRSendWorkAction(IEnumerable notifications) { - return async (_, serviceProvider, cancellationToken) => + return async (_, serviceProvider, onProgress, cancellationToken) => { var notificationPublisher = serviceProvider.GetRequiredService();