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; #nullable enable namespace AsbCloudInfrastructure.Services.SAUB { public abstract class TelemetryDataBaseService : ITelemetryDataService where TDto : AsbCloudApp.Data.ITelemetryData where TEntity : class, ITelemetryData { protected readonly IAsbCloudDbContext db; protected readonly ITelemetryService telemetryService; protected readonly TelemetryDataCache telemetryDataCache; public TelemetryDataBaseService( IAsbCloudDbContext db, ITelemetryService telemetryService, TelemetryDataCache telemetryDataCache) { this.db = db; this.telemetryService = telemetryService; this.telemetryDataCache = telemetryDataCache; } /// public virtual async Task UpdateDataAsync(string uid, IEnumerable 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(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 idTelemetry = telemetryService.GetOrCreateTelemetryIdByUid(uid); var timezone = telemetryService.GetTimezone(idTelemetry); telemetryDataCache.AddRange(idTelemetry, dtos); var entities = dtosList.Select(dto => { var entity = Convert(dto, timezone.Hours); entity.IdTelemetry = idTelemetry; return entity; }); var entityMaxDate = entities.Max(e => e.DateTime); telemetryService.TelemetryTracker.SaveRequestDate(uid, entityMaxDate); var dbset = db.Set(); 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()?.DateTime}, " + $"dbSaveDurationTime:{stopwatch.ElapsedMilliseconds}ms. " + $"Message: {ex.Message}"); return 0; } } // TODO: It shouldn`t be nullable. Throw exceptions instead and return empty. /// public virtual async Task?> GetOrDefaultAsync(int idWell, DateTime dateBegin = default, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default) { var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell); if (telemetry is null) return null; var timezone = telemetryService.GetTimezone(telemetry.Id); var filterByDateEnd = dateBegin != default; DateTimeOffset dateBeginUtc; if (dateBegin == default) { dateBeginUtc = telemetryService.GetLastTelemetryDate(telemetry.Id) .ToUtcDateTimeOffset(timezone.Hours); if (dateBeginUtc != default) dateBeginUtc = dateBeginUtc.AddSeconds(-intervalSec); } else { dateBeginUtc = dateBegin.ToUtcDateTimeOffset(timezone.Hours); } if (dateBeginUtc == default) dateBeginUtc = DateTime.UtcNow.AddSeconds(-intervalSec); 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(); 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 default; 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); } } #nullable disable