DD.WellWorkover.Cloud/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs
2023-06-20 14:41:19 +05:00

155 lines
5.6 KiB
C#

using AsbCloudApp.Services;
using AsbCloudDb;
using AsbCloudDb.Model;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services.SAUB
{
public abstract class TelemetryDataBaseService<TDto, TEntity> : ITelemetryDataService<TDto>
where TDto : AsbCloudApp.Data.ITelemetryData
where TEntity : class, ITelemetryData
{
protected readonly IAsbCloudDbContext db;
protected readonly ITelemetryService telemetryService;
protected readonly TelemetryDataCache<TDto> telemetryDataCache;
public TelemetryDataBaseService(
IAsbCloudDbContext db,
ITelemetryService telemetryService,
TelemetryDataCache<TDto> telemetryDataCache)
{
this.db = db;
this.telemetryService = telemetryService;
this.telemetryDataCache = telemetryDataCache;
}
/// <inheritdoc/>
public virtual async Task<int> UpdateDataAsync(string uid, IEnumerable<TDto> dtos, CancellationToken token = default)
{
if (dtos == default || !dtos.Any())
return 0;
var dtosList = dtos.OrderBy(d => d.DateTime).ToList();
var dtoMinDate = dtosList.First().DateTime;
var dtoMaxDate = dtosList.Last().DateTime;
if (dtosList.Count > 1)
{
var duplicates = new List<TDto>(8);
for (int i = 1; i < dtosList.Count; i++)
if (dtosList[i].DateTime - dtosList[i - 1].DateTime < TimeSpan.FromMilliseconds(100))
duplicates.Add(dtosList[i - 1]);
foreach (var duplicate in duplicates)
dtosList.Remove(duplicate);
}
var telemetry = telemetryService.GetOrCreateTelemetryByUid(uid);
var timezone = telemetryService.GetTimezone(telemetry.Id);
telemetryDataCache.AddRange(telemetry.Id, dtos);
var entities = dtosList.Select(dto =>
{
var entity = Convert(dto, timezone.Hours);
entity.IdTelemetry = telemetry.Id;
return entity;
});
var entityMaxDate = entities.Max(e => e.DateTime);
var dbset = db.Set<TEntity>();
var stopwatch = Stopwatch.StartNew();
try
{
return await db.Database.ExecInsertOrUpdateAsync(dbset, entities, token).ConfigureAwait(false);
}
catch (Exception ex)
{
stopwatch.Stop();
Trace.WriteLine($"Fail to save data telemetry " +
$"uid: {uid}, " +
$"idTelemetry {telemetry.Id}, " +
$"count: {entities.Count()}, " +
$"dataDate: {entities.FirstOrDefault()?.DateTime}, " +
$"dbSaveDurationTime:{stopwatch.ElapsedMilliseconds}ms. " +
$"Message: {ex.Message}");
return 0;
}
}
/// <inheritdoc/>
public virtual async Task<IEnumerable<TDto>> GetAsync(int idWell,
DateTime dateBegin = default, double intervalSec = 600d,
int approxPointsCount = 1024, CancellationToken token = default)
{
var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell);
if (telemetry is null)
return Enumerable.Empty<TDto>();
var timezone = telemetryService.GetTimezone(telemetry.Id);
var filterByDateEnd = dateBegin != default;
DateTimeOffset dateBeginUtc;
if (dateBegin == default)
{
var dateRange = telemetryDataCache.GetOrDefaultDataDateRange(telemetry.Id);
dateBeginUtc = (dateRange?.To.ToUtcDateTimeOffset(timezone.Hours) ?? DateTime.UtcNow)
.AddSeconds(-intervalSec);
}
else
{
dateBeginUtc = dateBegin.ToUtcDateTimeOffset(timezone.Hours);
}
var cacheData = telemetryDataCache.GetOrDefault(telemetry.Id, dateBeginUtc.ToRemoteDateTime(timezone.Hours), intervalSec, approxPointsCount);
if (cacheData is not null)
return cacheData;
var dateEnd = dateBeginUtc.AddSeconds(intervalSec);
var dbSet = db.Set<TEntity>();
var query = dbSet
.Where(d => d.IdTelemetry == telemetry.Id
&& d.DateTime >= dateBeginUtc);
if (filterByDateEnd)
query = query.Where(d => d.DateTime <= dateEnd);
var fullDataCount = await query.CountAsync(token)
.ConfigureAwait(false);
if (fullDataCount == 0)
return Enumerable.Empty<TDto>();
if (fullDataCount > 1.75 * approxPointsCount)
{
var m = (int)Math.Round(1d * fullDataCount / approxPointsCount);
if (m > 1)
query = query.Where((d) => (((d.DateTime.DayOfYear * 24 + d.DateTime.Hour) * 60 + d.DateTime.Minute) * 60 + d.DateTime.Second) % m == 0);
}
var entities = await query
.OrderBy(d => d.DateTime)
.AsNoTracking()
.ToListAsync(token)
.ConfigureAwait(false);
var dtos = entities.Select(e => Convert(e, timezone.Hours));
return dtos;
}
public abstract TDto Convert(TEntity src, double timezoneOffset);
public abstract TEntity Convert(TDto src, double timezoneOffset);
}
}