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 : 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.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(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 offsetHours = await telemetryService.GetTelemetryTimeZoneOffsetAsync(idTelemetry, token); var entities = dtosList.Select(d => { var e = Convert(d); e.IdTelemetry = idTelemetry; if(offsetHours is not null) e.Date = telemetryService.TimeZoneService.DateToUtc(d.Date, (double)offsetHours); return e; }); 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 = 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 filterByDateEnd = dateBegin != default; if (dateBegin == default) { dateBegin = telemetryService.GetLastTelemetryDate(idTelemetry); 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 timeOffset = await telemetryService.GetTelemetryTimeZoneOffsetAsync(idTelemetry, token) .ConfigureAwait(false); if(timeOffset is not null) dateBegin = telemetryService.TimeZoneService.DateToUtc(dateBegin, timeOffset?? default); var dateEnd = dateBegin.AddSeconds(intervalSec); var dbSet = db.Set(); var query = dbSet .Where(d => d.IdTelemetry == idTelemetry && d.Date >= dateBegin); 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; if (timeOffset is null) return dtos; dtos = dtos.Select(d => { d.Date = telemetryService.TimeZoneService.DateToTimeZone(d.Date, timeOffset ?? default); 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 telemetry = await cacheTelemetry.FirstOrDefaultAsync(t => t.Id == telemetryId, token) .ConfigureAwait(false); var dto = telemetryService.TelemetryTracker.GetTelemetryDateRangeByUid(telemetry.RemoteUid); if (isUtc) return dto; dto = await telemetryService.FixDatesRangeByTimeZoneAsync((int)telemetryId, dto, token) .ConfigureAwait(false); return dto; } public abstract TDto Convert(TModel src); public abstract TModel Convert(TDto src); } }