DD.WellWorkover.Cloud/AsbCloudInfrastructure/Services/TelemetryDataBaseService.cs

193 lines
7.5 KiB
C#

using AsbCloudApp.Data;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudDb;
using AsbCloudInfrastructure.Services.Cache;
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
{
public abstract class TelemetryDataBaseService<TDto, TModel> : ITelemetryDataService<TDto>, IConverter<TDto, TModel>
where TDto : AsbCloudApp.Data.ITelemetryData
where TModel : class, AsbCloudDb.Model.ITelemetryData
{
private readonly IAsbCloudDbContext db;
private readonly ITelemetryService telemetryService;
protected readonly CacheTable<Telemetry> cacheTelemetry;
protected readonly CacheTable<TelemetryUser> cacheTelemetryUsers;
protected readonly CacheTable<Well> cacheWells;
public TelemetryDataBaseService(
IAsbCloudDbContext db,
ITelemetryService telemetryService,
CacheDb cacheDb)
{
this.db = db;
this.telemetryService = telemetryService;
cacheTelemetry = cacheDb.GetCachedTable<Telemetry>((AsbCloudDbContext)db);
cacheTelemetryUsers = cacheDb.GetCachedTable<TelemetryUser>((AsbCloudDbContext)db);
cacheWells = cacheDb.GetCachedTable<Well>((AsbCloudDbContext)db);
}
public virtual async Task<int> UpdateDataAsync(string uid, IEnumerable<TDto> dtos, CancellationToken token = default)
{
if (dtos == default || !dtos.Any())
return 0;
var idTelemetry = telemetryService.GetOrCreateTelemetryIdByUid(uid);
var lastTelemetryDate = telemetryService.GetLastTelemetryDate(uid);
var dtosList = dtos.OrderBy(d => d.Date).ToList();
var dtoMinDate = dtosList.First().Date;
var dtoMaxDate = dtosList.Last().Date;
if (dtosList.Count > 1)
{
var duplicates = new List<TDto>(8);
for (int i = 1; i < dtosList.Count; i++)
if (dtosList[i].Date - dtosList[i-1].Date < TimeSpan.FromMilliseconds(100))
duplicates.Add(dtosList[i - 1]);
foreach (var duplicate in duplicates)
dtosList.Remove(duplicate);
}
var timeZoneOffset = (await telemetryService.GetTimeZoneOffsetAsync(idTelemetry, token)) ?? 0;
var entities = dtosList.Select(dto => {
var entity = Convert(dto);
entity.Date = dto.Date.ToUtcDateTimeOffset(timeZoneOffset);
entity.IdTelemetry = idTelemetry;
return entity;
});
var entityMaxDate = entities.Max(e => e.Date);
telemetryService.SaveRequestDate(uid, entityMaxDate);
var dbset = db.Set<TModel>();
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 {idTelemetry}, " +
$"count: {entities.Count()}, " +
$"dataDate: {entities.FirstOrDefault()?.Date}, " +
$"dbSaveDurationTime:{stopwatch.ElapsedMilliseconds}ms. " +
$"Message: {ex.Message}");
return 0;
}
}
public virtual async Task<IEnumerable<TDto>> GetAsync(int idWell,
DateTime dateBegin = default, double intervalSec = 600d,
int approxPointsCount = 1024, bool isUtc = false, CancellationToken token = default)
{
var well = cacheWells.FirstOrDefault(w => w.Id == idWell);
if (well?.IdTelemetry is null)
return default;
var idTelemetry = well?.IdTelemetry ?? default;
var timeZoneOffset = (await telemetryService.GetTimeZoneOffsetAsync(idTelemetry, token)) ?? 0d;
var filterByDateEnd = dateBegin != default;
DateTimeOffset dateBeginUtc;
if (dateBegin == default)
{
dateBeginUtc = telemetryService.GetLastTelemetryDate(idTelemetry)
.UtcDateTime;
if (dateBeginUtc != default)
dateBeginUtc = dateBeginUtc.AddSeconds(-intervalSec);
}
else
{
dateBeginUtc = dateBegin.ToUtcDateTimeOffset(timeZoneOffset);
}
if (dateBeginUtc == default)
dateBeginUtc = DateTime.UtcNow.AddSeconds(-intervalSec);
var dateEnd = dateBeginUtc.AddSeconds(intervalSec);
var dbSet = db.Set<TModel>();
var query = dbSet
.Where(d => d.IdTelemetry == idTelemetry
&& d.Date >= dateBeginUtc);
if (filterByDateEnd)
query = query.Where(d => d.Date < dateEnd);
var fullDataCount = await query.CountAsync(token)
.ConfigureAwait(false);
if (fullDataCount == 0)
return default;
if (fullDataCount > 1.75 * approxPointsCount)
{
var m = (int)Math.Round(1d * fullDataCount / approxPointsCount);
switch (m)
{
//case var i when i <= 1: // тут для полноты, но никогда не сработает из-за условия выше
// break;
case var i when i < 10:
query = query.Where((d) => d.Date.Second % m == 0);
break;
case var i when i < 30:
query = query.Where((d) => (d.Date.Minute * 60 + d.Date.Second) % m == 0);
break;
case var i when i < 600:
query = query.Where((d) => ((d.Date.Hour * 60 + d.Date.Minute) * 60 + d.Date.Second) % m == 0);
break;
default:
query = query.Where((d) => (((d.Date.DayOfYear * 24 + d.Date.Hour) * 60 + d.Date.Minute) * 60 + d.Date.Second) % m == 0);
break;
}
if (m > 1)
query = query.Where((d) => (((d.Date.DayOfYear*24 + d.Date.Hour)*60 + d.Date.Minute)*60 + d.Date.Second) % m == 0);
}
var entities = await query
.OrderBy(d=>d.Date)
.AsNoTracking()
.ToListAsync(token)
.ConfigureAwait(false);
var dtos = entities.Select(e => Convert(e));
if (isUtc)
return dtos;
dtos = dtos.Select(d =>
{
d.Date = d.Date.ToTimeZoneOffsetHours(timeZoneOffset);
return d;
});
return dtos;
}
[Obsolete("Use telemetryService.GetDatesRangeAsync instead", false)]
public virtual Task<DatesRangeDto> GetDataDatesRangeAsync(int idWell, bool isUtc,
CancellationToken token = default)
=> telemetryService.GetDatesRangeAsync(idWell, isUtc, token);
public abstract TDto Convert(TModel src);
public abstract TModel Convert(TDto src);
}
}