diff --git a/AsbCloudApp/Data/DataSaubStatDto.cs b/AsbCloudApp/Data/DataSaubStatDto.cs
new file mode 100644
index 00000000..639154df
--- /dev/null
+++ b/AsbCloudApp/Data/DataSaubStatDto.cs
@@ -0,0 +1,132 @@
+using System;
+
+namespace AsbCloudApp.Data
+{
+ public class DataSaubStatDto
+ {
+ ///
+ ///
+ ///
+ public int Id { get; set; }
+
+ ///
+ /// Дата и время начала
+ ///
+ public DateTimeOffset DateStart { get; set; }
+
+ ///
+ /// Дата и время окончания
+ ///
+ public DateTimeOffset DateEnd { get; set; }
+
+ ///
+ /// Глубина забоя по стволу начальная
+ ///
+ public double DepthStart { get; set; }
+
+ ///
+ /// Глубина забоя по стволу конечная
+ ///
+ public double DepthEnd { get; set; }
+
+ ///
+ /// Скорость бурения
+ ///
+ public double Speed { get; set; }
+
+ ///
+ /// Ограничение скорости блока
+ ///
+ public double? BlockSpeedSp { get; set; }
+
+ ///
+ /// Давление
+ ///
+ public double Pressure { get; set; }
+
+ ///
+ /// Давление холостого хода
+ ///
+ public double? PressureIdle { get; set; }
+
+ ///
+ /// Ограничение фактического давления
+ ///
+ public double? PressureSp { get; set; }
+
+ ///
+ /// Фактическая нагрузка
+ ///
+ public double AxialLoad { get; set; }
+
+ ///
+ /// Ограничение факт. нагрузки
+ ///
+ public double? AxialLoadSp { get; set; }
+
+ ///
+ /// Максимально допустимая нагрузка
+ ///
+ public double? AxialLoadLimitMax { get; set; }
+
+ ///
+ /// Фактический момент
+ ///
+ public double RotorTorque { get; set; }
+
+ ///
+ /// Ограничение факт. момента
+ ///
+ public double? RotorTorqueSp { get; set; }
+
+ ///
+ /// Максимально допустимый момент
+ ///
+ public double? RotorTorqueLimitMax { get; set; }
+
+ ///
+ /// Работа при достижении ограничения
+ ///
+ public short? IdFeedRegulator { get; set; }
+
+ ///
+ /// Фактическая скорость оборотов ВСП
+ ///
+ public double RotorSpeed { get; set; }
+
+ ///
+ /// Название автоопределённой операции
+ ///
+ public int IdCategory { get; set; }
+
+ ///
+ /// Флаги подсистем
+ ///
+ public int EnabledSubsystems { get; set; }
+
+ ///
+ /// Наличие или отсутствие осцилляции
+ ///
+ public bool HasOscillation { get; set; }
+
+ ///
+ /// Фактический расход
+ ///
+ public double Flow { get; set; }
+
+ ///
+ /// Ключ телеметрии
+ ///
+ public int IdTelemetry { get; set; }
+
+ ///
+ /// Телеметрия
+ ///
+ public TelemetryDto Telemetry { get; set; } = null!;
+
+ ///
+ /// Категория автоопределенной операции
+ ///
+ public WellOperationCategoryDto OperationCategory { get; set; } = null!;
+ }
+}
diff --git a/AsbCloudApp/Repositories/IDataSaubStatRepository.cs b/AsbCloudApp/Repositories/IDataSaubStatRepository.cs
new file mode 100644
index 00000000..efab95b7
--- /dev/null
+++ b/AsbCloudApp/Repositories/IDataSaubStatRepository.cs
@@ -0,0 +1,29 @@
+using AsbCloudApp.Data;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace AsbCloudApp.Repositories
+{
+ ///
+ /// Репозиторий работы с данными из таблицы t_data_daub_stat
+ ///
+ public interface IDataSaubStatRepository
+ {
+ ///
+ /// Получение последних по дате окончания бурения записей в разрезе телеметрий
+ ///
+ /// ключи телеметрий
+ ///
+ ///
+ Task> GetLastsAsync(int[] idTelemetries, CancellationToken token);
+
+ ///
+ /// Вставка записей статистики
+ ///
+ ///
+ ///
+ ///
+ Task InsertRangeAsync(IEnumerable dataSaubStats, CancellationToken token);
+ }
+}
diff --git a/AsbCloudInfrastructure/Background/PeriodicWorks/WorkDataSaubStat.cs b/AsbCloudInfrastructure/Background/PeriodicWorks/WorkDataSaubStat.cs
index d69b185e..bc942353 100644
--- a/AsbCloudInfrastructure/Background/PeriodicWorks/WorkDataSaubStat.cs
+++ b/AsbCloudInfrastructure/Background/PeriodicWorks/WorkDataSaubStat.cs
@@ -1,4 +1,5 @@
-using AsbCloudApp.Data.SAUB;
+using AsbCloudApp.Data;
+using AsbCloudApp.Data.SAUB;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudDb.Model;
@@ -29,6 +30,7 @@ namespace AsbCloudInfrastructure.Background.PeriodicWorks
protected override async Task Action(string id, IServiceProvider services, Action onProgressCallback, CancellationToken token)
{
using var db = services.GetRequiredService();
+
var telemetryDataCache = services.GetRequiredService>();
var cacheRequest = new TelemetryDataRequest()
@@ -40,26 +42,19 @@ namespace AsbCloudInfrastructure.Background.PeriodicWorks
if (!idTelemetries.Any())
return;
- var stats = await db.Set()
- .Where(s => idTelemetries.Contains(s.IdTelemetry))
- .GroupBy(s => s.IdTelemetry)
- .Select(g => new
- {
- IdTelemetry = g.Key,
- DateEnd = g.Max(s => s.DateEnd),
- })
- .ToArrayAsync(token);
+ var dataSaubStatRepo = services.GetRequiredService();
+ var stats = await dataSaubStatRepo.GetLastsAsync(idTelemetries, token);
for( var i =0; i < idTelemetries.Length; i++)
{
var idTelemetry = idTelemetries[i];
var lastDate = stats.FirstOrDefault(s => s.IdTelemetry == idTelemetry)?.DateEnd ?? DateTimeOffset.UnixEpoch;
- var statsCount = await CreateStatForTelemetryFromDate(db, idTelemetry, lastDate, token);
+ var statsCount = await CreateStatForTelemetryFromDate(db, idTelemetry, lastDate, dataSaubStatRepo, token);
onProgressCallback($"Calculate stat for telemetry: {idTelemetry}; from {lastDate}; results count: {statsCount};", 100*i / idTelemetries.Length);
}
}
- private async Task CreateStatForTelemetryFromDate(IAsbCloudDbContext db, int idTelemetry, DateTimeOffset begin, CancellationToken token)
+ private async Task CreateStatForTelemetryFromDate(IAsbCloudDbContext db, int idTelemetry, DateTimeOffset begin, IDataSaubStatRepository dataSaubStatRepo, CancellationToken token)
{
var detectedOperations = await db.Set()
.Where(o => o.IdTelemetry == idTelemetry)
@@ -89,15 +84,14 @@ namespace AsbCloudInfrastructure.Background.PeriodicWorks
var dataSaubStats = CreateDataSaubStat(detectedOperations, telemetryDataSaub);
- db.Set().AddRange(dataSaubStats);
- return await db.SaveChangesAsync(token);
+ return await dataSaubStatRepo.InsertRangeAsync(dataSaubStats, token);
}
- private static IEnumerable CreateDataSaubStat(IEnumerable detectedOperations, TelemetryDataSaub[] telemetryDataSaub)
+ private static IEnumerable CreateDataSaubStat(IEnumerable detectedOperations, TelemetryDataSaub[] telemetryDataSaub)
{
var indexStart = 0;
var indexEnd = 0;
- var result = new List();
+ var result = new List();
if (!telemetryDataSaub.Any())
return result;
@@ -125,9 +119,9 @@ namespace AsbCloudInfrastructure.Background.PeriodicWorks
return result;
}
- private static IEnumerable CalcStats(DetectedOperation operation, Span telemetryDataSaub)
+ private static IEnumerable CalcStats(DetectedOperation operation, Span telemetryDataSaub)
{
- var result = new List();
+ var result = new List();
var indexStart = 0;
for (var i = 1; i < telemetryDataSaub.Length; i++)
@@ -150,13 +144,13 @@ namespace AsbCloudInfrastructure.Background.PeriodicWorks
return result;
}
- private static DataSaubStat CalcStat(DetectedOperation operation, Span span)
+ private static DataSaubStatDto CalcStat(DetectedOperation operation, Span span)
{
var hasOscillation = operation.ExtraData.TryGetValue(DetectorDrilling.ExtraDataKeyHasOscillation, out object? hasOscillationObject)
&& hasOscillationObject is true;
var aggregatedValues = CalcAggregate(span);
- var processMapDrillingCacheItem = new DataSaubStat
+ var processMapDrillingCacheItem = new DataSaubStatDto
{
DateStart = operation.DateStart,
DateEnd = operation.DateEnd,
diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs
index 28a051ab..5b131299 100644
--- a/AsbCloudInfrastructure/DependencyInjection.cs
+++ b/AsbCloudInfrastructure/DependencyInjection.cs
@@ -222,6 +222,7 @@ namespace AsbCloudInfrastructure
services.AddTransient();
services.AddTransient();
services.AddTransient();
+ services.AddTransient();
services.AddTransient<
IChangeLogRepository,
diff --git a/AsbCloudInfrastructure/Repository/DataSaubStatRepository.cs b/AsbCloudInfrastructure/Repository/DataSaubStatRepository.cs
new file mode 100644
index 00000000..92a228d8
--- /dev/null
+++ b/AsbCloudInfrastructure/Repository/DataSaubStatRepository.cs
@@ -0,0 +1,68 @@
+using AsbCloudApp.Data;
+using AsbCloudApp.Repositories;
+using AsbCloudApp.Services;
+using AsbCloudDb.Model;
+using Mapster;
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace AsbCloudInfrastructure.Repository
+{
+ public class DataSaubStatRepository : IDataSaubStatRepository
+ {
+ private readonly IAsbCloudDbContext db;
+ private readonly ITelemetryService telemetryService;
+
+ public DataSaubStatRepository(IAsbCloudDbContext dbContext, ITelemetryService telemetryService)
+ {
+ db = dbContext;
+ this.telemetryService = telemetryService;
+
+ }
+
+ public async Task> GetLastsAsync(int[] idTelemetries, CancellationToken token)
+ {
+ var timeZoneOffsets = idTelemetries
+ .Distinct()
+ .ToDictionary(idTelemetry => idTelemetry, idTelemetry => TimeSpan.FromHours(telemetryService.GetTimezone(idTelemetry).Hours));
+
+ var stats = await db.Set()
+ .Where(s => idTelemetries.Contains(s.IdTelemetry))
+ .GroupBy(s => s.IdTelemetry, (key, group) => group.OrderByDescending(el => el.DateEnd).First())
+ .ToArrayAsync(token);
+
+ var result = stats.Select(s => ConvertToDto(s, timeZoneOffsets[s.IdTelemetry]));
+
+ return result;
+ }
+
+ public async Task InsertRangeAsync(IEnumerable dataSaubStats, CancellationToken token)
+ {
+ var entities = dataSaubStats.Select(data => ConvertToEntity(data));
+ db.Set().AddRange(entities);
+ return await db.SaveChangesAsync(token);
+ }
+
+ private static DataSaubStatDto ConvertToDto(DataSaubStat entity, TimeSpan timeSpan)
+ {
+ var dto = entity.Adapt();
+ dto.DateStart = dto.DateStart.ToOffset(timeSpan);
+ dto.DateEnd = dto.DateEnd.ToOffset(timeSpan);
+
+ return dto;
+ }
+
+ private static DataSaubStat ConvertToEntity(DataSaubStatDto dto)
+ {
+ var entity = dto.Adapt();
+ entity.DateStart = dto.DateStart.ToUniversalTime();
+ entity.DateEnd = dto.DateEnd.ToUniversalTime();
+
+ return entity;
+ }
+ }
+}
diff --git a/AsbCloudWebApi.IntegrationTests/Repository/DataSaubStatRepositoryTest.cs b/AsbCloudWebApi.IntegrationTests/Repository/DataSaubStatRepositoryTest.cs
new file mode 100644
index 00000000..f44d32ed
--- /dev/null
+++ b/AsbCloudWebApi.IntegrationTests/Repository/DataSaubStatRepositoryTest.cs
@@ -0,0 +1,165 @@
+using AsbCloudApp.Data;
+using AsbCloudApp.Repositories;
+using AsbCloudDb.Model;
+using Mapster;
+using Microsoft.Extensions.DependencyInjection;
+using Xunit;
+
+namespace AsbCloudWebApi.IntegrationTests.Repository;
+
+public class DataSaubStatRepositoryTest : BaseIntegrationTest
+{
+ private static readonly TimeSpan timeSpan = TimeSpan.FromHours(1);
+
+ private static readonly DataSaubStatDto[] statDtos = new DataSaubStatDto[2]
+ {
+ new()
+ {
+ IdTelemetry = 1,
+ DateEnd = new DateTimeOffset(2024, 1, 1, 20, 25, 0, timeSpan),
+ DateStart = new DateTimeOffset(2024, 1, 1, 20, 15, 0, timeSpan),
+ AxialLoad = 10.0,
+ AxialLoadLimitMax = 10.0,
+ AxialLoadSp = 10.0,
+ BlockSpeedSp = 1000,
+ DepthEnd = 10.0,
+ DepthStart = 5.0,
+ EnabledSubsystems = 1,
+ Flow = 10.0,
+ HasOscillation = true,
+ Id = default,
+ IdCategory = 2,
+ IdFeedRegulator = 1,
+ Pressure = 10.0,
+ PressureIdle = 10.0,
+ PressureSp = 10.0,
+ RotorSpeed = 9.0,
+ RotorTorque = 9.0,
+ RotorTorqueSp = 9.0,
+ RotorTorqueLimitMax = 9.0,
+ Speed = 10.0
+ },
+ new()
+ {
+ IdTelemetry = 1,
+ DateEnd = new DateTimeOffset(2024, 2, 2, 20, 25, 0, timeSpan),
+ DateStart = new DateTimeOffset(2024, 2, 2, 20, 15, 0, timeSpan),
+ AxialLoad = 10.0,
+ AxialLoadLimitMax = 10.0,
+ AxialLoadSp = 10.0,
+ BlockSpeedSp = 1000,
+ DepthEnd = 10.0,
+ DepthStart = 5.0,
+ EnabledSubsystems = 1,
+ Flow = 10.0,
+ HasOscillation = true,
+ Id = default,
+ IdCategory = 2,
+ IdFeedRegulator = 1,
+ Pressure = 10.0,
+ PressureIdle = 10.0,
+ PressureSp = 10.0,
+ RotorSpeed = 10.0,
+ RotorTorque = 10.0,
+ RotorTorqueSp = 10.0,
+ RotorTorqueLimitMax = 10.0,
+ Speed = 10.0
+ }
+ };
+ private static readonly WellOperationCategory category = new()
+ {
+ Id = 2,
+ IdParent = null,
+ Name = "Категория 2"
+ };
+
+ private readonly IDataSaubStatRepository dataSaubStatRepository;
+
+ public DataSaubStatRepositoryTest(WebAppFactoryFixture factory) : base(factory)
+ {
+ dataSaubStatRepository = scope.ServiceProvider.GetRequiredService();
+ }
+
+ [Fact]
+ public async Task GetLastDatesAsync_returns_success()
+ {
+ //prepare
+ dbContext.CleanupDbSet();
+ dbContext.CleanupDbSet();
+
+ var dbSetSaubStat = dbContext.Set();
+ var dbSetCategories = dbContext.Set();
+
+ var entities = statDtos.Select(stat => ConvertToEntity(stat));
+
+ dbSetCategories.Add(category);
+ dbContext.SaveChanges();
+
+ dbSetSaubStat.AddRange(entities);
+ dbContext.SaveChanges();
+
+ //act
+ var telemetryIds = statDtos.Select(stat => stat.IdTelemetry).ToArray();
+ var result = await dataSaubStatRepository.GetLastsAsync(telemetryIds, CancellationToken.None);
+
+ var expected = statDtos.Max(stat => stat.DateEnd);
+ var actual = result.First().DateEnd;
+
+ //assert
+ Assert.True((expected - actual).Ticks == 0.0);
+ }
+
+ [Fact]
+ public async Task InsertRangeAsync_returns_success()
+ {
+ //prepare
+ dbContext.CleanupDbSet();
+ var dbSet = dbContext.Set();
+
+ var dbSetCategories = dbContext.Set();
+ dbSetCategories.Add(category);
+
+ dbContext.SaveChanges();
+
+ //act
+ var result = await dataSaubStatRepository.InsertRangeAsync(statDtos, CancellationToken.None);
+
+ //assert
+ Assert.Equal(statDtos.Length, result);
+
+ var statDtosFromDb = dbSet.Select(stat => ConvertToDto(stat, timeSpan)).ToArray();
+
+ var excludedProps = new[] {
+ nameof(DataSaubStat.Telemetry),
+ nameof(DataSaubStat.Id),
+ nameof(DataSaubStat.OperationCategory)
+ };
+ foreach (var statDtoFromDb in statDtosFromDb)
+ {
+ var statDto = statDtos
+ .Where(stat => stat.DateStart == statDtoFromDb.DateStart)
+ .Where(stat => stat.DateEnd == statDtoFromDb.DateEnd)
+ .FirstOrDefault();
+
+ MatchHelper.Match(statDtoFromDb, statDto, excludedProps);
+ }
+ }
+
+ private static DataSaubStat ConvertToEntity(DataSaubStatDto stat)
+ {
+ var entity = stat.Adapt();
+ entity.DateStart = entity.DateStart.ToUniversalTime();
+ entity.DateEnd = entity.DateEnd.ToUniversalTime();
+
+ return entity;
+ }
+
+ private static DataSaubStatDto ConvertToDto(DataSaubStat stat, TimeSpan timeSpan)
+ {
+ var dto = stat.Adapt();
+ dto.DateStart = dto.DateStart.ToOffset(timeSpan);
+ dto.DateEnd = dto.DateEnd.ToOffset(timeSpan);
+
+ return dto;
+ }
+}