Fix telemetryDataDateRange timezone offset

This commit is contained in:
ngfrolov 2024-06-24 17:05:04 +05:00
parent 07be69a368
commit a213c27fbf
Signed by: ng.frolov
GPG Key ID: E99907A0357B29A7
4 changed files with 338 additions and 334 deletions

View File

@ -49,7 +49,7 @@ namespace AsbCloudApp.Repositories
/// </summary> /// </summary>
/// <param name="idTelemetry"></param> /// <param name="idTelemetry"></param>
/// <returns></returns> /// <returns></returns>
DatesRangeDto? GetOrDefaultCachedDateRange(int idTelemetry); DatesRangeDto? GetOrDefaultCachedDataDateRange(int idTelemetry);
/// <summary> /// <summary>
/// Получить диапазон дат телеметрии. /// Получить диапазон дат телеметрии.
@ -57,7 +57,7 @@ namespace AsbCloudApp.Repositories
/// </summary> /// </summary>
/// <param name="idTelemetry"></param> /// <param name="idTelemetry"></param>
/// <returns></returns> /// <returns></returns>
DatesRangeDto? GetOrDefaultDataDateRange(int idTelemetry); DatesRangeDto? GetOrDefaultWellDataDateRange(int idTelemetry);
/// <summary> /// <summary>
/// Получение первой и последней записи телеметрии. /// Получение первой и последней записи телеметрии.

View File

@ -12,6 +12,7 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsbCloudApp.Requests; using AsbCloudApp.Requests;
using Mapster;
namespace AsbCloudInfrastructure.Services.SAUB namespace AsbCloudInfrastructure.Services.SAUB
{ {
@ -67,10 +68,10 @@ namespace AsbCloudInfrastructure.Services.SAUB
}); });
var stopwatch = Stopwatch.StartNew(); var stopwatch = Stopwatch.StartNew();
var dbset = db.Set<TEntity>(); var dbSet = db.Set<TEntity>();
try try
{ {
return await db.Database.ExecInsertOrUpdateAsync(dbset, entities, token).ConfigureAwait(false); return await db.Database.ExecInsertOrUpdateAsync(dbSet, entities, token).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -101,7 +102,7 @@ namespace AsbCloudInfrastructure.Services.SAUB
DateTimeOffset dateBeginUtc; DateTimeOffset dateBeginUtc;
if (dateBegin == default) if (dateBegin == default)
{ {
var dateRange = telemetryDataCache.GetOrDefaultDataDateRange(telemetry.Id); var dateRange = telemetryDataCache.GetOrDefaultWellDataDateRange(telemetry.Id);
dateBeginUtc = (dateRange?.To.ToUniversalTime() ?? DateTimeOffset.UtcNow) dateBeginUtc = (dateRange?.To.ToUniversalTime() ?? DateTimeOffset.UtcNow)
.AddSeconds(-intervalSec); .AddSeconds(-intervalSec);
} }
@ -226,17 +227,17 @@ namespace AsbCloudInfrastructure.Services.SAUB
if ((DateTimeOffset.UtcNow - geDate) < TimeSpan.FromHours(12)) if ((DateTimeOffset.UtcNow - geDate) < TimeSpan.FromHours(12))
{ {
// пробуем обойтись кешем // пробуем обойтись кэшем
var cechedRange = telemetryDataCache.GetOrDefaultCachedDateRange(telemetry.Id); var cachedRange = telemetryDataCache.GetOrDefaultCachedDataDateRange(telemetry.Id);
if (cechedRange?.From <= geDate) if (cachedRange is not null)
{ {
var datesRange = new DatesRangeDto var datesRange = new DatesRangeDto {From = cachedRange.From, To = cachedRange.To };
{ if (geDate >= cachedRange.From)
From = geDate.DateTime, datesRange.From = geDate.ToOffset(cachedRange.From.Offset);
To = cechedRange.To
}; if (leDate.HasValue && leDate <= cachedRange.To)
if (leDate.HasValue && leDate > geDate) datesRange.To = leDate.Value.ToOffset(cachedRange.To.Offset);
datesRange.To = leDate.Value.Date;
return datesRange; return datesRange;
} }
} }
@ -248,7 +249,7 @@ namespace AsbCloudInfrastructure.Services.SAUB
if(leDate.HasValue) if(leDate.HasValue)
query = query.Where(entity => entity.DateTime <= leDate.Value.ToUniversalTime()); query = query.Where(entity => entity.DateTime <= leDate.Value.ToUniversalTime());
var gquery = query var groupQuery = query
.GroupBy(entity => entity.IdTelemetry) .GroupBy(entity => entity.IdTelemetry)
.Select(group => new .Select(group => new
{ {
@ -256,14 +257,14 @@ namespace AsbCloudInfrastructure.Services.SAUB
MaxDate = group.Max(entity => entity.DateTime), MaxDate = group.Max(entity => entity.DateTime),
}); });
var result = await gquery.FirstOrDefaultAsync(token); var result = await groupQuery.FirstOrDefaultAsync(token);
if (result is null) if (result is null)
return null; return null;
var range = new DatesRangeDto var range = new DatesRangeDto
{ {
From = result.MinDate.ToOffset(TimeSpan.FromHours(telemetry.TimeZone!.Hours)).DateTime, From = result.MinDate.ToOffset(telemetry.TimeZone!.Offset),
To = result.MaxDate.ToOffset(TimeSpan.FromHours(telemetry.TimeZone!.Hours)).DateTime, To = result.MaxDate.ToOffset(telemetry.TimeZone!.Offset),
}; };
return range; return range;
} }
@ -274,7 +275,7 @@ namespace AsbCloudInfrastructure.Services.SAUB
if (telemetry is null) if (telemetry is null)
return default; return default;
return telemetryDataCache.GetOrDefaultDataDateRange(telemetry.Id); return telemetryDataCache.GetOrDefaultWellDataDateRange(telemetry.Id);
} }
protected abstract TDto Convert(TEntity src, double timezoneOffset); protected abstract TDto Convert(TEntity src, double timezoneOffset);

View File

@ -13,347 +13,350 @@ using AsbCloudApp.Data;
using AsbCloudApp.Requests; using AsbCloudApp.Requests;
using AsbCloudApp.Repositories; using AsbCloudApp.Repositories;
namespace AsbCloudInfrastructure.Services.SAUB namespace AsbCloudInfrastructure.Services.SAUB;
public class TelemetryDataCache<TDto> : ITelemetryDataCache<TDto> where TDto : AsbCloudApp.Data.ITelemetryData
{ {
public class TelemetryDataCache<TDto> : ITelemetryDataCache<TDto> where TDto : AsbCloudApp.Data.ITelemetryData class TelemetryDataCacheItem
{ {
class TelemetryDataCacheItem public TDto FirstByDate { get; init; } = default!;
public CyclicArray<TDto> LastData { get; init; } = null!;
public double TimezoneHours { get; init; } = 5;
public TimeSpan TimezoneOffset => TimeSpan.FromHours(TimezoneHours);
}
private const int activeWellCapacity = 12 * 60 * 60;
private const int doneWellCapacity = 65 * 60;
// key == idTelemetry
private readonly ConcurrentDictionary<int, TelemetryDataCacheItem> caches;
private bool isLoading = false;
private TelemetryDataCache()
{
caches = new();
}
private static TelemetryDataCache<TDto>? instance;
public static TelemetryDataCache<TDto> GetInstance<TEntity>(IServiceProvider provider)
where TEntity : class, AsbCloudDb.Model.ITelemetryData
{
if (instance is null)
{ {
public TDto FirstByDate { get; init; } = default!; instance = new TelemetryDataCache<TDto>();
public CyclicArray<TDto> LastData { get; init; } = null!; var worker = provider.GetRequiredService<BackgroundWorker>();
public double TimezoneHours { get; init; } = 5; var workId = $"Telemetry cache loading from DB {typeof(TEntity).Name}";
var work = Work.CreateByDelegate(workId, async (workId, provider, onProgress, token) =>
{
var db = provider.GetRequiredService<IAsbCloudDbContext>();
await instance.InitializeCacheFromDBAsync<TEntity>(db, onProgress, token);
});
work.Timeout = TimeSpan.FromMinutes(15);
worker.Enqueue(work);
}
return instance;
}
/// <summary>
/// Добавить новые элементы в кеш
/// </summary>
/// <param name="idTelemetry"></param>
/// <param name="range"></param>
public void AddRange(int idTelemetry, IEnumerable<TDto> range)
{
if (!range.Any())
return;
range = range.OrderBy(x => x.DateTime);
foreach (var item in range)
item.IdTelemetry = idTelemetry;
TelemetryDataCacheItem cacheItem;
if (isLoading)
{
if (caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? localCacheItem))
cacheItem = localCacheItem;
else
return;
}
else
{
cacheItem = caches.GetOrAdd(idTelemetry, _ => new TelemetryDataCacheItem()
{
FirstByDate = range.ElementAt(0),
LastData = new CyclicArray<TDto>(activeWellCapacity)
});
} }
private const int activeWellCapacity = 12 * 60 * 60; cacheItem.LastData.AddRange(range);
private const int doneWellCapacity = 65 * 60; }
// key == idTelemetry /// <summary>
private readonly ConcurrentDictionary<int, TelemetryDataCacheItem> caches; /// Получить данные из кеша. <br/>
private bool isLoading = false; /// Если dateBegin меньше минимального элемента в кеше, то вернется null.
/// Даже если intervalSec частично перекрыт данными из кеша.
/// </summary>
/// <param name="idTelemetry"></param>
/// <param name="dateBegin"></param>
/// <param name="intervalSec"></param>
/// <param name="approxPointsCount">кол-во элементов до которых эти данные прореживаются</param>
/// <returns></returns>
public IEnumerable<TDto>? GetOrDefault(int idTelemetry, DateTime dateBegin, double intervalSec = 600d, int approxPointsCount = 1024)
{
if (!caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? cacheItem))
return null;
private TelemetryDataCache() var cacheLastData = cacheItem.LastData;
if (!cacheLastData.Any() || cacheLastData[0].DateTime > dateBegin)
return null;
var dateEnd = dateBegin.AddSeconds(intervalSec);
var items = cacheLastData
.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;
}
public IEnumerable<TelemetryDataStatDto> GetStat()
{
var result = caches.Select(cacheItem => new TelemetryDataStatDto
{ {
caches = new(); IdTelemetry = cacheItem.Key,
} DateFirst = cacheItem.Value.FirstByDate.DateTime,
DateLast = cacheItem.Value.LastData[^1].DateTime,
TimezoneOffsetHours = cacheItem.Value.TimezoneHours,
});
return result;
}
private static TelemetryDataCache<TDto>? instance; public virtual TDto? GetLastOrDefault(int idTelemetry)
{
if (!caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? cacheItem))
return default;
public static TelemetryDataCache<TDto> GetInstance<TEntity>(IServiceProvider provider) return cacheItem.LastData.LastOrDefault();
where TEntity : class, AsbCloudDb.Model.ITelemetryData }
{
if (instance is null)
{
instance = new TelemetryDataCache<TDto>();
var worker = provider.GetRequiredService<BackgroundWorker>();
var workId = $"Telemetry cache loading from DB {typeof(TEntity).Name}";
var work = Work.CreateByDelegate(workId, async (workId, provider, onProgress, token) =>
{
var db = provider.GetRequiredService<IAsbCloudDbContext>();
await instance.InitializeCacheFromDBAsync<TEntity>(db, onProgress, token);
});
work.Timeout = TimeSpan.FromMinutes(15);
worker.Enqueue(work);
}
return instance;
}
/// <summary> public DatesRangeDto? GetOrDefaultWellDataDateRange(int idTelemetry)
/// Добавить новые элементы в кеш {
/// </summary> if (!caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? cacheItem))
/// <param name="idTelemetry"></param> return null;
/// <param name="range"></param>
public void AddRange(int idTelemetry, IEnumerable<TDto> range) if (!cacheItem.LastData.Any())
{ return null;
if (!range.Any())
return;
range = range.OrderBy(x => x.DateTime); var to = FromDate(cacheItem.FirstByDate.DateTime, cacheItem.TimezoneOffset);
var from = FromDate(cacheItem.LastData[^1].DateTime, cacheItem.TimezoneOffset);
foreach (var item in range) return new DatesRangeDto { From = from, To = to };
item.IdTelemetry = idTelemetry; }
TelemetryDataCacheItem cacheItem; public DatesRangeDto? GetOrDefaultCachedDataDateRange(int idTelemetry)
if (isLoading) {
{ if (!caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? cacheItem))
if (caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? localCacheItem)) return null;
cacheItem = localCacheItem;
else
return;
}
else
{
cacheItem = caches.GetOrAdd(idTelemetry, _ => new TelemetryDataCacheItem()
{
FirstByDate = range.ElementAt(0),
LastData = new CyclicArray<TDto>(activeWellCapacity)
});
}
cacheItem.LastData.AddRange(range);
}
/// <summary> if (cacheItem.LastData.Count < 2)
/// Получить данные из кеша. <br/> return null;
/// Если dateBegin меньше минимального элемента в кеше, то вернется null.
/// Даже если intervalSec частично перекрыт данными из кеша.
/// </summary>
/// <param name="idTelemetry"></param>
/// <param name="dateBegin"></param>
/// <param name="intervalSec"></param>
/// <param name="approxPointsCount">кол-во элементов до которых эти данные прореживаются</param>
/// <returns></returns>
public IEnumerable<TDto>? GetOrDefault(int idTelemetry, DateTime dateBegin, double intervalSec = 600d, int approxPointsCount = 1024)
{
if (!caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? cacheItem))
return null;
var cacheLastData = cacheItem.LastData; var to = FromDate(cacheItem.LastData[^1].DateTime, cacheItem.TimezoneOffset);
var from = FromDate(cacheItem.LastData[0].DateTime, cacheItem.TimezoneOffset);
if (!cacheLastData.Any() || cacheLastData[0].DateTime > dateBegin) return new DatesRangeDto { From = from, To = to };
return null; }
var dateEnd = dateBegin.AddSeconds(intervalSec); public (TDto First, TDto Last)? GetOrDefaultFirstLast(int idTelemetry)
var items = cacheLastData {
.Where(i => i.DateTime >= dateBegin && i.DateTime <= dateEnd); if (!caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? cacheItem))
return null;
var ratio = items.Count() / approxPointsCount; if (!cacheItem.LastData.Any())
if (ratio > 1) return null;
items = items
.Where((_, index) => index % ratio == 0);
return items; var last = cacheItem.LastData[^1];
} var first = cacheItem.FirstByDate;
return (first, last);
}
public IEnumerable<TelemetryDataStatDto> GetStat() private async Task InitializeCacheFromDBAsync<TEntity>(IAsbCloudDbContext db, Action<string, double?> onProgress, CancellationToken token)
{ where TEntity : class, AsbCloudDb.Model.ITelemetryData
var result = caches.Select(cacheItem => new TelemetryDataStatDto {
{ var defaultTimeout = db.Database.GetCommandTimeout();
IdTelemetry = cacheItem.Key, db.Database.SetCommandTimeout(TimeSpan.FromMinutes(5));
DateFirst = cacheItem.Value.FirstByDate.DateTime,
DateLast = cacheItem.Value.LastData[^1].DateTime,
TimezoneOffsetHours = cacheItem.Value.TimezoneHours,
});
return result;
}
public virtual TDto? GetLastOrDefault(int idTelemetry) if (isLoading)
{ throw new Exception("Multiple cache loading detected.");
if (!caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? cacheItem))
return default;
return cacheItem.LastData.LastOrDefault(); try
} {
isLoading = true;
public DatesRangeDto? GetOrDefaultDataDateRange(int idTelemetry)
{ Well[] wells = await db.Set<Well>()
if (!caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? cacheItem)) .Include(well => well.Telemetry)
return null; .Include(well => well.Cluster)
.Where(well => well.IdTelemetry != null)
if (!cacheItem.LastData.Any())
return null;
var from = DateTime.SpecifyKind(cacheItem.FirstByDate.DateTime, DateTimeKind.Unspecified);
var to = DateTime.SpecifyKind(cacheItem.LastData[^1].DateTime, DateTimeKind.Unspecified);
return new DatesRangeDto
{
From = new DateTimeOffset(from, TimeSpan.FromHours(cacheItem.TimezoneHours)),
To = new DateTimeOffset(to, TimeSpan.FromHours(cacheItem.TimezoneHours))
};
}
public DatesRangeDto? GetOrDefaultCachedDateRange(int idTelemetry)
{
if (!caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? cacheItem))
return null;
if (cacheItem.LastData.Count < 2)
return null;
var to = cacheItem.LastData[^1].DateTime;
var from = cacheItem.LastData[0].DateTime;
return new DatesRangeDto { From = from, To = to };
}
public (TDto First, TDto Last)? GetOrDefaultFirstLast(int idTelemetry)
{
if (!caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? cacheItem))
return null;
if (!cacheItem.LastData.Any())
return null;
var last = cacheItem.LastData[^1];
var first = cacheItem.FirstByDate;
return (first, last);
}
private async Task InitializeCacheFromDBAsync<TEntity>(IAsbCloudDbContext db, Action<string, double?> onProgress, CancellationToken token)
where TEntity : class, AsbCloudDb.Model.ITelemetryData
{
var defaultTimeout = db.Database.GetCommandTimeout();
db.Database.SetCommandTimeout(TimeSpan.FromMinutes(5));
if (isLoading)
throw new Exception("Multiple cache loading detected.");
try
{
isLoading = true;
Well[] wells = await db.Set<Well>()
.Include(well => well.Telemetry)
.Include(well => well.Cluster)
.Where(well => well.IdTelemetry != null)
.ToArrayAsync(token);
var count = wells.Length;
var i = 0d;
foreach (Well well in wells)
{
var capacity = well.IdState == 1
? activeWellCapacity
: doneWellCapacity;
var idTelemetry = well.IdTelemetry!.Value;
var hoursOffset = well.Timezone.Hours;
onProgress($"Loading for well: {well.Cluster?.Caption}/{well.Caption} (capacity:{capacity}) idTelemetry:{idTelemetry}", i++ / count);
var cacheItem = await GetOrDefaultCacheDataFromDbAsync<TEntity>(db, idTelemetry, capacity, hoursOffset, token);
if (cacheItem is not null)
caches.TryAdd(idTelemetry, cacheItem);
}
}
finally
{
isLoading = false;
db.Database.SetCommandTimeout(defaultTimeout);
}
}
private static async Task<TelemetryDataCacheItem?> GetOrDefaultCacheDataFromDbAsync<TEntity>(IAsbCloudDbContext db, int idTelemetry, int capacity, double hoursOffset, CancellationToken token)
where TEntity : class, AsbCloudDb.Model.ITelemetryData
{
var query = db.Set<TEntity>()
.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<TDto>();
first.DateTime = firstDbEntity.DateTime.ToRemoteDateTime(hoursOffset);
var entities = await query
.OrderByDescending(i => i.DateTime)
.Take(capacity)
.ToArrayAsync(token); .ToArrayAsync(token);
var dtos = entities var count = wells.Length;
.AsEnumerable() var i = 0d;
.Reverse() foreach (Well well in wells)
.Select(entity =>
{
var dto = entity.Adapt<TDto>();
dto.DateTime = entity.DateTime.ToRemoteDateTime(hoursOffset);
return dto;
});
var cacheItem = new CyclicArray<TDto>(capacity);
cacheItem.AddRange(dtos);
var item = new TelemetryDataCacheItem
{ {
FirstByDate = first, var capacity = well.IdState == 1
LastData = cacheItem, ? activeWellCapacity
TimezoneHours = hoursOffset, : doneWellCapacity;
};
return item; var idTelemetry = well.IdTelemetry!.Value;
var hoursOffset = well.Timezone.Hours;
onProgress($"Loading for well: {well.Cluster?.Caption}/{well.Caption} (capacity:{capacity}) idTelemetry:{idTelemetry}", i++ / count);
var cacheItem = await GetOrDefaultCacheDataFromDbAsync<TEntity>(db, idTelemetry, capacity, hoursOffset, token);
if (cacheItem is not null)
caches.TryAdd(idTelemetry, cacheItem);
}
} }
finally
public IEnumerable<TDto>? GetOrDefault(int idTelemetry, TelemetryDataRequest request)
{ {
if (!caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? cacheItem)) isLoading = false;
return null; db.Database.SetCommandTimeout(defaultTimeout);
IEnumerable<TDto> data = cacheItem.LastData;
if (!data.Any())
return null;
if (request.GeDate.HasValue)
{
var geDate = request.GeDate.Value.ToRemoteDateTime(cacheItem.TimezoneHours);
if (data.First().DateTime > geDate)
return null;
data = data.Where(d => d.DateTime >= geDate);
}
else
{
if (request.Order == 0)
return null;
}
if (request.LeDate.HasValue)
{
var leDate = request.LeDate.Value.ToRemoteDateTime(cacheItem.TimezoneHours);
data = data.Where(d => d.DateTime <= request.LeDate);
}
if (request.Divider > 1)
data = data.Where((d) => (((d.DateTime.DayOfYear * 24 + d.DateTime.Hour) * 60 + d.DateTime.Minute) * 60 + d.DateTime.Second) % request.Divider == 0);
switch (request.Order)
{
case 1: // Поздние вперед
data = data
.OrderByDescending(d => d.DateTime)
.Skip(request.Skip)
.Take(request.Take)
.OrderBy(d => d.DateTime);
break;
default: // Ранние вперед
data = data
.OrderBy(d => d.DateTime)
.Skip(request.Skip)
.Take(request.Take);
break;
}
return data;
}
public IEnumerable<int> GetIds(TelemetryDataRequest request)
{
var data = caches.Where(i => i.Value.LastData.Count > 0);
if (request.GeDate.HasValue)
{
data = data
.Where(item => {
var lastItem = item.Value.LastData.Last();
var geDate = request.GeDate.Value.ToOffset(TimeSpan.FromHours(item.Value.TimezoneHours));
return lastItem.DateTime >= geDate;
});
}
if (request.LeDate.HasValue)
{
data = data
.Where(item => {
var firstItem = item.Value.LastData.First();
var leDate = request.LeDate.Value.ToOffset(TimeSpan.FromHours(item.Value.TimezoneHours));
return firstItem.DateTime <= leDate;
});
}
var telemetryIds = data.Select(item => item.Key);
return telemetryIds;
} }
} }
private static async Task<TelemetryDataCacheItem?> GetOrDefaultCacheDataFromDbAsync<TEntity>(IAsbCloudDbContext db, int idTelemetry, int capacity, double hoursOffset, CancellationToken token)
where TEntity : class, AsbCloudDb.Model.ITelemetryData
{
var query = db.Set<TEntity>()
.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<TDto>();
first.DateTime = firstDbEntity.DateTime.ToRemoteDateTime(hoursOffset);
var entities = await query
.OrderByDescending(i => i.DateTime)
.Take(capacity)
.ToArrayAsync(token);
var dtos = entities
.AsEnumerable()
.Reverse()
.Select(entity =>
{
var dto = entity.Adapt<TDto>();
dto.DateTime = entity.DateTime.ToRemoteDateTime(hoursOffset);
return dto;
});
var cacheItem = new CyclicArray<TDto>(capacity);
cacheItem.AddRange(dtos);
var item = new TelemetryDataCacheItem
{
FirstByDate = first,
LastData = cacheItem,
TimezoneHours = hoursOffset,
};
return item;
}
public IEnumerable<TDto>? GetOrDefault(int idTelemetry, TelemetryDataRequest request)
{
if (!caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? cacheItem))
return null;
IEnumerable<TDto> data = cacheItem.LastData;
if (!data.Any())
return null;
if (request.GeDate.HasValue)
{
var geDate = request.GeDate.Value.ToRemoteDateTime(cacheItem.TimezoneHours);
if (data.First().DateTime > geDate)
return null;
data = data.Where(d => d.DateTime >= geDate);
}
else
{
if (request.Order == 0)
return null;
}
if (request.LeDate.HasValue)
{
var leDate = request.LeDate.Value.ToRemoteDateTime(cacheItem.TimezoneHours);
data = data.Where(d => d.DateTime <= request.LeDate);
}
if (request.Divider > 1)
data = data.Where((d) => (((d.DateTime.DayOfYear * 24 + d.DateTime.Hour) * 60 + d.DateTime.Minute) * 60 + d.DateTime.Second) % request.Divider == 0);
switch (request.Order)
{
case 1: // Поздние вперед
data = data
.OrderByDescending(d => d.DateTime)
.Skip(request.Skip)
.Take(request.Take)
.OrderBy(d => d.DateTime);
break;
default: // Ранние вперед
data = data
.OrderBy(d => d.DateTime)
.Skip(request.Skip)
.Take(request.Take);
break;
}
return data;
}
public IEnumerable<int> GetIds(TelemetryDataRequest request)
{
var data = caches.Where(i => i.Value.LastData.Count > 0);
if (request.GeDate.HasValue)
{
data = data
.Where(item => {
var lastItem = item.Value.LastData.Last();
var geDate = request.GeDate.Value.ToOffset(TimeSpan.FromHours(item.Value.TimezoneHours));
return lastItem.DateTime >= geDate;
});
}
if (request.LeDate.HasValue)
{
data = data
.Where(item => {
var firstItem = item.Value.LastData.First();
var leDate = request.LeDate.Value.ToOffset(TimeSpan.FromHours(item.Value.TimezoneHours));
return firstItem.DateTime <= leDate;
});
}
var telemetryIds = data.Select(item => item.Key);
return telemetryIds;
}
private static DateTimeOffset FromDate(DateTime dateTime, TimeSpan timezoneOffset)
{
var dateTimeNoKind = DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified);
var dateTimeOffset = new DateTimeOffset(dateTimeNoKind, timezoneOffset);
return dateTimeOffset;
}
} }

View File

@ -51,7 +51,7 @@ namespace AsbCloudInfrastructure.Services.SAUB
public DatesRangeDto GetDatesRange(int idTelemetry) public DatesRangeDto GetDatesRange(int idTelemetry)
{ {
var cacheDataRange = dataSaubCache.GetOrDefaultDataDateRange(idTelemetry) var cacheDataRange = dataSaubCache.GetOrDefaultWellDataDateRange(idTelemetry)
?? new (); ?? new ();
return cacheDataRange; return cacheDataRange;
} }