using AsbCloudApp.Data; using AsbCloudApp.Services; using AsbCloudDb.Model; using AsbCloudDb; using AsbCloudInfrastructure.Services.Cache; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; 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 : ITelemetryDataService, IConverter where TDto : AsbCloudApp.Data.ITelemetryData where TModel : class, AsbCloudDb.Model.ITelemetryData { private readonly IAsbCloudDbContext db; private readonly ITelemetryService telemetryService; protected readonly CacheTable cacheTelemetry; protected readonly CacheTable cacheTelemetryUsers; protected readonly CacheTable cacheWells; public TelemetryDataBaseService( IAsbCloudDbContext db, ITelemetryService telemetryService, CacheDb cacheDb) { this.db = db; this.telemetryService = telemetryService; cacheTelemetry = cacheDb.GetCachedTable((AsbCloudDbContext)db); cacheTelemetryUsers = cacheDb.GetCachedTable((AsbCloudDbContext)db); cacheWells = cacheDb.GetCachedTable((AsbCloudDbContext)db); } public virtual async Task UpdateDataAsync(string uid, IEnumerable dtos, CancellationToken token = default) { if (dtos == default || !dtos.Any()) return 0; var idTelemetry = telemetryService.GetOrCreateTemetryIdByUid(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(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 tasks = dtosList.Select(async d => { var e = Convert(d); e.IdTelemetry = idTelemetry; e.Date = await telemetryService.FixDateToTimeZoneAsync(idTelemetry, d.Date, token) .ConfigureAwait(false); return e; }); var entities = await Task.WhenAll(tasks); var dbset = db.Set(); try { return await db.Database.ExecInsertOrUpdateAsync(dbset, entities, token).ConfigureAwait(false); } catch(Exception ex) { Trace.WriteLine($"Fail to save data telemerty uid: {uid}, idTelemetry {idTelemetry} count: {entities.Count()} dataDate: {entities.FirstOrDefault()?.Date}. Message: {ex.Message}"); return 0; } } public virtual async Task> GetAsync(int idWell, DateTime dateBegin = default, double intervalSec = 600d, int approxPointsCount = 1024, bool isUtc = true, CancellationToken token = default) { var well = cacheWells.FirstOrDefault(w => w.Id == idWell); if (well?.IdTelemetry is null) return default; if (dateBegin == default) { dateBegin = telemetryService.GetLastTelemetryDate(well.IdTelemetry ?? 0); if (dateBegin != default) dateBegin = dateBegin.AddSeconds(-intervalSec); } if (dateBegin == default) dateBegin = DateTime.Now.AddSeconds(-intervalSec); if (dateBegin.Kind == DateTimeKind.Unspecified) dateBegin = DateTime.SpecifyKind(dateBegin, DateTimeKind.Utc); var datEnd = dateBegin.AddSeconds(intervalSec); var dbSet = db.Set(); var query = from data in dbSet where data.IdTelemetry == well.IdTelemetry && data.Date >= dateBegin && data.Date < datEnd select data; 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); 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; var telemetry = await cacheTelemetry.FirstOrDefaultAsync(t => t.Id == well.IdTelemetry, token).ConfigureAwait(false); if (telemetry?.TelemetryTimeZone is null) return dtos; dtos = dtos.Select(d => { d.Date = d.Date.AddHours(telemetry.TelemetryTimeZone.Hours); return d; }); return dtos; } public virtual async Task GetDataDatesRangeAsync(int idWell, bool isUtc, CancellationToken token = default) { var telemetryId = telemetryService.GetIdTelemetryByIdWell(idWell); if (telemetryId is null) return null; var (From, To) = await db.GetDatesRangeAsync((int)telemetryId, token) .ConfigureAwait(false); var result = new DatesRangeDto {From = From, To = To}; if (isUtc) return result; result = await telemetryService.FixDatesRangeByTimeZoneAsync((int)telemetryId, result, token) .ConfigureAwait(false); return result; } public abstract TDto Convert(TModel src); public abstract TModel Convert(TDto src); } }