From 62008f871230a8e54a15adbae5ba559e988922b5 Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Fri, 19 May 2023 16:25:20 +0500 Subject: [PATCH 1/8] remove InstantDataRepository --- AsbCloudApp/Services/InstantDataRepository.cs | 12 ------------ AsbCloudInfrastructure/DependencyInjection.cs | 7 ++----- 2 files changed, 2 insertions(+), 17 deletions(-) delete mode 100644 AsbCloudApp/Services/InstantDataRepository.cs diff --git a/AsbCloudApp/Services/InstantDataRepository.cs b/AsbCloudApp/Services/InstantDataRepository.cs deleted file mode 100644 index d58e602e..00000000 --- a/AsbCloudApp/Services/InstantDataRepository.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Concurrent; - -namespace AsbCloudApp.Services -{ - /// - /// Репозиторий для хранения в оперативке данных (от панели) - /// - public class InstantDataRepository : ConcurrentDictionary> - { - } -} diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index 1911d5a8..41151953 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -1,12 +1,10 @@ using AsbCloudApp.Data; -using AsbCloudApp.Data.GTR; using AsbCloudApp.Data.SAUB; using AsbCloudApp.Data.Subsystems; using AsbCloudApp.Repositories; using AsbCloudApp.Services; using AsbCloudApp.Services.Subsystems; using AsbCloudDb.Model; -using AsbCloudDb.Model.GTR; using AsbCloudDb.Model.Subsystems; using AsbCloudInfrastructure.Background; using AsbCloudInfrastructure.Repository; @@ -102,9 +100,8 @@ namespace AsbCloudInfrastructure services.AddScoped(); services.AddSingleton(new WitsInfoService()); - services.AddSingleton(new InstantDataRepository()); - services.AddSingleton(provider=> TelemetryDataCache.GetInstance(configuration)); - services.AddSingleton(provider=> TelemetryDataCache.GetInstance(configuration)); + services.AddSingleton(provider => TelemetryDataCache.GetInstance(provider)); + services.AddSingleton(provider => TelemetryDataCache.GetInstance(provider)); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); From e9af9f7ddf7b360049e8b5ad6c1c1bf224671192 Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Fri, 19 May 2023 16:26:04 +0500 Subject: [PATCH 2/8] TelemetryTracker remove unused GetTransmittingTelemetriesUids() --- AsbCloudApp/Services/ITelemetryTracker.cs | 6 ------ AsbCloudInfrastructure/Services/SAUB/TelemetryTracker.cs | 2 -- 2 files changed, 8 deletions(-) diff --git a/AsbCloudApp/Services/ITelemetryTracker.cs b/AsbCloudApp/Services/ITelemetryTracker.cs index ae9acb80..5a6f255e 100644 --- a/AsbCloudApp/Services/ITelemetryTracker.cs +++ b/AsbCloudApp/Services/ITelemetryTracker.cs @@ -23,12 +23,6 @@ namespace AsbCloudApp.Services /// DatesRangeDto GetTelemetryDateRangeByUid(string uid); - /// - /// список передающих телеметрий - /// - /// - IEnumerable GetTransmittingTelemetriesUids(); - /// /// обновить статистику по телеметрии /// diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryTracker.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryTracker.cs index e3469f1e..588a4b87 100644 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryTracker.cs +++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryTracker.cs @@ -149,8 +149,6 @@ namespace AsbCloudInfrastructure.Services.SAUB return range; } - public IEnumerable GetTransmittingTelemetriesUids() => - telemetriesStats.Keys; } } From 9106658ebfb15f1ed185d752084169d6d472b433 Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Fri, 19 May 2023 16:27:24 +0500 Subject: [PATCH 3/8] Startup. Initialize cache loading. --- AsbCloudInfrastructure/Startup.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/AsbCloudInfrastructure/Startup.cs b/AsbCloudInfrastructure/Startup.cs index eec7d716..207270c2 100644 --- a/AsbCloudInfrastructure/Startup.cs +++ b/AsbCloudInfrastructure/Startup.cs @@ -10,6 +10,8 @@ using System; using System.Threading.Tasks; using System.Threading; using AsbCloudInfrastructure.Background; +using AsbCloudApp.Data.SAUB; +using AsbCloudInfrastructure.Services.SAUB; namespace AsbCloudInfrastructure { @@ -28,6 +30,9 @@ namespace AsbCloudInfrastructure var wellService = provider.GetRequiredService(); wellService.EnshureTimezonesIsSetAsync(CancellationToken.None).Wait();// TODO: make this background work + _ = provider.GetRequiredService>(); + _ = provider.GetRequiredService>(); + var backgroundWorker = provider.GetRequiredService(); backgroundWorker.Push(WellInfoService.MakeWork()); backgroundWorker.Push(OperationDetectionWorkFactory.MakeWork()); @@ -45,7 +50,7 @@ namespace AsbCloudInfrastructure var workAction = (string _, IServiceProvider _, CancellationToken _) => { var bytes = GC.GetTotalMemory(false); var bytesString = FromatBytes(bytes); - System.Diagnostics.Trace.TraceInformation($"Total memory allocated is {bytesString} bytes. DbContext count is:{AsbCloudDb.Model.AsbCloudDbContext.ReferenceCount}"); + System.Diagnostics.Trace.TraceInformation($"Total memory allocated is {bytesString} bytes. DbContext count is:{AsbCloudDbContext.ReferenceCount}"); return Task.CompletedTask; }; var workPeriod = TimeSpan.FromMinutes(1); From 36556bd6e3031bb3512e354924c8a05ad4d7b85e Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Fri, 19 May 2023 16:30:41 +0500 Subject: [PATCH 4/8] TelemetryDataCache loads cache by BackgroundWorker --- .../Services/SAUB/TelemetryDataCache.cs | 182 +++++++++++------- 1 file changed, 111 insertions(+), 71 deletions(-) diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataCache.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataCache.cs index f1b2186b..bb3bb20c 100644 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataCache.cs +++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataCache.cs @@ -7,17 +7,28 @@ using Microsoft.EntityFrameworkCore; using Mapster; using System.Threading.Tasks; using Microsoft.Extensions.Configuration; - +using Microsoft.Extensions.DependencyInjection; +using AsbCloudInfrastructure.Background; +using System.Threading; +using AsbCloudApp.Data; namespace AsbCloudInfrastructure.Services.SAUB { public class TelemetryDataCache where TDto : AsbCloudApp.Data.ITelemetryData { + class TelemetryDataCacheItem + { + public TDto? FirstByDate { get; init; } + public CyclycArray LastData { get; init; } = null!; + } + + private IServiceProvider provider = null!; private const int activeWellCapacity = 12 * 60 * 60; private const int doneWellCapacity = 65 * 60; - private readonly ConcurrentDictionary> caches; + // key == idTelemetry + private readonly ConcurrentDictionary caches; private bool isLoading = false; private TelemetryDataCache() @@ -27,35 +38,22 @@ namespace AsbCloudInfrastructure.Services.SAUB private static TelemetryDataCache? instance; - //TODO: Move initialize fromDB to bacground service task - public static TelemetryDataCache GetInstance(IConfiguration configuration) - where TEntity : class, ITelemetryData + public static TelemetryDataCache GetInstance(IServiceProvider provider) + where TEntity : class, AsbCloudDb.Model.ITelemetryData { if (instance is null) { instance = new TelemetryDataCache(); - _ = Task.Run(() => - { - using var db = MakeContext(configuration); - instance.InitializeCacheFromDB(db); - db.Dispose(); + var worker = provider.GetRequiredService(); + var workId = $"Telemetry cache loading from DB {typeof(TEntity).Name}"; + var work = new WorkBase(workId, async (workId, provider, token) => { + var db = provider.GetRequiredService(); + await instance.InitializeCacheFromDBAsync(db, token); }); + + worker.Push(work); } - return instance; - } - public static TelemetryDataCache GetInstance(IAsbCloudDbContext db, out Task initializationTask) - where TEntity : class, ITelemetryData - { - if (instance is null) - { - instance = new TelemetryDataCache(); - initializationTask = Task.Run(() => - { - instance.InitializeCacheFromDB(db); - }); - } - else - initializationTask = Task.CompletedTask; + instance.provider = provider; return instance; } @@ -66,24 +64,33 @@ namespace AsbCloudInfrastructure.Services.SAUB /// public void AddRange(int idTelemetry, IEnumerable range) { - CyclycArray cacheItem; + if (!range.Any()) + return; + + var newItems = range + .OrderBy(i => i.DateTime); + + foreach (var item in newItems) + item.IdTelemetry = idTelemetry; + + TelemetryDataCacheItem cacheItem; if (isLoading) { - if (caches.TryGetValue(idTelemetry, out CyclycArray? localCacheItem)) + if (caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? localCacheItem)) cacheItem = localCacheItem; else return; } else { - cacheItem = caches.GetOrAdd(idTelemetry, _ => new CyclycArray(activeWellCapacity)); + cacheItem = caches.GetOrAdd(idTelemetry, _ => new TelemetryDataCacheItem() + { + FirstByDate = newItems.ElementAt(0), + LastData = new CyclycArray(activeWellCapacity) + }); } - - var newItems = range - .OrderBy(i => i.DateTime); - foreach (var item in newItems) - item.IdTelemetry = idTelemetry; - cacheItem.AddRange(newItems); + + cacheItem.LastData.AddRange(newItems); } /// @@ -98,14 +105,16 @@ namespace AsbCloudInfrastructure.Services.SAUB /// public IEnumerable? GetOrDefault(int idTelemetry, DateTime dateBegin, double intervalSec = 600d, int approxPointsCount = 1024) { - if(!caches.TryGetValue(idTelemetry, out CyclycArray? cacheItem)) + if(!caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? cacheItem)) return null; - if (cacheItem is null || !cacheItem.Any() || cacheItem[0].DateTime > dateBegin) + var cacheLastData = cacheItem.LastData; + + if (!cacheLastData.Any() || cacheLastData[0].DateTime > dateBegin) return null; var dateEnd = dateBegin.AddSeconds(intervalSec); - var items = cacheItem + var items = cacheLastData .Where(i => i.DateTime >= dateBegin && i.DateTime <= dateEnd); var ratio = items.Count() / approxPointsCount; @@ -116,19 +125,35 @@ namespace AsbCloudInfrastructure.Services.SAUB return items; } - private void InitializeCacheFromDB(IAsbCloudDbContext db) - where TEntity : class, ITelemetryData + public DatesRangeDto? GetOrDefaultDataDateRange(int idTelemetry) + { + if (!caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? cacheItem)) + return null; + + var from = cacheItem.FirstByDate?.DateTime; + if(!cacheItem.LastData.Any()) + return null; + + var to = cacheItem.LastData[^1].DateTime; + from = from ?? cacheItem.LastData[0].DateTime; + + return new DatesRangeDto { From = from.Value, To = to }; + } + + private async Task InitializeCacheFromDBAsync(IAsbCloudDbContext db, CancellationToken token) + where TEntity : class, AsbCloudDb.Model.ITelemetryData { if (isLoading) throw new Exception("Multiple cache loading detected."); isLoading = true; Well[] wells = Array.Empty(); - wells = db.Set() - .Include(well => well.Telemetry) - .Include(well => well.Cluster) - .Where(well => well.IdTelemetry != null) - .ToArray(); + + wells = await db.Set() + .Include(well => well.Telemetry) + .Include(well => well.Cluster) + .Where(well => well.IdTelemetry != null) + .ToArrayAsync(token); foreach (Well well in wells) { @@ -139,46 +164,61 @@ namespace AsbCloudInfrastructure.Services.SAUB var idTelemetry = well.IdTelemetry!.Value; var hoursOffset = well.Timezone.Hours; - IEnumerable cacheItemData = GetCacheDataFromDb(db, idTelemetry, capacity, hoursOffset); - var cacheItem = new CyclycArray(capacity); - cacheItem.AddRange(cacheItemData); - caches.TryAdd(idTelemetry, cacheItem); - - System.Diagnostics.Trace.TraceInformation($"cache<{typeof(TDto).Name}> for well: {well.Cluster?.Caption}/{well.Caption} loaded"); + var cacheItem = await GetOrDefaultCacheDataFromDbAsync(db, idTelemetry, capacity, hoursOffset, token); + if(cacheItem is not null) + { + caches.TryAdd(idTelemetry, cacheItem); + System.Diagnostics.Trace.TraceInformation($"cache<{typeof(TDto).Name}> for well: {well.Cluster?.Caption}/{well.Caption} loaded"); + } + else + { + System.Diagnostics.Trace.TraceInformation($"cache<{typeof(TDto).Name}> for well: {well.Cluster?.Caption}/{well.Caption} has no data"); + } } System.Diagnostics.Trace.TraceInformation($"cache<{typeof(TDto).Name}> load complete"); isLoading = false; } - private static IAsbCloudDbContext MakeContext(IConfiguration configuration) + private static async Task GetOrDefaultCacheDataFromDbAsync(IAsbCloudDbContext db, int idTelemetry, int capacity, double hoursOffset, CancellationToken token) + where TEntity : class, AsbCloudDb.Model.ITelemetryData { - var connectionString = configuration.GetConnectionString("DefaultConnection"); - var options = new DbContextOptionsBuilder() - .UseNpgsql(connectionString) - .Options; - var db = new AsbCloudDbContext(options); - return db; - } + var query = db.Set() + .Where(i => i.IdTelemetry == idTelemetry); - private static IEnumerable GetCacheDataFromDb(IAsbCloudDbContext db, int idTelemetry, int capacity, double hoursOffset) - where TEntity : class, ITelemetryData - { - var entities = db.Set() - .Where(i => i.IdTelemetry == idTelemetry) + var firstDbEntity = await query + .OrderBy(i => i.DateTime) + .FirstOrDefaultAsync(token); + + if (firstDbEntity is null) + return default; + + var first = firstDbEntity.Adapt(); + first.DateTime = firstDbEntity.DateTime.ToRemoteDateTime(hoursOffset); + + var entities = await query .OrderByDescending(i => i.DateTime) .Take(capacity) - .ToArray() + .ToArrayAsync(token); + + var dtos = entities .AsEnumerable() - .Reverse(); + .Reverse() + .Select(entity => { + var dto = entity.Adapt(); + dto.DateTime = entity.DateTime.ToRemoteDateTime(hoursOffset); + return dto; + }); - var dtos = entities.Select(entity => { - var dto = entity.Adapt(); - dto.DateTime = entity.DateTime.ToRemoteDateTime(hoursOffset); - return dto; - }); + var cacheItem = new CyclycArray(capacity); + cacheItem.AddRange(dtos); - return dtos; + var item = new TelemetryDataCacheItem + { + FirstByDate = first, + LastData = cacheItem, + }; + return item; } } } From b2b2682dbaa3d51e6c8bca777a6b62e4cd94a4c4 Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Fri, 19 May 2023 16:32:37 +0500 Subject: [PATCH 5/8] TelemetryDataBaseService replace telemetry tracer by telemetryDataCache. --- .../Services/SAUB/TelemetryDataBaseService.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs index 3dea46b9..54ab56c5 100644 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs +++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs @@ -101,19 +101,15 @@ namespace AsbCloudInfrastructure.Services.SAUB DateTimeOffset dateBeginUtc; if (dateBegin == default) { - dateBeginUtc = telemetryService.GetLastTelemetryDate(telemetry.Id) - .ToUtcDateTimeOffset(timezone.Hours); - if (dateBeginUtc != default) - dateBeginUtc = dateBeginUtc.AddSeconds(-intervalSec); + var dateRange = telemetryDataCache.GetOrDefaultDataDateRange(telemetry.Id); + dateBeginUtc = (dateRange?.To.ToUtcDateTimeOffset(timezone.Hours) ?? DateTime.UtcNow) + .AddSeconds(-intervalSec); } else { dateBeginUtc = dateBegin.ToUtcDateTimeOffset(timezone.Hours); } - if (dateBeginUtc == default) - dateBeginUtc = DateTime.UtcNow.AddSeconds(-intervalSec); - var cacheData = telemetryDataCache.GetOrDefault(telemetry.Id, dateBeginUtc.ToRemoteDateTime(timezone.Hours), intervalSec, approxPointsCount); if (cacheData is not null) return cacheData; From ed156b1ce8ad541f1c49415464aea0908d7120a5 Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Fri, 19 May 2023 17:57:07 +0500 Subject: [PATCH 6/8] remove TelemetryTracker --- AsbCloudApp/Services/ITelemetryService.cs | 12 -- AsbCloudApp/Services/ITelemetryTracker.cs | 33 ---- AsbCloudApp/Services/IWellService.cs | 2 +- AsbCloudInfrastructure/DependencyInjection.cs | 1 - .../Repository/DepositRepository.cs | 2 +- .../Services/SAUB/TelemetryDataBaseService.cs | 1 - .../Services/SAUB/TelemetryService.cs | 36 +--- .../Services/SAUB/TelemetryTracker.cs | 154 ------------------ .../OperationsStatService.cs | 2 +- .../Services/WellService.cs | 12 +- 10 files changed, 15 insertions(+), 240 deletions(-) delete mode 100644 AsbCloudApp/Services/ITelemetryTracker.cs delete mode 100644 AsbCloudInfrastructure/Services/SAUB/TelemetryTracker.cs diff --git a/AsbCloudApp/Services/ITelemetryService.cs b/AsbCloudApp/Services/ITelemetryService.cs index 2e6592b4..c46a32ec 100644 --- a/AsbCloudApp/Services/ITelemetryService.cs +++ b/AsbCloudApp/Services/ITelemetryService.cs @@ -16,11 +16,6 @@ namespace AsbCloudApp.Services /// ITimezoneService TimeZoneService { get; } - /// - /// трекер запросов - /// - ITelemetryTracker TelemetryTracker { get; } - /// /// получить idWell по uid телеметрии /// @@ -42,13 +37,6 @@ namespace AsbCloudApp.Services /// SimpleTimezoneDto GetTimezone(int idTelemetry); - /// - /// Получить дату получения последних данных - /// - /// - /// - DateTime GetLastTelemetryDate(int idTelemetry); - /// /// получить idTelemetry по IdWell /// diff --git a/AsbCloudApp/Services/ITelemetryTracker.cs b/AsbCloudApp/Services/ITelemetryTracker.cs deleted file mode 100644 index 5a6f255e..00000000 --- a/AsbCloudApp/Services/ITelemetryTracker.cs +++ /dev/null @@ -1,33 +0,0 @@ -using AsbCloudApp.Data; -using System; -using System.Collections.Generic; - -namespace AsbCloudApp.Services -{ - /// - /// Сервис статистики телеметрии - /// - public interface ITelemetryTracker - { - /// - /// получить дату последней отправки данных панелью - /// - /// - /// - DateTimeOffset GetLastTelemetryDateByUid(string uid); - - /// - /// получить диапазон дат за которые есть данные по телеметрии - /// - /// - /// - DatesRangeDto GetTelemetryDateRangeByUid(string uid); - - /// - /// обновить статистику по телеметрии - /// - /// - /// - void SaveRequestDate(string uid, DateTimeOffset remoteDate); - } -} diff --git a/AsbCloudApp/Services/IWellService.cs b/AsbCloudApp/Services/IWellService.cs index 057b3444..eb8d9f37 100644 --- a/AsbCloudApp/Services/IWellService.cs +++ b/AsbCloudApp/Services/IWellService.cs @@ -63,7 +63,7 @@ namespace AsbCloudApp.Services /// /// /// - DateTimeOffset GetLastTelemetryDate(int idWell); + DateTime GetLastTelemetryDate(int idWell); //TODO: выяснить и удалить отсюда /// diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index 41151953..7ee76fe2 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -102,7 +102,6 @@ namespace AsbCloudInfrastructure services.AddSingleton(new WitsInfoService()); services.AddSingleton(provider => TelemetryDataCache.GetInstance(provider)); services.AddSingleton(provider => TelemetryDataCache.GetInstance(provider)); - services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(provider => ReduceSamplingService.GetInstance(configuration)); diff --git a/AsbCloudInfrastructure/Repository/DepositRepository.cs b/AsbCloudInfrastructure/Repository/DepositRepository.cs index 4944f79d..7f19f1d7 100644 --- a/AsbCloudInfrastructure/Repository/DepositRepository.cs +++ b/AsbCloudInfrastructure/Repository/DepositRepository.cs @@ -111,7 +111,7 @@ namespace AsbCloudInfrastructure.Repository { var dto = well.Adapt(); dto.WellType = well.WellType.Caption; - dto.LastTelemetryDate = wellService.GetLastTelemetryDate(well.Id).DateTime; + dto.LastTelemetryDate = wellService.GetLastTelemetryDate(well.Id); dto.Cluster = gCluster.Key.Caption; dto.Deposit = gDeposit.Key.Caption; return dto; diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs index 54ab56c5..359efd18 100644 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs +++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs @@ -64,7 +64,6 @@ namespace AsbCloudInfrastructure.Services.SAUB }); var entityMaxDate = entities.Max(e => e.DateTime); - telemetryService.TelemetryTracker.SaveRequestDate(uid, entityMaxDate); var dbset = db.Set(); var stopwatch = Stopwatch.StartNew(); diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryService.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryService.cs index 593bf981..1f841aad 100644 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryService.cs +++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryService.cs @@ -19,21 +19,20 @@ namespace AsbCloudInfrastructure.Services.SAUB { private readonly IAsbCloudDbContext db; private readonly IMemoryCache memoryCache; - private readonly ITelemetryTracker telemetryTracker; + private readonly TelemetryDataCache dataSaubCache; private readonly ITimezoneService timezoneService; public ITimezoneService TimeZoneService => timezoneService; - public ITelemetryTracker TelemetryTracker => telemetryTracker; public TelemetryService( IAsbCloudDbContext db, IMemoryCache memoryCache, - ITelemetryTracker telemetryTracker, + TelemetryDataCache dataSaubCache, ITimezoneService timezoneService) { this.db = db; this.memoryCache = memoryCache; - this.telemetryTracker = telemetryTracker; + this.dataSaubCache = dataSaubCache; this.timezoneService = timezoneService; } @@ -47,34 +46,11 @@ namespace AsbCloudInfrastructure.Services.SAUB memoryCache.DropBasic(); } - public DateTime GetLastTelemetryDate(int idTelemetry) - { - var telemetry = GetTelemetryCache().FirstOrDefault(t => t.Id == idTelemetry); - - if (telemetry is null) - throw new Exception($"Telemetry id:{idTelemetry} does not exist"); - - var uid = telemetry.RemoteUid; - var timzone = GetTimezone(idTelemetry); - var lastTelemetryDate = telemetryTracker.GetLastTelemetryDateByUid(uid); - return lastTelemetryDate.ToRemoteDateTime(timzone.Hours); - } - public DatesRangeDto GetDatesRange(int idTelemetry) { - var telemetry = GetTelemetryCache().FirstOrDefault(t => t.Id == idTelemetry); - if (telemetry is null) - throw new Exception($"Telemetry id:{idTelemetry} does not exist"); - - var dto = TelemetryTracker.GetTelemetryDateRangeByUid(telemetry.RemoteUid); - if (dto is null) - throw new Exception($"Telemetry id:{idTelemetry} has no data"); - - var timezone = GetTimezone(idTelemetry); - dto.From = dto.From.ToTimeZoneOffsetHours(timezone.Hours); - dto.To = dto.To.ToTimeZoneOffsetHours(timezone.Hours); - - return dto; + var cacheDataRange = dataSaubCache.GetOrDefaultDataDateRange(idTelemetry) + ?? new (); + return cacheDataRange; } public int GetOrCreateTelemetryIdByUid(string uid) diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryTracker.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryTracker.cs deleted file mode 100644 index 588a4b87..00000000 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryTracker.cs +++ /dev/null @@ -1,154 +0,0 @@ -using AsbCloudApp.Data; -using AsbCloudApp.Services; -using AsbCloudDb.Model; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.Configuration; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace AsbCloudInfrastructure.Services.SAUB -{ - - public class TelemetryTracker : ITelemetryTracker - { - class TrackerStat - { - //public int Id { get; set; } - - public string RemoteUid { get; set; } = null!; - - /// - /// Время последнего запроса (по времени сервера) - /// - public DateTimeOffset LastTimeServer { get; set; } - - /// - /// Дата первых данных в БД - /// - public DateTimeOffset TelemetryDateUtcMin { get; set; } - - /// - /// Дата последних данных в БД - /// - public DateTimeOffset TelemetryDateUtcMax { get; set; } - - } - - private readonly ConcurrentDictionary telemetriesStats; - - public TelemetryTracker(IConfiguration configuration, IMemoryCache memoryCache) - { - // TODO: make this background work - var contextOptions = new DbContextOptionsBuilder() - .UseNpgsql(configuration.GetConnectionString("DefaultConnection")) - .Options; - var db = new AsbCloudDbContext(contextOptions); - - var cacheTelemetry = memoryCache.GetOrCreateBasic(db.Set().Include(t=>t.Well)); - var keyValuePairs = new Dictionary(cacheTelemetry.Count()); - foreach (var telemetry in cacheTelemetry) - { - var date = telemetry.Info?.DrillingStartDate - ?? ParseDateFromUidOrDefault(telemetry.RemoteUid, DateTimeOffset.MinValue); - - keyValuePairs[telemetry.RemoteUid] = new TrackerStat - { - RemoteUid = telemetry.RemoteUid, - TelemetryDateUtcMin = date, - TelemetryDateUtcMax = date, - LastTimeServer = date, - }; - } - telemetriesStats = new ConcurrentDictionary(keyValuePairs); - - Task.Run(async () => - { - db.Database.SetCommandTimeout(2 * 60); - var dates = await db.TelemetryDataSaub - .GroupBy(d => d.IdTelemetry) - .Select(g => new - { - IdTelemetry = g.Key, - DateMax = g.Max(d => d.DateTime), - DateMin = g.Min(d => d.DateTime), - }) - .AsNoTracking() - .ToListAsync() - .ConfigureAwait(false); - - var oldRequests = dates.Select(t => new - { - Uid = cacheTelemetry.FirstOrDefault(c => c.Id == t.IdTelemetry)?.RemoteUid, - t.DateMax, - t.DateMin, - }).Where(s => !string.IsNullOrEmpty(s.Uid)); - - foreach (var oldReq in oldRequests) - { - if (oldReq.Uid is not null) - { - var telemetryStat = telemetriesStats.GetOrAdd(oldReq.Uid, (uid) => new TrackerStat { RemoteUid = uid }); - telemetryStat.TelemetryDateUtcMin = oldReq.DateMin; - telemetryStat.TelemetryDateUtcMax = oldReq.DateMax; - telemetryStat.LastTimeServer = oldReq.DateMax; - } - - } - }).ContinueWith((t) => - { - db.Dispose(); - return t; - }); - } - - private static DateTimeOffset ParseDateFromUidOrDefault(string remoteUid, DateTimeOffset defaultValue = default) - { - //example: uid = 20211102_173407926 - if (string.IsNullOrEmpty(remoteUid) || remoteUid.Length != 18) - return defaultValue; - - if (DateTime.TryParseExact(remoteUid, "yyyyMMdd_HHmmssfff", - System.Globalization.CultureInfo.InvariantCulture, - System.Globalization.DateTimeStyles.AssumeUniversal, - out DateTime parsedDate)) - return parsedDate; - - return defaultValue; - } - - public void SaveRequestDate(string uid, DateTimeOffset remoteDate) - { - var stat = telemetriesStats.GetOrAdd(uid, _ => new TrackerStat - { - RemoteUid = uid, - TelemetryDateUtcMin = remoteDate - } - ); - - stat.LastTimeServer = DateTime.Now; - - if (stat.TelemetryDateUtcMax.ToUniversalTime() < remoteDate.ToUniversalTime()) - stat.TelemetryDateUtcMax = remoteDate; - } - - public DateTimeOffset GetLastTelemetryDateByUid(string uid) => - telemetriesStats.GetValueOrDefault(uid)?.TelemetryDateUtcMax ?? default; - - public DatesRangeDto GetTelemetryDateRangeByUid(string uid) - { - var stat = telemetriesStats.GetValueOrDefault(uid); - var range = new DatesRangeDto - { - From = stat?.TelemetryDateUtcMin.UtcDateTime ?? default, - To = stat?.TelemetryDateUtcMax.UtcDateTime ?? default, - }; - return range; - } - - } - -} diff --git a/AsbCloudInfrastructure/Services/WellOperationService/OperationsStatService.cs b/AsbCloudInfrastructure/Services/WellOperationService/OperationsStatService.cs index 408c895c..c32956d8 100644 --- a/AsbCloudInfrastructure/Services/WellOperationService/OperationsStatService.cs +++ b/AsbCloudInfrastructure/Services/WellOperationService/OperationsStatService.cs @@ -151,7 +151,7 @@ namespace AsbCloudInfrastructure.Services.WellOperationService WellType = wellType?.Caption ?? "", IdState = well.IdState, State = wellService.GetStateText(well.IdState), - LastTelemetryDate = wellService.GetLastTelemetryDate(well.Id).DateTime, + LastTelemetryDate = wellService.GetLastTelemetryDate(well.Id), Companies = await wellService.GetCompaniesAsync(well.Id, token) }; diff --git a/AsbCloudInfrastructure/Services/WellService.cs b/AsbCloudInfrastructure/Services/WellService.cs index 2d2599b8..711b495e 100644 --- a/AsbCloudInfrastructure/Services/WellService.cs +++ b/AsbCloudInfrastructure/Services/WellService.cs @@ -57,15 +57,15 @@ namespace AsbCloudInfrastructure.Services private void DropCacheRelationCompanyWell() => memoryCache.DropBasic(); - public DateTimeOffset GetLastTelemetryDate(int idWell) + public DateTime GetLastTelemetryDate(int idWell) { var well = GetOrDefault(idWell); if (well?.IdTelemetry is null) - return DateTimeOffset.MinValue; + return DateTime.MinValue; - var lastTelemetryDate = telemetryService.GetLastTelemetryDate((int)well.IdTelemetry); - return lastTelemetryDate; + var datesRange = telemetryService.GetDatesRange(well.IdTelemetry.Value); + return datesRange.To; } /// @@ -97,7 +97,7 @@ namespace AsbCloudInfrastructure.Services dto.Latitude ??= gCluster.Key.Latitude ?? gDeposit.Key.Latitude; dto.Longitude ??= gCluster.Key.Longitude ?? gDeposit.Key.Longitude; if (well.IdTelemetry is not null) - dto.LastTelemetryDate = telemetryService.GetLastTelemetryDate(well.IdTelemetry.Value); + dto.LastTelemetryDate = telemetryService.GetDatesRange(well.IdTelemetry.Value).To; return dto; }), @@ -256,7 +256,7 @@ namespace AsbCloudInfrastructure.Services dto.Cluster = entity.Cluster.Caption; dto.Deposit = entity.Cluster.Deposit.Caption; if (entity.IdTelemetry is not null) - dto.LastTelemetryDate = telemetryService.GetLastTelemetryDate((int)entity.IdTelemetry); + dto.LastTelemetryDate = telemetryService.GetDatesRange(entity.IdTelemetry.Value).To; dto.Companies = entity.RelationCompaniesWells .Select(r => Convert(r.Company)) .ToList(); From 9347e9610b62cf8a9f22186be791f9bfa2ea800f Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Mon, 22 May 2023 10:20:45 +0500 Subject: [PATCH 7/8] =?UTF-8?q?WellInfoService=20=D0=B8=D1=81=D0=BF=D0=BE?= =?UTF-8?q?=D0=BB=D1=8C=D0=B7=D1=83=D0=B5=D1=82=20=D0=BA=D0=B5=D1=88=20?= =?UTF-8?q?=D1=82=D0=B5=D0=BB=D0=B5=D0=BC=D0=B5=D1=82=D1=80=D0=B8=D0=B8=20?= =?UTF-8?q?=D0=B2=D0=BC=D0=B5=D1=81=D1=82=D0=BE=20=D0=B4=D0=BE=D0=BB=D0=B3?= =?UTF-8?q?=D0=BE=D0=B3=D0=BE=20=D0=B7=D0=B0=D0=BF=D1=80=D0=BE=D1=81=D0=B0?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/SAUB/TelemetryDataCache.cs | 8 +++ .../Services/WellInfoService.cs | 54 +++++++++---------- 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataCache.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataCache.cs index bb3bb20c..53089f17 100644 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataCache.cs +++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataCache.cs @@ -125,6 +125,14 @@ namespace AsbCloudInfrastructure.Services.SAUB return items; } + public TDto? GetLastOrDefault(int idTelemetry) + { + if (!caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? cacheItem)) + return default; + + return cacheItem.LastData.LastOrDefault(); + } + public DatesRangeDto? GetOrDefaultDataDateRange(int idTelemetry) { if (!caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? cacheItem)) diff --git a/AsbCloudInfrastructure/Services/WellInfoService.cs b/AsbCloudInfrastructure/Services/WellInfoService.cs index f37cb2f6..e298fe7c 100644 --- a/AsbCloudInfrastructure/Services/WellInfoService.cs +++ b/AsbCloudInfrastructure/Services/WellInfoService.cs @@ -1,13 +1,14 @@ using AsbCloudApp.Data; using AsbCloudApp.Data.ProcessMap; +using AsbCloudApp.Data.SAUB; using AsbCloudApp.Repositories; using AsbCloudApp.Requests; using AsbCloudApp.Services; using AsbCloudApp.Services.Subsystems; using AsbCloudDb.Model; using AsbCloudInfrastructure.Background; +using AsbCloudInfrastructure.Services.SAUB; using Mapster; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; @@ -46,7 +47,8 @@ namespace AsbCloudInfrastructure.Services var operationsStatService = serviceProvider.GetRequiredService(); var processMapRepository = serviceProvider.GetRequiredService(); var subsystemOperationTimeService = serviceProvider.GetRequiredService(); - + var telemetryDataSaubCache = serviceProvider.GetRequiredService>(); + var activeWells = await wellService.GetAsync(new() {IdState = 1}, token); IEnumerable activeWellsIds = activeWells @@ -56,21 +58,6 @@ namespace AsbCloudInfrastructure.Services .Where(w => w.IdTelemetry != null) .Select(t => t.IdTelemetry); - var lastTelemetryInfo = await db.TelemetryDataSaub - .Where(t => idTelemetries.Contains(t.IdTelemetry)) - .Select(t => new - { - t.IdTelemetry, - t.WellDepth, - t.DateTime, - }) - .GroupBy(t => t.IdTelemetry) - .Select(g => g.OrderByDescending(t => t.DateTime) - .First() - ) - .AsNoTracking() - .ToArrayAsync(token); - var processMapRequests = activeWellsIds.Select(id => new ProcessMapRequest { IdWell = id }); var processMaps = await processMapRepository.GetProcessMapAsync(processMapRequests, token); @@ -83,40 +70,46 @@ namespace AsbCloudInfrastructure.Services }); var operationsStat = await operationsStatService.GetWellsStatAsync(activeWellsIds, token); + var subsystemStat = await subsystemOperationTimeService.GetStatByActiveWells(activeWellsIds, token); WellMapInfo = activeWells.Select(well => { var wellMapInfo = well.Adapt(); - - var wellLastTelemetryInfo = lastTelemetryInfo.FirstOrDefault(t => t.IdTelemetry == well.IdTelemetry); - - var wellOperationsStat = operationsStat.FirstOrDefault(s => s.Id == well.Id); + var wellOperationsStat = operationsStat.FirstOrDefault(s => s.Id == well.Id); var wellLastFactSection = wellOperationsStat?.Sections.LastOrDefault(s => s.Fact is not null); - var wellSubsystemStat = subsystemStat.FirstOrDefault(s => s.Well.Id == well.Id); + double? currentDepth = null; + DateTime lastTelemetryDate = default; - double currentDepth = wellLastTelemetryInfo?.WellDepth - ?? wellLastFactSection?.Fact?.WellDepthEnd - ?? 0d; + if (well.IdTelemetry.HasValue) + { + var lastSaubTelemetry = telemetryDataSaubCache.GetLastOrDefault(well.IdTelemetry.Value); + if(lastSaubTelemetry is not null) + { + currentDepth = lastSaubTelemetry.WellDepth; + lastTelemetryDate = lastSaubTelemetry.DateTime; + } + } + + currentDepth ??= wellLastFactSection?.Fact?.WellDepthEnd; var wellProcessMaps = processMaps .Where(p => p.IdWell == well.Id) .OrderBy(p => p.DepthEnd); int? idSection = wellLastFactSection?.Id; + ProcessMapPlanDto? welllProcessMap = null; - ProcessMapPlanDto? welllProcessMap; - if (idSection is not null) + if (idSection.HasValue) { welllProcessMap = wellProcessMaps.FirstOrDefault(p => p.IdWellSectionType == idSection); } - else + else if(currentDepth.HasValue) { - welllProcessMap = wellProcessMaps.FirstOrDefault(p => p.DepthStart <= currentDepth && p.DepthEnd >= currentDepth); + welllProcessMap = wellProcessMaps.FirstOrDefault(p => p.DepthStart <= currentDepth.Value && p.DepthEnd >= currentDepth.Value); idSection ??= welllProcessMap?.IdWellSectionType; } - wellMapInfo.LastTelemetryDate = wellLastTelemetryInfo?.DateTime.ToRemoteDateTime(5) ?? new DateTime(); wellMapInfo.WellDepth = new() { Plan = wellDepthByProcessMap.FirstOrDefault(p => p.Id == well.Id)?.DepthEnd, @@ -135,6 +128,7 @@ namespace AsbCloudInfrastructure.Services Fact = wellOperationsStat?.Total.Fact?.RouteSpeed, }; + var wellSubsystemStat = subsystemStat.FirstOrDefault(s => s.Well.Id == well.Id); wellMapInfo.SaubUsage = wellSubsystemStat?.SubsystemAKB?.KUsage ?? 0d; wellMapInfo.SpinUsage = wellSubsystemStat?.SubsystemSpinMaster?.KUsage ?? 0d; wellMapInfo.TvdLagPercent = wellOperationsStat?.TvdLagDays ?? 0d; From 2415647dce12daba7099d4eaef558ed9a63df707 Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Mon, 22 May 2023 10:52:27 +0500 Subject: [PATCH 8/8] WellInfoService add another source for planned well depth. --- AsbCloudInfrastructure/Services/WellInfoService.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/AsbCloudInfrastructure/Services/WellInfoService.cs b/AsbCloudInfrastructure/Services/WellInfoService.cs index e298fe7c..d24b04a3 100644 --- a/AsbCloudInfrastructure/Services/WellInfoService.cs +++ b/AsbCloudInfrastructure/Services/WellInfoService.cs @@ -79,6 +79,7 @@ namespace AsbCloudInfrastructure.Services var wellLastFactSection = wellOperationsStat?.Sections.LastOrDefault(s => s.Fact is not null); double? currentDepth = null; + double? planTotalDepth = null; DateTime lastTelemetryDate = default; if (well.IdTelemetry.HasValue) @@ -110,9 +111,12 @@ namespace AsbCloudInfrastructure.Services idSection ??= welllProcessMap?.IdWellSectionType; } + planTotalDepth = wellDepthByProcessMap.FirstOrDefault(p => p.Id == well.Id)?.DepthEnd; + planTotalDepth ??= wellOperationsStat?.Total.Plan?.WellDepthEnd; + wellMapInfo.WellDepth = new() { - Plan = wellDepthByProcessMap.FirstOrDefault(p => p.Id == well.Id)?.DepthEnd, + Plan = planTotalDepth, Fact = currentDepth, };