diff --git a/AsbCloudApp/Services/IDataSaubStatService.cs b/AsbCloudApp/Services/IDataSaubStatService.cs new file mode 100644 index 00000000..f72d4480 --- /dev/null +++ b/AsbCloudApp/Services/IDataSaubStatService.cs @@ -0,0 +1,24 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudApp.Services +{ + /// + /// Сервис записи данных в таблицу DataSaubStat, которая используется для построения РТК-отчета + /// + public interface IDataSaubStatService + { + /// + /// Расчет статистики DataSaubStat + /// + /// + /// Количество дней за которые должны были приходить данные, чтобы телеметрия попала в обработку. + /// + /// + /// + /// + Task CreateStatAsync(int lastDaysFilter, Action onProgressCallback, CancellationToken token); + + } +} diff --git a/AsbCloudInfrastructure/Background/PeriodicWorks/WorkDataSaubStat.cs b/AsbCloudInfrastructure/Background/PeriodicWorks/WorkDataSaubStat.cs index c7ea36ff..6a2f92de 100644 --- a/AsbCloudInfrastructure/Background/PeriodicWorks/WorkDataSaubStat.cs +++ b/AsbCloudInfrastructure/Background/PeriodicWorks/WorkDataSaubStat.cs @@ -16,7 +16,7 @@ using System.Threading.Tasks; namespace AsbCloudInfrastructure.Background.PeriodicWorks { /// - /// задача по добавлению данных в таблицу DataSaubStat, которая используется дл построения РТК-отчета + /// задача по добавлению данных в таблицу DataSaubStat, которая используется для построения РТК-отчета /// internal class WorkDataSaubStat : Work { @@ -29,209 +29,11 @@ namespace AsbCloudInfrastructure.Background.PeriodicWorks protected override async Task Action(string id, IServiceProvider services, Action onProgressCallback, CancellationToken token) { + var dataSaubStatService = services.GetRequiredService(); - var telemetryDataCache = services.GetRequiredService>(); + if (dataSaubStatService != null ) + await dataSaubStatService.CreateStatAsync(Gap, onProgressCallback, token); - var cacheRequest = new TelemetryDataRequest() - { - GeDate = DateTime.UtcNow.AddDays(-Gap) - }; - var idTelemetries = telemetryDataCache.GetIds(cacheRequest).ToArray(); - - if (!idTelemetries.Any()) - return; - - var dataSaubStatRepo = services.GetRequiredService(); - var dataSaubService = services.GetRequiredService(); - var detectedOperationRepository = 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.ToUniversalTime() ?? DateTimeOffset.UnixEpoch; - var statsCount = await CreateStatForTelemetryFromDate(idTelemetry, lastDate, dataSaubService, dataSaubStatRepo, detectedOperationRepository, token); - onProgressCallback($"Calculate stat for telemetry: {idTelemetry}; from {lastDate}; results count: {statsCount};", 100*i / idTelemetries.Length); - } - } - - private static async Task CreateStatForTelemetryFromDate( - int idTelemetry, - DateTimeOffset begin, - ITelemetryDataSaubService dataSaubService, - IDataSaubStatRepository dataSaubStatRepo, - IDetectedOperationRepository detectedOperationRepository, - CancellationToken token) - { - var detectedOperationRequest = new DetectedOperationByTelemetryRequest { - GeDateStart = begin, - IdTelemetry = idTelemetry, - IdsCategories = WellOperationCategory.MechanicalDrillingSubIds, - SortFields = new[] {nameof(DetectedOperation.DateStart) }, - Take = 250, - }; - - var detectedOperations = await detectedOperationRepository.Get(detectedOperationRequest, token); - - if (!detectedOperations.Any()) - return 0; - - var geDate = detectedOperations.First().DateStart; - var leDate = detectedOperations.OrderByDescending(d => d.DateEnd).First().DateEnd; - - var dataSaub = await dataSaubService.Get(idTelemetry, true, geDate, leDate, 100_000, token); - - if (!dataSaub.Any()) - return 0; - - if(dataSaub is not TelemetryDataSaubDto[] dataSaubArray) - dataSaubArray = dataSaub.ToArray(); - - var dataSaubStats = CreateDataSaubStat(detectedOperations, dataSaubArray); - - return await dataSaubStatRepo.InsertRangeAsync(dataSaubStats, token); - } - - private static IEnumerable CreateDataSaubStat(IEnumerable detectedOperations, TelemetryDataSaubDto[] dataSaub) - { - var indexStart = 0; - var indexEnd = 0; - var result = new List(); - - if (!dataSaub.Any()) - return result; - - foreach (var operation in detectedOperations) - { - indexStart = Array.FindIndex(dataSaub, indexEnd, t => t.DateTime >= operation.DateStart); - if (indexStart < 0) - break; - - indexEnd = Array.FindIndex(dataSaub, indexStart, t => t.DateTime > operation.DateEnd); - - if (indexEnd < 0) - indexEnd = dataSaub.Length - 1; - - if (indexEnd == indexStart) - continue; - - var length = indexEnd - indexStart; - - var subset = dataSaub.AsSpan(indexStart, length); - var stats = CalcStats(operation, subset); - result.AddRange(stats); - } - return result; - } - - private static IEnumerable CalcStats(DetectedOperationDto operation, Span dataSaub) - { - var result = new List(); - - var indexStart = 0; - for (var i = 1; i < dataSaub.Length; i++) - { - var previous = dataSaub[i - 1]; - var current = dataSaub[i]; - - if (IsNewCacheItem(previous, current) || i == dataSaub.Length - 1) - { - var length = i - indexStart; - var span = dataSaub.Slice(indexStart, length); - indexStart = i; - if (length <= 2 || (span[^1].WellDepth - span[0].WellDepth) < 0.001) - continue; // мелкие выборки не учитываем. - var stat = CalcStat(operation, span); - result.Add(stat); - } - } - - return result; - } - - private static DataSaubStatDto CalcStat(DetectedOperationDto operation, Span span) - { - var aggregatedValues = CalcAggregate(span); - var dateStart = span[0].DateTime; - var dateEnd = span[^1].DateTime; - var depthStart = span[0].WellDepth; - var depthEnd = span[^1].WellDepth; - var speed = ((depthEnd - depthStart) / (dateEnd - dateStart).TotalHours); - - var processMapDrillingCacheItem = new DataSaubStatDto - { - DateStart = dateStart, - DateEnd = dateEnd, - DepthStart = depthStart, - DepthEnd = depthEnd, - Speed = speed, - BlockSpeedSp = span[0].BlockSpeedSp, - Pressure = aggregatedValues.Pressure, - PressureIdle = span[0].PressureIdle, - PressureSp = span[0].PressureSp, - AxialLoad = aggregatedValues.AxialLoad, - AxialLoadSp = span[0].AxialLoadSp, - AxialLoadLimitMax = span[0].AxialLoadLimitMax, - RotorTorque = aggregatedValues.RotorTorque, - RotorTorqueSp = span[0].RotorTorqueSp, - RotorTorqueLimitMax = span[0].RotorTorqueLimitMax, - IdFeedRegulator = span[0].IdFeedRegulator, - RotorSpeed = aggregatedValues.RotorSpeed, - IdCategory = operation.IdCategory, - EnabledSubsystems = operation.EnabledSubsystems, - HasOscillation = operation.EnabledSubsystems.IsAutoOscillation, - IdTelemetry = operation.IdTelemetry, - Flow = aggregatedValues.Flow - }; - return processMapDrillingCacheItem; - } - - private static ( - double Pressure, - double AxialLoad, - double RotorTorque, - double RotorSpeed, - double Flow - ) CalcAggregate(Span span) - { - var sumPressure = 0.0; - var sumAxialLoad = 0.0; - var sumRotorTorque = 0.0; - var sumRotorSpeed = 0.0; - var flow = span[0].Flow ?? 0.0; - var diffDepthTotal = span[^1].WellDepth - span[0].WellDepth; - for (var i = 0; i < span.Length - 1; i++) - { - var diffDepth = span[i + 1].WellDepth - span[i].WellDepth; - sumPressure += diffDepth * span[i].Pressure; - sumAxialLoad += diffDepth * span[i].AxialLoad; - sumRotorTorque += diffDepth * span[i].RotorTorque; - sumRotorSpeed += diffDepth * span[i].RotorSpeed; - flow = span[i + 1].Flow > flow ? span[i + 1].Flow ?? 0.0 : flow; - } - return ( - Pressure: sumPressure / diffDepthTotal, - AxialLoad: sumAxialLoad / diffDepthTotal, - RotorTorque: sumRotorTorque / diffDepthTotal, - RotorSpeed: sumRotorSpeed / diffDepthTotal, - Flow: flow - ); - } - - private static bool IsNewCacheItem(TelemetryDataSaubDto previous, TelemetryDataSaubDto current) - { - return !(current.Mode == previous.Mode) - || !(current.WellDepth >= previous.WellDepth) - || !(current.BlockSpeedSp == previous.BlockSpeedSp) - || !(current.PressureIdle == previous.PressureIdle) - || !(current.PressureSp == previous.PressureSp) - || !(current.AxialLoadSp == previous.AxialLoadSp) - || !(current.AxialLoadLimitMax == previous.AxialLoadLimitMax) - || !(current.HookWeightIdle == previous.HookWeightIdle) - || !(current.RotorTorqueIdle == previous.RotorTorqueIdle) - || !(current.RotorTorqueSp == previous.RotorTorqueSp) - || !(current.RotorTorqueLimitMax == previous.RotorTorqueLimitMax) - || !(current.IdFeedRegulator == previous.IdFeedRegulator); - } + } } } diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index 9fbe1721..5ce1ada0 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -316,6 +316,7 @@ namespace AsbCloudInfrastructure services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient< IChangeLogRepository, diff --git a/AsbCloudInfrastructure/Services/DataSaubStatService.cs b/AsbCloudInfrastructure/Services/DataSaubStatService.cs new file mode 100644 index 00000000..e54a380d --- /dev/null +++ b/AsbCloudInfrastructure/Services/DataSaubStatService.cs @@ -0,0 +1,237 @@ +using AsbCloudApp.Data; +using AsbCloudApp.Data.DetectedOperation; +using AsbCloudApp.Data.SAUB; +using AsbCloudApp.Repositories; +using AsbCloudApp.Requests; +using AsbCloudApp.Services; +using AsbCloudDb.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudInfrastructure.Services +{ + + public class DataSaubStatService : IDataSaubStatService + { + private IDataSaubStatRepository dataSaubStatRepository; + private ITelemetryDataCache telemetryDataCache; + private ITelemetryDataSaubService dataSaubService; + private IDetectedOperationRepository detectedOperationRepository; + + public DataSaubStatService( + IDataSaubStatRepository dataSaubStatRepository, + ITelemetryDataCache telemetryDataCache, + ITelemetryDataSaubService dataSaubService, + IDetectedOperationRepository detectedOperationRepository) + { + this.dataSaubStatRepository = dataSaubStatRepository; + this.telemetryDataCache = telemetryDataCache; + this.dataSaubService = dataSaubService; + this.detectedOperationRepository = detectedOperationRepository; + } + + public async Task CreateStatAsync(int lastDaysFilter, Action onProgressCallback, CancellationToken token) + { + var cacheRequest = new TelemetryDataRequest() + { + GeDate = DateTime.UtcNow.AddDays(-lastDaysFilter) + }; + var idTelemetries = telemetryDataCache.GetIds(cacheRequest).ToArray(); + + if (!idTelemetries.Any()) + return; + + var stats = await dataSaubStatRepository.GetLastsAsync(idTelemetries, token); + + for (var i = 0; i < idTelemetries.Length; i++) + { + var idTelemetry = idTelemetries[i]; + var lastDate = stats.FirstOrDefault(s => s.IdTelemetry == idTelemetry)?.DateEnd.ToUniversalTime() ?? DateTimeOffset.UnixEpoch; + var statsCount = await CreateStatForTelemetryFromDate(idTelemetry, lastDate, token); + if(onProgressCallback != null) + onProgressCallback($"Calculate stat for telemetry: {idTelemetry}; from {lastDate}; results count: {statsCount};", 1d * i / idTelemetries.Length); + } + } + + private async Task CreateStatForTelemetryFromDate( + int idTelemetry, + DateTimeOffset begin, + CancellationToken token) + { + var detectedOperationRequest = new DetectedOperationByTelemetryRequest + { + GeDateStart = begin, + IdTelemetry = idTelemetry, + IdsCategories = WellOperationCategory.MechanicalDrillingSubIds, + SortFields = new[] { nameof(DetectedOperation.DateStart) }, + Take = 250, + }; + + var detectedOperations = await detectedOperationRepository.Get(detectedOperationRequest, token); + + if (!detectedOperations.Any()) + return 0; + + var geDate = detectedOperations.First().DateStart; + var leDate = detectedOperations.OrderByDescending(d => d.DateEnd).First().DateEnd; + + var dataSaub = await dataSaubService.Get(idTelemetry, true, geDate, leDate, 100_000, token); + + if (!dataSaub.Any()) + return 0; + + if (dataSaub is not TelemetryDataSaubDto[] dataSaubArray) + dataSaubArray = dataSaub.ToArray(); + + var dataSaubStats = CreateDataSaubStat(detectedOperations, dataSaubArray); + + return await dataSaubStatRepository.InsertRangeAsync(dataSaubStats, token); + } + + private static IEnumerable CreateDataSaubStat(IEnumerable detectedOperations, TelemetryDataSaubDto[] dataSaub) + { + var indexStart = 0; + var indexEnd = 0; + var result = new List(); + + if (!dataSaub.Any()) + return result; + + foreach (var operation in detectedOperations) + { + indexStart = Array.FindIndex(dataSaub, indexEnd, t => t.DateTime >= operation.DateStart); + if (indexStart < 0) + break; + + indexEnd = Array.FindIndex(dataSaub, indexStart, t => t.DateTime > operation.DateEnd); + + if (indexEnd < 0) + indexEnd = dataSaub.Length - 1; + + if (indexEnd == indexStart) + continue; + + var length = indexEnd - indexStart + 1; + + var subset = dataSaub.AsSpan(indexStart, length); + var stats = CalcStats(operation, subset); + result.AddRange(stats); + } + return result; + } + + private static IEnumerable CalcStats(DetectedOperationDto operation, Span dataSaub) + { + var result = new List(); + + var indexStart = 0; + for (var i = 1; i < dataSaub.Length; i++) + { + var previous = dataSaub[i - 1]; + var current = dataSaub[i]; + + if (IsNewCacheItem(previous, current) || i == dataSaub.Length - 1) + { + var length = i - indexStart + 1; + var span = dataSaub.Slice(indexStart, length); + indexStart = i; + if (length <= 2 || (span[^1].WellDepth - span[0].WellDepth) < 0.001) + continue; // мелкие выборки не учитываем. + var stat = CalcStat(operation, span); + result.Add(stat); + } + } + + return result; + } + + private static DataSaubStatDto CalcStat(DetectedOperationDto operation, Span span) + { + var aggregatedValues = CalcAggregate(span); + var dateStart = span[0].DateTime; + var dateEnd = span[^1].DateTime; + var depthStart = span[0].WellDepth; + var depthEnd = span[^1].WellDepth; + var speed = ((depthEnd - depthStart) / (dateEnd - dateStart).TotalHours); + + var processMapDrillingCacheItem = new DataSaubStatDto + { + DateStart = dateStart, + DateEnd = dateEnd, + DepthStart = depthStart, + DepthEnd = depthEnd, + Speed = speed, + BlockSpeedSp = span[0].BlockSpeedSp, + Pressure = aggregatedValues.Pressure, + PressureIdle = span[0].PressureIdle, + PressureSp = span[0].PressureSp, + AxialLoad = aggregatedValues.AxialLoad, + AxialLoadSp = span[0].AxialLoadSp, + AxialLoadLimitMax = span[0].AxialLoadLimitMax, + RotorTorque = aggregatedValues.RotorTorque, + RotorTorqueSp = span[0].RotorTorqueSp, + RotorTorqueLimitMax = span[0].RotorTorqueLimitMax, + IdFeedRegulator = span[0].IdFeedRegulator, + RotorSpeed = aggregatedValues.RotorSpeed, + IdCategory = operation.IdCategory, + EnabledSubsystems = operation.EnabledSubsystems, + HasOscillation = operation.EnabledSubsystems.IsAutoOscillation, + IdTelemetry = operation.IdTelemetry, + Flow = aggregatedValues.Flow + }; + return processMapDrillingCacheItem; + } + + private static ( + double Pressure, + double AxialLoad, + double RotorTorque, + double RotorSpeed, + double Flow + ) CalcAggregate(Span span) + { + var sumPressure = 0.0; + var sumAxialLoad = 0.0; + var sumRotorTorque = 0.0; + var sumRotorSpeed = 0.0; + var flow = span[0].Flow ?? 0.0; + var diffDepthTotal = span[^1].WellDepth - span[0].WellDepth; + for (var i = 0; i < span.Length - 1; i++) + { + var diffDepth = span[i + 1].WellDepth - span[i].WellDepth; + sumPressure += diffDepth * span[i].Pressure; + sumAxialLoad += diffDepth * span[i].AxialLoad; + sumRotorTorque += diffDepth * span[i].RotorTorque; + sumRotorSpeed += diffDepth * span[i].RotorSpeed; + flow = span[i + 1].Flow > flow ? span[i + 1].Flow ?? 0.0 : flow; + } + return ( + Pressure: sumPressure / diffDepthTotal, + AxialLoad: sumAxialLoad / diffDepthTotal, + RotorTorque: sumRotorTorque / diffDepthTotal, + RotorSpeed: sumRotorSpeed / diffDepthTotal, + Flow: flow + ); + } + + private static bool IsNewCacheItem(TelemetryDataSaubDto previous, TelemetryDataSaubDto current) + { + return !(current.Mode == previous.Mode) + || !(current.WellDepth >= previous.WellDepth) + || !(current.BlockSpeedSp == previous.BlockSpeedSp) + || !(current.PressureIdle == previous.PressureIdle) + || !(current.PressureSp == previous.PressureSp) + || !(current.AxialLoadSp == previous.AxialLoadSp) + || !(current.AxialLoadLimitMax == previous.AxialLoadLimitMax) + || !(current.HookWeightIdle == previous.HookWeightIdle) + || !(current.RotorTorqueIdle == previous.RotorTorqueIdle) + || !(current.RotorTorqueSp == previous.RotorTorqueSp) + || !(current.RotorTorqueLimitMax == previous.RotorTorqueLimitMax) + || !(current.IdFeedRegulator == previous.IdFeedRegulator); + } + } + +} diff --git a/AsbCloudWebApi.Tests/Services/DataSaubStatServiceTest.cs b/AsbCloudWebApi.Tests/Services/DataSaubStatServiceTest.cs new file mode 100644 index 00000000..6d65a1f2 --- /dev/null +++ b/AsbCloudWebApi.Tests/Services/DataSaubStatServiceTest.cs @@ -0,0 +1,323 @@ +using AsbCloudApp.Data; +using AsbCloudApp.Data.DetectedOperation; +using AsbCloudApp.Data.SAUB; +using AsbCloudApp.Repositories; +using AsbCloudApp.Requests; +using AsbCloudApp.Services; +using AsbCloudInfrastructure.Services; +using Mapster; +using NSubstitute; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace AsbCloudWebApi.Tests.Services; + +public class DataSaubStatServiceTest +{ + private readonly int Gap = 5; + private readonly IDataSaubStatRepository dataSaubStatRepositoryMock = Substitute.For(); + private readonly ITelemetryDataCache telemetryDataCacheMock = Substitute.For>(); + private readonly IDetectedOperationRepository detectedOperationRepositoryMock = Substitute.For(); + private readonly ITelemetryDataSaubService dataSaubServiceMock = Substitute.For(); + + private DataSaubStatService dataSaubStatService; + + private int[] idTelemetries = [1]; + private IEnumerable dataSaubStatDtos = new List() + { + new DataSaubStatDto { + Id = 1, + AxialLoad = 1, + AxialLoadLimitMax = 1, + AxialLoadSp = 1, + BlockSpeedSp = 1, + DateEnd = DateTime.UtcNow, + DateStart = DateTime.UtcNow.AddHours(-1), + DepthEnd = 2, + DepthStart = 1, + EnabledSubsystems = 1, + Flow = 1, + HasOscillation = true, + IdCategory = 1, + IdFeedRegulator = 1, + IdTelemetry = 1, + Pressure = 1, + PressureIdle = 1, + PressureSp = 1, + RotorSpeed = 1, + RotorTorque = 1, + RotorTorqueLimitMax = 1, + RotorTorqueSp = 1, + Speed = 1 + }, + new DataSaubStatDto { + Id = 2, + AxialLoad = 2, + AxialLoadLimitMax = 2, + AxialLoadSp = 2, + BlockSpeedSp = 2, + DateEnd = DateTime.UtcNow, + DateStart = DateTime.UtcNow.AddHours(-1), + DepthEnd = 3, + DepthStart = 2, + EnabledSubsystems = 2, + Flow = 2, + HasOscillation = true, + IdCategory = 2, + IdFeedRegulator = 2, + IdTelemetry = 2, + Pressure = 2, + PressureIdle = 2, + PressureSp = 2, + RotorSpeed = 2, + RotorTorque = 2, + RotorTorqueLimitMax = 2, + RotorTorqueSp = 2, + Speed = 2 + }, + new DataSaubStatDto { + Id = 3, + AxialLoad = 3, + AxialLoadLimitMax = 3, + AxialLoadSp = 3, + BlockSpeedSp = 3, + DateEnd = DateTime.UtcNow, + DateStart = DateTime.UtcNow.AddHours(-1), + DepthEnd = 4, + DepthStart = 3, + EnabledSubsystems = 3, + Flow = 3, + HasOscillation = true, + IdCategory = 3, + IdFeedRegulator = 3, + IdTelemetry = 3, + Pressure = 3, + PressureIdle = 3, + PressureSp = 3, + RotorSpeed = 3, + RotorTorque = 3, + RotorTorqueLimitMax = 3, + RotorTorqueSp = 3, + Speed = 3 + }, + }; + + private List detectedOperationDtos = new List() { + new DetectedOperationDto { + Id = 1, + DateEnd = DateTimeOffset.UtcNow, + DateStart = DateTimeOffset.UtcNow.AddHours(-1), + DepthStart = 1, + DepthEnd = 2, + IdCategory = 5002, + IdTelemetry = 1, + Value = 1, + IdEditor = 1, + IdUserAtStart = 1 + } + }; + + private List telemetryDataSaubDtos = new List { + new TelemetryDataSaubDto() + { + IdTelemetry = 1, + DateTime = DateTime.UtcNow.AddMinutes(-30), + AxialLoad = 1, + AxialLoadLimitMax = 1, + AxialLoadSp = 1, + BitDepth = 1, + BlockPosition = 1, + BlockPositionMax = 1, + BlockPositionMin = 1, + BlockSpeed = 1, + BlockSpeedSp = 1, + BlockSpeedSpDevelop = 1, + BlockSpeedSpRotor = 1, + BlockSpeedSpSlide = 1, + Flow = 1, + FlowDeltaLimitMax = 1, + FlowIdle = 1, + HookWeight = 1, + HookWeightIdle = 1, + HookWeightLimitMax = 1, + HookWeightLimitMin = 1, + IdFeedRegulator = 1, + IdUser = 1, + Mode = 1, + Mse = 1, + MseState = 1, + Pressure = 1, + PressureDeltaLimitMax = 1, + PressureIdle = 1, + PressureSp = 1, + PressureSpDevelop = 1, + PressureSpRotor = 1, + PressureSpSlide = 1, + Pump0Flow = 1, + Pump1Flow = 1, + Pump2Flow = 1, + RotorSpeed = 1, + RotorTorque = 1, + RotorTorqueIdle = 1, + RotorTorqueSp = 1, + RotorTorqueLimitMax = 1, + WellDepth = 10, + } + }; + + public DataSaubStatServiceTest() + { + telemetryDataCacheMock + .GetIds(Arg.Any()) + .Returns(idTelemetries); + + dataSaubStatRepositoryMock + .GetLastsAsync(Arg.Any(), Arg.Any()) + .Returns(dataSaubStatDtos); + + var telemetrySaubDto = telemetryDataSaubDtos.FirstOrDefault(); + if (telemetrySaubDto != null) + { + //заполнение списка телеметрий следующим образом: + // - всего в списке 6 элементов: + // - все они попадают в диапазон, определенный датами DateStart и DateEnd соответствующей записи detectedOperation + // - из этих 6-х записей у 2-х записей меняется параметр, + // являющийся признаком начала нового интервала (новой записи dataSaubStat) + // таким образом, в базе данных должно создаться 1 новая запись dataSaubStat (insertedDataSaubStatCount = 1) + var telemetrySaubDto1 = telemetrySaubDto.Adapt(); + telemetrySaubDto1.DateTime = DateTime.UtcNow.AddMinutes(-20); + telemetryDataSaubDtos.Add(telemetrySaubDto1); + + var telemetrySaubDto2 = telemetrySaubDto.Adapt(); + telemetrySaubDto2.DateTime = DateTime.UtcNow.AddMinutes(-10); + telemetrySaubDto2.RotorTorqueLimitMax = 2; + telemetryDataSaubDtos.Add(telemetrySaubDto2); + + var telemetrySaubDto3 = telemetrySaubDto.Adapt(); + telemetrySaubDto3.DateTime = DateTime.UtcNow.AddMinutes(-8); + telemetrySaubDto3.RotorTorqueLimitMax = 2; + telemetryDataSaubDtos.Add(telemetrySaubDto3); + + var telemetrySaubDto4 = telemetrySaubDto.Adapt(); + telemetrySaubDto4.DateTime = DateTime.UtcNow.AddMinutes(-6); + telemetrySaubDto4.RotorTorqueLimitMax = 2; + telemetryDataSaubDtos.Add(telemetrySaubDto4); + + var telemetrySaubDto5 = telemetrySaubDto.Adapt(); + telemetrySaubDto5.DateTime = DateTime.UtcNow.AddMinutes(-4); + telemetrySaubDto5.RotorTorqueLimitMax = 2; + telemetryDataSaubDtos.Add(telemetrySaubDto5); + + var telemetrySaubDto6 = telemetrySaubDto.Adapt(); + telemetrySaubDto6.DateTime = DateTime.UtcNow.AddMinutes(-2); + telemetrySaubDto6.RotorTorqueLimitMax = 3; + telemetrySaubDto6.WellDepth = 11; + telemetryDataSaubDtos.Add(telemetrySaubDto6); + } + + dataSaubServiceMock + .Get(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + .Returns(telemetryDataSaubDtos); + + dataSaubStatService = new DataSaubStatService( + dataSaubStatRepositoryMock, + telemetryDataCacheMock, + dataSaubServiceMock, + detectedOperationRepositoryMock); + } + + [Fact] + public async Task Create_1DataSaubStatItems_ShouldReturn__Success() + { + var insertedDataSaubStatCount = 1; + + detectedOperationRepositoryMock + .Get(Arg.Any(), Arg.Any()) + .Returns(detectedOperationDtos); + + + dataSaubStatRepositoryMock + .InsertRangeAsync(Arg.Any>(), Arg.Any()) + .Returns(insertedDataSaubStatCount); + + Action action = (message, percent) => + { + //assert + Assert.NotNull(percent); + Assert.InRange(percent.Value, 0.0, 1.0); + }; + + //act + await dataSaubStatService.CreateStatAsync(Gap, action, CancellationToken.None); + + //assert + await dataSaubStatRepositoryMock.Received().InsertRangeAsync( + Arg.Is>(l => l.Count() == insertedDataSaubStatCount), + Arg.Any()); + } + + + + [Fact] + public async Task Create_2DataSaubStatItems_ShouldReturn__Success() + { + var insertedDataSaubStatCount = 2; + + var detectedOperationDto = detectedOperationDtos.FirstOrDefault(); + if (detectedOperationDto != null) + { + var detectedOperationDto1 = detectedOperationDto.Adapt(); + detectedOperationDto1.DateStart = DateTimeOffset.UtcNow.AddMinutes(1); + detectedOperationDto1.DateEnd = DateTimeOffset.UtcNow.AddHours(1); + + detectedOperationDtos.Add(detectedOperationDto1); + } + + var telemetryDataSaubDto = telemetryDataSaubDtos.LastOrDefault(); + if (telemetryDataSaubDto != null) + { + var telemetryDataSaubDto1 = telemetryDataSaubDto.Adapt(); + telemetryDataSaubDto1.DateTime = DateTime.UtcNow.AddMinutes(10); + telemetryDataSaubDto1.WellDepth = telemetryDataSaubDto.WellDepth + 1; + + var telemetryDataSaubDto2 = telemetryDataSaubDto.Adapt(); + telemetryDataSaubDto2.DateTime = DateTime.UtcNow.AddMinutes(20); + telemetryDataSaubDto2.WellDepth = telemetryDataSaubDto1.WellDepth + 1; + + var telemetryDataSaubDto3 = telemetryDataSaubDto.Adapt(); + telemetryDataSaubDto3.DateTime = DateTime.UtcNow.AddMinutes(30); + telemetryDataSaubDto3.WellDepth = telemetryDataSaubDto2.WellDepth + 1; + + telemetryDataSaubDtos.Add(telemetryDataSaubDto1); + telemetryDataSaubDtos.Add(telemetryDataSaubDto2); + telemetryDataSaubDtos.Add(telemetryDataSaubDto3); + } + + detectedOperationRepositoryMock + .Get(Arg.Any(), Arg.Any()) + .Returns(detectedOperationDtos); + + dataSaubStatRepositoryMock + .InsertRangeAsync(Arg.Any>(), Arg.Any()) + .Returns(insertedDataSaubStatCount); + + Action action = (message, percent) => + { + //assert + Assert.NotNull(percent); + Assert.InRange(percent.Value, 0.0, 1.0); + }; + + //act + await dataSaubStatService.CreateStatAsync(Gap, action, CancellationToken.None); + + //assert + await dataSaubStatRepositoryMock.Received().InsertRangeAsync( + Arg.Is>(l => l.Count() == insertedDataSaubStatCount), + Arg.Any()); + } +} \ No newline at end of file