using DD.Persistence.Extensions; using DD.Persistence.Models; using DD.Persistence.Repositories; using DD.Persistence.Services.Interfaces; using System.Text.Json; namespace DD.Persistence.Services; /// public class TimestampedValuesService : ITimestampedValuesService { private readonly ITimestampedValuesRepository timestampedValuesRepository; private readonly ISchemePropertyRepository dataSchemeRepository; /// public TimestampedValuesService(ITimestampedValuesRepository timestampedValuesRepository, ISchemePropertyRepository relatedDataRepository) { this.timestampedValuesRepository = timestampedValuesRepository; this.dataSchemeRepository = relatedDataRepository; } /// public async Task AddRange(Guid discriminatorId, IEnumerable dtos, CancellationToken token) { // ToDo: реализовать без foreach foreach (var dto in dtos) { await CreateDataSchemeIfNotExist(discriminatorId, dto, token); } var result = await timestampedValuesRepository.AddRange(discriminatorId, dtos, token); return result; } /// public async Task> Get(IEnumerable discriminatorIds, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) { var result = await timestampedValuesRepository.Get(discriminatorIds, geTimestamp, columnNames, skip, take, token); var dtos = await BindingToDataScheme(result, token); if (!columnNames.IsNullOrEmpty()) { dtos = ReduceSetColumnsByNames(dtos, columnNames!).ToList(); } return dtos; } /// public async Task> GetFirst(Guid discriminatorId, int takeCount, CancellationToken token) { var result = await timestampedValuesRepository.GetFirst(discriminatorId, takeCount, token); var resultBeforeBinding = new[] { KeyValuePair.Create(discriminatorId, result) } .ToDictionary(); var dtos = await BindingToDataScheme(resultBeforeBinding, token); return dtos; } /// public async Task> GetLast(Guid discriminatorId, int takeCount, CancellationToken token) { var result = await timestampedValuesRepository.GetLast(discriminatorId, takeCount, token); var resultBeforeBinding = new[] { KeyValuePair.Create(discriminatorId, result) } .ToDictionary(); var dtos = await BindingToDataScheme(resultBeforeBinding, token); return dtos; } /// public async Task> GetResampledData( Guid discriminatorId, DateTimeOffset beginTimestamp, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default) { var result = await timestampedValuesRepository.GetResampledData(discriminatorId, beginTimestamp, intervalSec, approxPointsCount, token); var resultBeforeBinding = new[] { KeyValuePair.Create(discriminatorId, result) } .ToDictionary(); var dtos = await BindingToDataScheme(resultBeforeBinding, token); return dtos; } /// public async Task> GetGtDate(Guid discriminatorId, DateTimeOffset beginTimestamp, CancellationToken token) { var result = await timestampedValuesRepository.GetGtDate(discriminatorId, beginTimestamp, token); var resultBeforeBinding = new[] { KeyValuePair.Create(discriminatorId, result) } .ToDictionary(); var dtos = await BindingToDataScheme(resultBeforeBinding, token); return dtos; } // ToDo: рефакторинг, переименовать (текущее название не отражает суть) /// /// Преобразовать результат запроса в набор dto /// /// /// /// private async Task> BindingToDataScheme(IDictionary> queryResult, CancellationToken token) { IEnumerable result = []; foreach (var keyValuePair in queryResult) { var dataScheme = await dataSchemeRepository.Get(keyValuePair.Key, token); if (dataScheme is null) continue; foreach (var tuple in keyValuePair.Value) { var dto = new TimestampedValuesDto() { Timestamp = tuple.Timestamp.ToUniversalTime(), Values = dataScheme .ToDictionary(k => k.PropertyName, v => tuple.Values[v.Index]) }; result = result.Append(dto); } } return result; } /// /// Создать схему данных, при отсутствии таковой /// /// Дискриминатор схемы /// Набор данных, по образу которого будет создана соответствующая схема /// /// /// Некорректный набор наименований полей private async Task CreateDataSchemeIfNotExist(Guid discriminatorId, TimestampedValuesDto dto, CancellationToken token) { var valuesList = dto.Values.ToList(); var properties = valuesList.ToList().Select(e => new SchemePropertyDto() { Index = valuesList.IndexOf(e), PropertyName = e.Key, PropertyKind = ((JsonElement) e.Value).ValueKind }).ToArray(); var dataScheme = await dataSchemeRepository.Get(discriminatorId, token); if (dataScheme is null) { dataScheme = new DataSchemeDto(discriminatorId, properties); await dataSchemeRepository.AddRange(dataScheme, token); return; } if (!dataScheme.Equals(properties)) { throw new InvalidOperationException($"Для системы {discriminatorId.ToString()} " + $"был передан нехарактерный набор данных"); } } /// /// Отсеить лишние поля в соответствии с заданным фильтром /// /// /// Поля, которые необходимо оставить /// private IEnumerable ReduceSetColumnsByNames(IEnumerable dtos, IEnumerable fieldNames) { var result = dtos.Select(dto => { var reducedValues = dto.Values .Where(v => fieldNames.Contains(v.Key)) .ToDictionary(); dto.Values = reducedValues; return dto; }); return result; } }