using AsbCloudApp.Data.GTR; using AsbCloudApp.Repositories; using AsbCloudApp.Services; using AsbCloudDb.Model; using AsbCloudDb.Model.GTR; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace AsbCloudInfrastructure.Repository { public class GtrWitsRepository : IGtrRepository { private readonly IAsbCloudDbContext db; private readonly ITelemetryService telemetryService; public GtrWitsRepository( IAsbCloudDbContext db, ITelemetryService telemetryService) { this.db = db; this.telemetryService = telemetryService; } public async Task> GetAsync(int idWell, DateTime? dateBegin, double intervalSec = 600, int approxPointsCount = 1024, CancellationToken token = default) { var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell); if (telemetry is null) return Enumerable.Empty(); var timezone = telemetryService.GetTimezone(telemetry.Id); DateTimeOffset? dateBeginUtc = dateBegin?.ToUtcDateTimeOffset(timezone.Hours); var dateEnd = dateBeginUtc?.AddSeconds(intervalSec); var witsRequest = new WitsRequest() { IdTelemetry = telemetry.Id, DateBeginUtc = dateBeginUtc, DateEnd = dateEnd, ApproxPointsCount = approxPointsCount, TimezoneHours = timezone.Hours }; var recordAllInt = await GetItemsOrDefaultAsync(witsRequest, token); var recordAllFloat = await GetItemsOrDefaultAsync(witsRequest, token); var recordAllString = await GetItemsOrDefaultAsync(witsRequest, token); var dtos = (recordAllFloat.Union(recordAllInt)).Union(recordAllString) .GroupBy(g => new { g.IdRecord, g.Date }) .Select(g => new WitsRecordDto { Id = g.Key.IdRecord, Date = g.Key.Date, Items = g.Select(r => new { Key = r.IdItem, r.Value }).ToDictionary(x => x.Key, x => x.Value) }); return dtos; } public async Task> GetLastDataByRecordIdAsync(int idWell, int idRecord, CancellationToken token = default) { var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell); if (telemetry is null) return Enumerable.Empty(); var timezone = telemetryService.GetTimezone(telemetry.Id); var witsRequest = new WitsRequest() { IdTelemetry = telemetry.Id, TimezoneHours = timezone.Hours, IdRecord = idRecord, }; var recordAllInt = await GetGroupedItemsOrDefaultAsync(witsRequest, token); var recordAllFloat = await GetGroupedItemsOrDefaultAsync(witsRequest, token); var recordAllString = await GetGroupedItemsOrDefaultAsync(witsRequest, token); var dtos = recordAllFloat.Union(recordAllInt).Union(recordAllString); return dtos; } private async Task> GetGroupedItemsOrDefaultAsync(WitsRequest request, CancellationToken token) where TEntity : WitsItemBase where TValue : notnull { var query = BuildQuery(request); var groupedQuery = query.GroupBy(g => new { g.IdRecord, g.IdTelemetry, g.IdItem }) .Select(g => new { g.Key.IdRecord, g.Key.IdItem, Data = g.OrderByDescending(i => i.DateTime) .FirstOrDefault() }); var groupedEntities = await groupedQuery .ToArrayAsync(token) .ConfigureAwait(false); var dtos = groupedEntities .Select(e => new WitsItemRecordDto() { IdRecord = e.IdRecord, IdItem = e.IdItem, Date = e.Data!.DateTime.ToRemoteDateTime(request.TimezoneHours), Value = new JsonValue(e.Data!.Value) }); return dtos; } private async Task> GetItemsOrDefaultAsync( WitsRequest request, CancellationToken token) where TEntity : WitsItemBase where TValue : notnull { var query = BuildQuery(request); var fullDataCount = await query.CountAsync(token); if (fullDataCount == 0) return Enumerable.Empty(); if (request.ApproxPointsCount is not null && fullDataCount > 1.75 * request.ApproxPointsCount) { var m = (int)Math.Round(1d * fullDataCount / request.ApproxPointsCount!.Value); 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 items = entities.Select(e => new WitsItemRecordDto { IdRecord = e.IdRecord, Date = e.DateTime.ToRemoteDateTime(request.TimezoneHours), IdItem = e.IdItem, Value = new JsonValue(e.Value) }); return items; } private IQueryable BuildQuery(WitsRequest request) where TEntity : WitsItemBase where TValue : notnull { var query = db.Set().Where(i => i.IdTelemetry == request.IdTelemetry); if (request.IdRecord is not null) query = query .Where(d => d.IdRecord == request.IdRecord); if (request.DateBeginUtc is not null) query = query .Where(d => d.DateTime >= request.DateBeginUtc); if (request.DateEnd is not null) query = query .Where(d => d.DateTime <= request.DateEnd); return query; } public async Task SaveDataAsync(int idTelemetry, IEnumerable dtos, CancellationToken token) { var timezoneHours = telemetryService.GetTimezone(idTelemetry).Hours; foreach (var dto in dtos) { foreach (var item in dto.Items) { var dateTime = dto.Date.ToUtcDateTimeOffset(timezoneHours); if (item.Value.Value is string valueString) { var entity = MakeEntity(dto.Id, item.Key, idTelemetry, dateTime, valueString); db.WitsItemString.Add(entity); } if (item.Value.Value is float valueFloat) { var entity = MakeEntity(dto.Id, item.Key, idTelemetry, dateTime, valueFloat); db.WitsItemFloat.Add(entity); } if (item.Value.Value is int valueInt) { var entity = MakeEntity(dto.Id, item.Key, idTelemetry, dateTime, valueInt); db.WitsItemInt.Add(entity); } } } await db.SaveChangesAsync(token); } private static TEntity MakeEntity(int idRecord, int idItem, int idTelemetry, DateTimeOffset dateTime, TValue value) where TEntity : WitsItemBase, new() where TValue : notnull => new TEntity() { IdRecord = idRecord, IdItem = idItem, IdTelemetry = idTelemetry, DateTime = dateTime, Value = value, }; private class WitsRequest { public int IdTelemetry { get; set; } public DateTimeOffset? DateBeginUtc { get; set; } public DateTimeOffset? DateEnd { get; set; } public int? ApproxPointsCount { get; set; } public double TimezoneHours { get; set; } public int? IdRecord { get; set; } } } }