using AsbCloudDb.Model; using System.Collections.Concurrent; using System.Collections.Generic; using System; using System.Linq; using Microsoft.EntityFrameworkCore; using Mapster; #nullable enable namespace AsbCloudInfrastructure.Services.SAUB { public class TelemetryDataCache where TDto : AsbCloudApp.Data.ITelemetryData where TEntity : class, ITelemetryData { private const int activeWellCapacity = 24 * 60 * 60; private const int doneWellCapacity = 65 * 60; private readonly ConcurrentDictionary> caches; public TelemetryDataCache(IAsbCloudDbContext db) { caches = new (); LoadCaches(db); } private void LoadCaches(IAsbCloudDbContext db) { Well[] wells = db.Set() .Include(well => well.Telemetry) .Where(well => well.IdTelemetry != null) .ToArray(); foreach (Well well in wells) { var capacity = well.IdState == 1 ? activeWellCapacity : doneWellCapacity; 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); } } private static IEnumerable GetCacheDataFromDb(IAsbCloudDbContext db, int idTelemetry, int capacity, double hoursOffset) { var entities = db.Set() .Where(i => i.IdTelemetry == idTelemetry) .OrderByDescending(i => i.DateTime) .Take(capacity) .ToArray() .OrderBy(i => i.DateTime); var dtos = entities.Select(entity => { var dto = entity.Adapt(); dto.DateTime = entity.DateTime.ToRemoteDateTime(hoursOffset); return dto; }); return dtos; } /// /// Добавить элементы в кеш /// /// /// public void AddRange(int idTelemetry, IEnumerable range) { var cacheItem = caches.GetOrAdd(idTelemetry, _ => new CyclycArray(activeWellCapacity)); var newItems = range .OrderBy(i => i.DateTime); foreach (var item in newItems) item.IdTelemetry = idTelemetry; cacheItem.AddRange(newItems); } /// /// Получить данные из кеша.
/// Если dateBegin меньше минимального элемента в кеше, то вернется null. /// Даже если intervalSec частично перекрыт данными из кеша. ///
/// /// /// /// кол-во элементов до которых эти данные прореживаются /// public IEnumerable? GetOrDefault(int idTelemetry, DateTime dateBegin, double intervalSec = 600d, int approxPointsCount = 1024) { if(!caches.TryGetValue(idTelemetry, out CyclycArray? cacheItem)) return null; if (cacheItem is null || cacheItem[0].DateTime > dateBegin) return null; var dateEnd = dateBegin.AddSeconds(intervalSec); var items = cacheItem .Where(i => i.DateTime >= dateBegin && i.DateTime <= dateEnd); var ratio = items.Count() / approxPointsCount; if (ratio > 1) items = items .Where((_, index) => index % ratio == 0); return items; } } } #nullable disable