diff --git a/AsbCloudApp/Data/ProcessMaps/ProcessMapPlanBaseDto.cs b/AsbCloudApp/Data/ProcessMaps/ProcessMapPlanBaseDto.cs index 9ae3c6ff..c2ce713a 100644 --- a/AsbCloudApp/Data/ProcessMaps/ProcessMapPlanBaseDto.cs +++ b/AsbCloudApp/Data/ProcessMaps/ProcessMapPlanBaseDto.cs @@ -45,6 +45,6 @@ public abstract class ProcessMapPlanBaseDto : ChangeLogAbstract, IId, IWellRelat public virtual IEnumerable Validate(ValidationContext validationContext) { if (DepthEnd <= DepthStart) - yield return new("Глубина окончания должна быть больше глубины начала", new string[] { nameof(DepthEnd), nameof(DepthStart) }); + yield return new("Глубина окончания должна быть больше глубины начала", new string[] { nameof(DepthEnd) }); } } \ No newline at end of file diff --git a/AsbCloudDb/Model/WellOperation.cs b/AsbCloudDb/Model/WellOperation.cs index 9926f0b1..06b330df 100644 --- a/AsbCloudDb/Model/WellOperation.cs +++ b/AsbCloudDb/Model/WellOperation.cs @@ -66,7 +66,7 @@ namespace AsbCloudDb.Model [JsonIgnore] [ForeignKey(nameof(IdPlan))] - public virtual WellOperation? OperationPlan { get; set; } = null!; + public virtual WellOperation? OperationPlan { get; set; } } } diff --git a/AsbCloudInfrastructure/Repository/GtrWitsRepository.cs b/AsbCloudInfrastructure/Repository/GtrWitsRepository.cs index 57b36ef2..cbf87456 100644 --- a/AsbCloudInfrastructure/Repository/GtrWitsRepository.cs +++ b/AsbCloudInfrastructure/Repository/GtrWitsRepository.cs @@ -94,15 +94,8 @@ namespace AsbCloudInfrastructure.Repository if (telemetry.TimeZone is null) throw new ArgumentInvalidException(nameof(idWell),$"Telemetry id: {telemetry.Id} can't find timezone"); - var timezoneOffset = TimeSpan.FromHours(telemetry.TimeZone.Hours); - var query = BuildQuery(telemetry.Id, request); - if (!await query.AnyAsync(token)) - return Enumerable.Empty(); - - var interval = TimeSpan.FromSeconds(10); - var idsRecord = WitsParameters.Select(p => p.Key.IdRecord); var entities = await query @@ -111,6 +104,12 @@ namespace AsbCloudInfrastructure.Repository .AsNoTracking() .ToArrayAsync(token); + if (!entities.Any()) + return Enumerable.Empty(); + + var interval = TimeSpan.FromSeconds(10); + var timezoneOffset = TimeSpan.FromHours(telemetry.TimeZone.Hours); + var dtos = entities .GroupBy(e => e.DateTime.Ticks / interval.Ticks) .Select(groupByInterval => @@ -131,17 +130,17 @@ namespace AsbCloudInfrastructure.Repository private IQueryable BuildQuery(int idTelemetry, GtrRequest request) where TEntity : WitsItemBase where TType : notnull - { - var dateIntervalStart = DateTime.UtcNow.AddSeconds(-request.IntervalSec); - + { var query = db.Set() - .Where(e => e.IdTelemetry == idTelemetry) - .Where(e => e.DateTime >= dateIntervalStart); + .Where(e => e.IdTelemetry == idTelemetry); if (request.Begin.HasValue) { - var dateBeginUtc = request.Begin.Value.ToUniversalTime(); - query = query.Where(e => e.DateTime >= dateBeginUtc); + var dateBegin = request.Begin.Value.ToUniversalTime(); + var dateEnd = dateBegin.AddSeconds(request.IntervalSec); + query = query + .Where(e => e.DateTime >= dateBegin) + .Where(e => e.DateTime <= dateEnd); } return query; diff --git a/AsbCloudInfrastructure/Services/DailyReport/DailyReportService.cs b/AsbCloudInfrastructure/Services/DailyReport/DailyReportService.cs index 21fba541..3f498ad9 100644 --- a/AsbCloudInfrastructure/Services/DailyReport/DailyReportService.cs +++ b/AsbCloudInfrastructure/Services/DailyReport/DailyReportService.cs @@ -177,8 +177,7 @@ public class DailyReportService : IDailyReportService datesRange.To = finishDate; } - result.Count = (int)(Math.Ceiling((datesRange.To - DateTimeOffset.UnixEpoch).TotalDays) - - Math.Floor((datesRange.From - DateTimeOffset.UnixEpoch).TotalDays)); + result.Count = (datesRange.To.Day - DateTimeOffset.UnixEpoch.Day) - (datesRange.From.Day - DateTimeOffset.UnixEpoch.Day); var existingDailyReports = await dailyReportRepository.GetAsync(idWell, request, cancellationToken); diff --git a/AsbCloudInfrastructure/Services/WellOperationService/OperationsStatService.cs b/AsbCloudInfrastructure/Services/WellOperationService/OperationsStatService.cs index 638232f3..87fdc3a2 100644 --- a/AsbCloudInfrastructure/Services/WellOperationService/OperationsStatService.cs +++ b/AsbCloudInfrastructure/Services/WellOperationService/OperationsStatService.cs @@ -420,46 +420,20 @@ public class OperationsStatService : IOperationsStatService public async Task>> GetTvdAsync(int idWell, CancellationToken token) { - var wellOperations = await db.WellOperations - .Include(o => o.OperationCategory) - .Include(o => o.WellSectionType) - .Include(o => o.OperationPlan) - .Where(o => o.IdWell == idWell) - .OrderBy(o => o.DateStart) - .ThenBy(o => o.DepthEnd) - .AsNoTracking() - .ToListAsync(token) - .ConfigureAwait(false); - - var wellOperationsPlan = wellOperations - .Where(o => o.IdType == WellOperation.IdOperationTypePlan) - .OrderBy(o => o.DateStart) - .ThenBy(o => o.DepthEnd); - - var wellOperationsFact = wellOperations - .Where(o => o.IdType == WellOperation.IdOperationTypeFact) - .OrderBy(o => o.DateStart) - .ThenBy(o => o.DepthEnd); - - var sectionsIds = wellOperations - .Select(o => o.IdWellSectionType) - .Distinct(); - - var tzOffsetHours = wellService.GetTimezone(idWell).Hours; - - var merged = MergeArraysBySections(sectionsIds, wellOperationsPlan, wellOperationsFact).ToList(); - if (merged.Count ==0) + var wellOperations = (await GetOperationsAsync(idWell, token)).ToArray(); + if (!wellOperations.Any()) return Enumerable.Empty>(); - - var tvd = new List>(merged.Count); - var (Plan, Fact) = merged.FirstOrDefault(); + + var tzOffsetHours = wellService.GetTimezone(idWell).Hours; + var tvd = new List>(wellOperations.Length); + var (Plan, Fact) = wellOperations.FirstOrDefault(); var dateStart = Plan?.DateStart ?? Fact!.DateStart; int? iLastMatch = null; int iLastFact = 0; var nptHours = 0d; - for (int i = 0; i < merged.Count; i++) + for (int i = 0; i < wellOperations.Length; i++) { - var item = merged[i]; + var item = wellOperations[i]; var plan = item.Plan; var fact = item.Fact; @@ -483,23 +457,23 @@ public class OperationsStatService : IOperationsStatService iLastFact = i; } - tvd.Add(planFactPredict); + tvd.Add(planFactPredict); } - if (iLastMatch is null || iLastMatch == merged.Count - 1) + if (iLastMatch is null || iLastMatch == wellOperations.Length - 1) return tvd; - var lastMatchPlan = merged[iLastMatch.Value].Plan!; + var lastMatchPlan = wellOperations[iLastMatch.Value].Plan!; var lastMatchPlanOperationEnd = lastMatchPlan.DateStart.AddHours(lastMatchPlan.DurationHours); - var lastFact = merged[iLastFact].Fact!; + var lastFact = wellOperations[iLastFact].Fact!; var lastFactDateEnd = lastFact.DateStart.AddHours(lastFact.DurationHours); var startOffset = lastFactDateEnd - lastMatchPlanOperationEnd; - for (int i = iLastMatch.Value + 1; i < merged.Count; i++) + for (int i = iLastMatch.Value + 1; i < wellOperations.Length; i++) { - if (merged[i].Plan is null) + if (wellOperations[i].Plan is null) continue; - var predict = Convert(merged[i].Plan!, tzOffsetHours); + var predict = Convert(wellOperations[i].Plan!, tzOffsetHours); predict.IdType = 2; predict.DateStart = predict.DateStart + startOffset; predict.Day = (predict.DateStart - dateStart).TotalDays; @@ -509,33 +483,33 @@ public class OperationsStatService : IOperationsStatService return tvd; } - private static IEnumerable<(WellOperation? Plan, WellOperation? Fact)> MergeArraysBySections( - IEnumerable sectionsIds, - IOrderedEnumerable wellOperationsPlan, - IOrderedEnumerable wellOperationsFact) + private async Task> GetOperationsAsync(int idWell, CancellationToken token) { - var merged = new List<(WellOperation? Plan, WellOperation? Fact)>(wellOperationsPlan.Count()); - foreach (var sectionId in sectionsIds) - { - var sectionOperationsPlan = wellOperationsPlan - .Where(o => o.IdWellSectionType == sectionId); - var sectionOperationsFact = wellOperationsFact - .Where(o => o.IdWellSectionType == sectionId); - var sectionMerged = MergeArrays(sectionOperationsPlan, sectionOperationsFact); - merged.AddRange(sectionMerged); - } - return merged; - } + var query = db.WellOperations + .Include(o => o.OperationCategory) + .Include(o => o.WellSectionType) + .Where(o => o.IdWell == idWell) + .OrderBy(o => o.DateStart) + .ThenBy(o => o.DepthEnd); - private static IEnumerable<(WellOperation? Plan, WellOperation? Fact)> MergeArrays(IEnumerable operationsPlan, IEnumerable operationsFact) - { - var operationsFactWithNoPlan = operationsFact.Where(x => x.IdPlan == null).ToArray(); - var operationsFactWithPlan = operationsFact.Where(x => x.IdPlan != null).ToArray(); + var operationsFactWithNoPlan = await query.Where(o => o.IdPlan == null && o.IdType == WellOperation.IdOperationTypeFact) + .AsNoTracking() + .ToArrayAsync(token); - var idsPlanWithFact = operationsFact.Where(x => x.IdPlan is not null).Select(x => x.IdPlan).Distinct(); - var operationsPlanWithNoFact = operationsPlan.Where(x => !idsPlanWithFact.Contains(x.IdPlan)).ToArray(); + var operationsFactWithPlan = await query.Where(o => o.IdPlan != null && o.IdType == WellOperation.IdOperationTypeFact) + .Include(o => o.OperationPlan) + .ThenInclude(o => o!.WellSectionType) + .Include(o => o.OperationPlan) + .ThenInclude(o => o!.OperationCategory) + .AsNoTracking() + .ToArrayAsync(token); - var result = new List<(WellOperation? Plan, WellOperation? Fact)>(operationsFactWithNoPlan.Length + operationsFactWithPlan.Length + operationsPlanWithNoFact.Length); + var idsPlanWithFact = operationsFactWithPlan.Select(o => o.IdPlan).Distinct(); + var operationsPlanWithNoFact = await query + .Where(o => o.IdType == WellOperation.IdOperationTypePlan && !idsPlanWithFact.Contains(o.IdPlan)).ToArrayAsync(token); + + var capacity = operationsFactWithNoPlan.Length + operationsFactWithPlan.Length + operationsPlanWithNoFact.Length; + var result = new List<(WellOperation? Plan, WellOperation? Fact)>(capacity); foreach (var operation in operationsFactWithPlan) result.Add((operation.OperationPlan, operation)); diff --git a/AsbCloudWebApi.Tests/Background/PeriodicBackgroundWorkerTest.cs b/AsbCloudWebApi.Tests/Background/PeriodicBackgroundWorkerTest.cs index 2701c26e..9fc0f6d9 100644 --- a/AsbCloudWebApi.Tests/Background/PeriodicBackgroundWorkerTest.cs +++ b/AsbCloudWebApi.Tests/Background/PeriodicBackgroundWorkerTest.cs @@ -67,10 +67,12 @@ public class PeriodicBackgroundWorkerTest { var expectadResult = 42; var result = 0; + using var semaphore = new SemaphoreSlim(0, 1); Task workAction(string id, IServiceProvider services, Action callback, CancellationToken token) { result = expectadResult; + semaphore.Release(); return Task.CompletedTask; } var goodWork = Work.CreateByDelegate("", workAction); @@ -85,12 +87,12 @@ public class PeriodicBackgroundWorkerTest service.Add(badWork, TimeSpan.FromSeconds(2)); service.Add(goodWork, TimeSpan.FromSeconds(2)); - await Task.Delay(TimeSpan.FromMilliseconds(256)); + await semaphore.WaitAsync(4_100); //assert - Assert.Equal(expectadResult, result); Assert.Equal(1, badWork.CountErrors); Assert.Equal(1, goodWork.CountComplete); Assert.Equal(1, goodWork.CountStart); + Assert.Equal(expectadResult, result); } } diff --git a/AsbCloudWebApi.Tests/Background/WorkTest.cs b/AsbCloudWebApi.Tests/Background/WorkTest.cs index 41fc486d..97a0c6c2 100644 --- a/AsbCloudWebApi.Tests/Background/WorkTest.cs +++ b/AsbCloudWebApi.Tests/Background/WorkTest.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using AsbCloudInfrastructure.Background; @@ -87,7 +88,7 @@ public class WorkTest Assert.Equal(expectedProgress, currentState.Progress); } - [Fact] + [Fact, MethodImpl(MethodImplOptions.NoOptimization)] public async Task ExecutionWork_ShouldReturn_FailsWithInfo() { //arrange diff --git a/AsbCloudWebApi.Tests/Services/DailyReportServiceTest.cs b/AsbCloudWebApi.Tests/Services/DailyReportServiceTest.cs index 38f1959e..90c00957 100644 --- a/AsbCloudWebApi.Tests/Services/DailyReportServiceTest.cs +++ b/AsbCloudWebApi.Tests/Services/DailyReportServiceTest.cs @@ -157,7 +157,7 @@ public class DailyReportServiceTest IdParentCategory = 4001, IdWellSectionType = 1, OperationCategoryName = "Механическое. бурение", - DateStart = new DateTime(2023, 10, 26), + DateStart = new DateTime(2023, 10, 26, 0, 0, 0), DepthStart = 80, DepthEnd = 150, DurationHours = 8, @@ -169,7 +169,7 @@ public class DailyReportServiceTest OperationCategoryName = "Механическое. бурение", IdWellSectionType = 1, IdParentCategory = 4001, - DateStart = new DateTime(2023, 10, 26), + DateStart = new DateTime(2023, 10, 26, 23, 59, 59), DepthStart = 150, DepthEnd = 200, DurationHours = 8, @@ -507,7 +507,7 @@ public class DailyReportServiceTest public async Task GetAsync_ShouldReturn_FictiveDailyReport() { //arrange - var expectedCount = (fakeLastFactWellOperation.DateStart - fakeFirstFactWellOperation.DateStart).TotalDays + 1; + var expectedCount = fakeLastFactWellOperation.DateStart.Day - fakeFirstFactWellOperation.DateStart.Day; //act var result = await dailyReportService.GetAsync(idWell, new FileReportRequest(), CancellationToken.None); diff --git a/AsbCloudWebApi/Controllers/OperationStatController.cs b/AsbCloudWebApi/Controllers/OperationStatController.cs index 7126c5f5..81a9431f 100644 --- a/AsbCloudWebApi/Controllers/OperationStatController.cs +++ b/AsbCloudWebApi/Controllers/OperationStatController.cs @@ -1,4 +1,5 @@ -using AsbCloudApp.Data; +using System; +using AsbCloudApp.Data; using AsbCloudApp.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -150,9 +151,8 @@ namespace AsbCloudWebApi.Controllers { if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false)) return Forbid(); - - var result = await operationsStatService.GetTvdAsync(idWell, token) - .ConfigureAwait(false); + + var result = await operationsStatService.GetTvdAsync(idWell, token); return Ok(result); } diff --git a/AsbCloudWebApi/Controllers/SAUB/GtrWitsController.cs b/AsbCloudWebApi/Controllers/SAUB/GtrWitsController.cs index 8f38b2ad..a339f1b8 100644 --- a/AsbCloudWebApi/Controllers/SAUB/GtrWitsController.cs +++ b/AsbCloudWebApi/Controllers/SAUB/GtrWitsController.cs @@ -38,7 +38,7 @@ namespace AsbCloudWebApi.Controllers.SAUB this.wellService = wellService; this.telemetryHubContext = telemetryHubContext; } - + /// /// Получить значение от ГТИ /// @@ -49,7 +49,7 @@ namespace AsbCloudWebApi.Controllers.SAUB [HttpGet] [Permission] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] - public async Task GetAllAsync(int idWell, [FromQuery] GtrRequest request, CancellationToken token) + public async Task GetAllAsync([Required] int idWell, [FromQuery] GtrRequest request, CancellationToken token) { await AssertUserHasAccessToWellAsync(idWell, token);