diff --git a/AsbCloudApp/Repositories/IGtrRepository.cs b/AsbCloudApp/Repositories/IGtrRepository.cs index 706b6be2..39fbef07 100644 --- a/AsbCloudApp/Repositories/IGtrRepository.cs +++ b/AsbCloudApp/Repositories/IGtrRepository.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using AsbCloudApp.Requests; +using AsbCloudApp.Data; namespace AsbCloudApp.Repositories { @@ -60,5 +61,15 @@ namespace AsbCloudApp.Repositories /// [Obsolete] IEnumerable GetLastData(int idWell); + + /// + /// Доступные даты по скважине + /// + /// + /// + /// + /// + /// + Task GetRangeAsync(int idWell, DateTimeOffset? geDate, DateTimeOffset? leDate, CancellationToken token); } } diff --git a/AsbCloudApp/Requests/GtrRequest.cs b/AsbCloudApp/Requests/GtrRequest.cs index 43de25c4..89d281a3 100644 --- a/AsbCloudApp/Requests/GtrRequest.cs +++ b/AsbCloudApp/Requests/GtrRequest.cs @@ -10,7 +10,7 @@ public class GtrRequest /// /// Дата начала выборки.По умолчанию: текущее время - IntervalSec /// - public DateTime? Begin { get; set; } + public DateTimeOffset? Begin { get; set; } /// /// Интервал времени даты начала выборки, секунды diff --git a/AsbCloudInfrastructure/Repository/GtrWitsRepository.cs b/AsbCloudInfrastructure/Repository/GtrWitsRepository.cs index cbf87456..3f8333c9 100644 --- a/AsbCloudInfrastructure/Repository/GtrWitsRepository.cs +++ b/AsbCloudInfrastructure/Repository/GtrWitsRepository.cs @@ -15,6 +15,8 @@ using System.Threading.Tasks; using AsbCloudApp.Exceptions; using AsbCloudApp.Requests; using Mapster; +using AsbCloudApp.Data; +using System.ComponentModel.DataAnnotations; namespace AsbCloudInfrastructure.Repository { @@ -82,6 +84,42 @@ namespace AsbCloudInfrastructure.Repository public async Task> GetAsync(int idWell, GtrRequest request, CancellationToken token) => await GetAsync(idWell, request, token); + public async Task GetRangeAsync(int idWell, DateTimeOffset? geDate, DateTimeOffset? leDate, CancellationToken token) + { + var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell); + + if (telemetry is null) + return null; + + var rangeQuery = db + .Set() + .Where(e => e.IdTelemetry == telemetry.Id); + + if (geDate is not null) + rangeQuery = rangeQuery.Where(e => e.DateTime >= geDate); + + if (leDate is not null) + rangeQuery = rangeQuery.Where(e => e.DateTime <= leDate); + + var groupedQuery = rangeQuery.GroupBy(e => e.IdTelemetry) + .Select(group => new + { + Min = group.Min(e => e.DateTime), + Max = group.Max(e => e.DateTime) + }); + var range = await groupedQuery.FirstOrDefaultAsync(token); + + if (range is null) + return null; + + var result = new DatesRangeDto + { + From = range.Min.ToOffset(telemetry.TimeZone!.Offset), + To = range.Max.ToOffset(telemetry.TimeZone!.Offset), + }; + return result; + } + private async Task> GetAsync(int idWell, GtrRequest request, CancellationToken token) where TEntity : WitsItemBase where TType : notnull @@ -116,8 +154,8 @@ namespace AsbCloudInfrastructure.Repository { var items = groupByInterval.Select(e => e); var values = items.GroupBy(e => (e.IdRecord, e.IdItem)) - .Where(parameter => parameter.Any()) - .ToDictionary(parameter => WitsParameters[parameter.Key], g => g.Last().Value.ToString()); + .Where(group => WitsParameters.ContainsKey(group.Key)) + .ToDictionary(group => WitsParameters[group.Key], g => (object)g.Last().Value); var dto = values.Adapt(); dto.DateTime = items.Last().DateTime.ToOffset(timezoneOffset); @@ -142,6 +180,19 @@ namespace AsbCloudInfrastructure.Repository .Where(e => e.DateTime >= dateBegin) .Where(e => e.DateTime <= dateEnd); } + else + { + var lastDate = query + .OrderBy(e=>e.DateTime) + .LastOrDefault() + ?.DateTime + ?? DateTimeOffset.UtcNow; + var dateBegin = lastDate.AddSeconds(-request.IntervalSec); + var dateEnd = lastDate; + query = query + .Where(e => e.DateTime >= dateBegin) + .Where(e => e.DateTime <= dateEnd); + } return query; } diff --git a/AsbCloudInfrastructure/Services/WellCompositeOperationService.cs b/AsbCloudInfrastructure/Services/WellCompositeOperationService.cs index 783228c3..d1312e6e 100644 --- a/AsbCloudInfrastructure/Services/WellCompositeOperationService.cs +++ b/AsbCloudInfrastructure/Services/WellCompositeOperationService.cs @@ -11,239 +11,232 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -namespace AsbCloudInfrastructure.Services +namespace AsbCloudInfrastructure.Services; + +public class WellCompositeOperationService : IWellCompositeOperationService { - public class WellCompositeOperationService : IWellCompositeOperationService + private readonly ICrudRepository wellSectionTypeRepository; + private readonly IWellOperationCategoryRepository wellOperationCategoryRepository; + private readonly IWellOperationRepository wellOperationRepository; + private readonly IWellService wellService; + + /// + /// Тип секции "Транспортный стол" + /// + private const int wellSectionTransportTable = 5; + + /// + /// Тип секции "Эксплуатационная колонна" + /// + private const int wellSectionProductionString = 4; + + + /// + /// набор настроек для замены одной категории секции на другую + /// + private static Dictionary<(int, int), int> SettingsForSectionCategoryChange = new Dictionary<(int, int), int>() { + { (2, 5096), 5013 }, + { (2, 5008), 5013 }, + { (3, 5096), 5084 }, + { (3, 5008), 5084 }, + { (3, 5085), 5015 }, + { (3, 5014), 5015 }, + { (31, 5014), 5015 }, + { (31, 5012), 5013 }, + { (31, 5083), 5013 }, + { (4, 5085), 5015 }, + { (4, 5087), 5015 }, + { (4, 5014), 5015 }, + { (4, 5053), 5037 }, + { (4, 5084), 5096 }, + { (4, 5086), 5013 }, + { (6, 5085), 5015 }, + { (6, 5036), 5034 }, + { (6, 5035), 5097 } + }; + + private HashSet<(int IdSectionType, int IdCategory)> WellSectionTypesWithCategories = new HashSet<(int IdSectionType, int IdCategory)>() { - private readonly ICrudRepository wellSectionTypeRepository; - private readonly IWellOperationCategoryRepository wellOperationCategoryRepository; - private readonly IWellOperationRepository wellOperationRepository; - private readonly IWellService wellService; + { (2, 5001) }, + { (2, 5003) }, + { (2, 5013) }, + { (2, 5000) }, + { (2, 5022) }, + { (2, 5017) }, + { (2, 5023) }, + { (2, 4007) }, + { (2, 5090) }, + { (3, 5001) }, + { (3, 5015) }, + { (3, 5037) }, + { (3, 5057) }, + { (3, 5003) }, + { (3, 5036) }, + { (3, 5084) }, + { (3, 5013) }, + { (3, 5000) }, + { (3, 5022) }, + { (3, 5017) }, + { (3, 4007) }, + { (3, 5090) }, + { (3, 5045) }, + { (3, 5042) }, + { (3, 5046) }, + { (31, 5001) }, + { (31, 5015) }, + { (31, 5037) }, + { (31, 5057) }, + { (31, 5003) }, + { (31, 5036) }, + { (31, 5013) }, + { (31, 5022) }, + { (31, 5017) }, + { (31, 5023) }, + { (31, 4007) }, + { (31, 5045) }, + { (31, 5042) }, + { (31, 5046) }, + { (4, 5001) }, + { (4, 5015) }, + { (4, 5046) }, + { (4, 5037) }, + { (4, 5097) }, + { (4, 5057) }, + { (4, 5003) }, + { (4, 5036) }, + { (4, 5008) }, + { (4, 5003) }, + { (4, 5036) }, + { (4, 5013) }, + { (4, 5000) }, + { (4, 5029) }, + { (4, 5022) }, + { (4, 5017) }, + { (4, 5019) }, + { (4, 5042) }, + { (4, 5046) }, + { (6, 5001) }, + { (6, 5015) }, + { (6, 5034) }, + { (6, 5037) }, + { (6, 5097) }, + { (6, 5057) }, + { (6, 5003) } + }; - /// - /// Тип секции "Транспортный стол" - /// - private const int wellSectionTransportTable = 5; + public WellCompositeOperationService( + ICrudRepository wellSectionTypeRepository, + IWellOperationCategoryRepository wellOperationCategoryRepository, + IWellOperationRepository wellOperationRepository, + IWellService wellService) + { + this.wellSectionTypeRepository = wellSectionTypeRepository; + this.wellOperationCategoryRepository = wellOperationCategoryRepository; + this.wellOperationRepository = wellOperationRepository; + this.wellService = wellService; + } - /// - /// Тип секции "Эксплуатационная колонна" - /// - private const int wellSectionProductionString = 4; + public async Task GetAsync(IEnumerable idsWells, CancellationToken token) + { + var sections = await wellSectionTypeRepository.GetAllAsync(token); + var sectionsDict = sections.ToDictionary(s => s.Id, s => s.Caption); + var categories = wellOperationCategoryRepository.Get(true); + var categoriesDict = categories.ToDictionary(s => s.Id, s => s.Name); - /// - /// набор настроек для замены одной категории секции на другую - /// - private static Dictionary<(int, int), int> SettingsForSectionCategoryChange = new Dictionary<(int, int), int>() { - { (2, 5096), 5013 }, - { (2, 5008), 5013 }, - { (3, 5096), 5084 }, - { (3, 5008), 5084 }, - { (3, 5085), 5015 }, - { (3, 5014), 5015 }, - { (31, 5014), 5015 }, - { (31, 5012), 5013 }, - { (31, 5083), 5013 }, - { (4, 5085), 5015 }, - { (4, 5087), 5015 }, - { (4, 5014), 5015 }, - { (4, 5053), 5037 }, - { (4, 5084), 5096 }, - { (4, 5086), 5013 }, - { (6, 5085), 5015 }, - { (6, 5036), 5034 }, - { (6, 5035), 5097 } + var wells = await wellService.GetAsync(new WellRequest { Ids = idsWells }, token); + var wellsDict = wells.ToDictionary(w => w.Id); + + var idsWellSectionTypes = WellSectionTypesWithCategories.Select(t => t.IdSectionType).Distinct(); + var usedCategories = WellSectionTypesWithCategories.Select(c => c.IdCategory).Distinct(); + + var wellOperationRequest = new WellOperationRequest(idsWells) + { + OperationCategoryIds = usedCategories, + SectionTypeIds = idsWellSectionTypes, + OperationType = WellOperation.IdOperationTypeFact }; + var operations = await wellOperationRepository.GetAsync(wellOperationRequest, token); - private HashSet<(int IdSectionType, int IdCategory)> WellSectionTypesWithCategories = new HashSet<(int IdSectionType, int IdCategory)>() + var operationsForComposite = operations.Select(o => CreateCompositeOperation(o, sectionsDict, categoriesDict)); + + var wellOperationsWithComposite = new List>(); + var compositeDepth = 0d; + var compositeDay = 0d; + var result = new WellCompositeOperationDto(); + + var compositeOperations = new List(); + foreach ((int IdSection, int IdCategory) in WellSectionTypesWithCategories) { - { (2, 5001) }, - { (2, 5003) }, - { (2, 5013) }, - { (2, 5000) }, - { (2, 5022) }, - { (2, 5017) }, - { (2, 5023) }, - { (2, 4007) }, - { (2, 5090) }, - { (3, 5001) }, - { (3, 5015) }, - { (3, 5037) }, - { (3, 5057) }, - { (3, 5003) }, - { (3, 5036) }, - { (3, 5084) }, - { (3, 5013) }, - { (3, 5000) }, - { (3, 5022) }, - { (3, 5017) }, - { (3, 4007) }, - { (3, 5090) }, - { (3, 5045) }, - { (3, 5042) }, - { (3, 5046) }, - { (31, 5001) }, - { (31, 5015) }, - { (31, 5037) }, - { (31, 5057) }, - { (31, 5003) }, - { (31, 5036) }, - { (31, 5013) }, - { (31, 5022) }, - { (31, 5017) }, - { (31, 5023) }, - { (31, 4007) }, - { (31, 5045) }, - { (31, 5042) }, - { (31, 5046) }, - { (4, 5001) }, - { (4, 5015) }, - { (4, 5046) }, - { (4, 5037) }, - { (4, 5097) }, - { (4, 5057) }, - { (4, 5003) }, - { (4, 5036) }, - { (4, 5008) }, - { (4, 5003) }, - { (4, 5036) }, - { (4, 5013) }, - { (4, 5000) }, - { (4, 5029) }, - { (4, 5022) }, - { (4, 5017) }, - { (4, 5019) }, - { (4, 5042) }, - { (4, 5046) }, - { (6, 5001) }, - { (6, 5015) }, - { (6, 5034) }, - { (6, 5037) }, - { (6, 5097) }, - { (6, 5057) }, - { (6, 5003) } - }; + var filteredByTemplate = operationsForComposite + .Where(o => o.IdWellSectionType == IdSection) + .Where(o => o.IdCategory == IdCategory); + if (!filteredByTemplate.Any()) + continue; + var groupedByWell = filteredByTemplate.GroupBy(o => o.IdWell); - public WellCompositeOperationService( - ICrudRepository wellSectionTypeRepository, - IWellOperationCategoryRepository wellOperationCategoryRepository, - IWellOperationRepository wellOperationRepository, - IWellService wellService) - { - this.wellSectionTypeRepository = wellSectionTypeRepository; - this.wellOperationCategoryRepository = wellOperationCategoryRepository; - this.wellOperationRepository = wellOperationRepository; - this.wellService = wellService; + var aggreagtedByWell = groupedByWell.Select(g => new WellOperationDto + { + IdCategory = IdCategory, + IdWell = g.Key, + IdWellSectionType = IdSection, + DepthStart = g.Min(o => o.DepthStart), + DurationHours = g.Sum(o => o.DurationHours), + OperationCategoryName = g.First().OperationCategoryName, + WellSectionTypeCaption = g.First().WellSectionTypeCaption, + }); + + var compositeOperation = aggreagtedByWell.OrderBy(o => o.DurationHours). + ThenByDescending(o => o.DepthStart) + .First(); + + compositeOperation.IdWell = 0; + + if (compositeDepth > compositeOperation.DepthStart) + compositeOperation.DepthStart = compositeDepth; + compositeDepth = compositeOperation.DepthStart; + + compositeDay += compositeOperation.DurationHours; + compositeOperation.Day = compositeDay; + + compositeOperations.Add(compositeOperation); } - public async Task GetAsync(IEnumerable idsWells, CancellationToken token) + var groupedByWellOperations = operations + .GroupBy(o => o.IdWell) + .ToDictionary(o => o.Key, o => o.ToArray()) + .Select(o => new WellCompositeOperationSourceDto() + { + Operations = o.Value, + Well = wellsDict[o.Key], + }); + + result.WellOperationsComposite = compositeOperations; + result.WellCompositeSourceOperations = groupedByWellOperations; + + return result; + } + + private static WellOperationDto CreateCompositeOperation( + WellOperationDto dto, + IDictionary sectionTypes, + IDictionary operationCategories) + { + var newDto = dto.Adapt(); + if (newDto.IdWellSectionType == wellSectionTransportTable) { - var sections = await wellSectionTypeRepository.GetAllAsync(token); - var sectionsDict = sections.ToDictionary(s => s.Id, s => s.Caption); - - var categories = wellOperationCategoryRepository.Get(true); - var categoriesDict = categories.ToDictionary(s => s.Id, s => s.Name); - - var wells = await wellService.GetAsync(new WellRequest { Ids = idsWells }, token); - var wellsDict = wells.ToDictionary(w => w.Id); - - var idsWellSectionTypes = WellSectionTypesWithCategories.Select(t => t.IdSectionType).Distinct(); - var usedCategories = WellSectionTypesWithCategories.Select(c => c.IdCategory).Distinct(); - - var wellOperationRequest = new WellOperationRequest(idsWells) - { - OperationCategoryIds = usedCategories, - SectionTypeIds = idsWellSectionTypes, - OperationType = WellOperation.IdOperationTypeFact - }; - var operations = await wellOperationRepository.GetAsync(wellOperationRequest, token); - - var operationsForComposite = operations.Select(o => CreateCompositeOperation(o, sectionsDict, categoriesDict)); - - var wellOperationsWithComposite = new List>(); - var compositeDepth = 0d; - var compositeDay = 0d; - var result = new WellCompositeOperationDto(); - - var compositeOperations = new List(); - foreach ((int IdSection, int IdCategory) in WellSectionTypesWithCategories) - { - var filteredByTemplate = operationsForComposite - .Where(o => o.IdWellSectionType == IdSection) - .Where(o => o.IdCategory == IdCategory); - - if (!filteredByTemplate.Any()) - continue; - - var groupedByWell = filteredByTemplate.GroupBy(o => o.IdWell); - - var aggreagtedByWell = groupedByWell.Select(g => new WellOperationDto - { - IdCategory = IdCategory, - IdWell = g.Key, - IdWellSectionType = IdSection, - DepthStart = g.Min(o => o.DepthStart), - DurationHours = g.Sum(o => o.DurationHours), - OperationCategoryName = g.First().OperationCategoryName, - WellSectionTypeCaption = g.First().WellSectionTypeCaption, - }); - - var compositeOperation = aggreagtedByWell.OrderBy(o => o.DurationHours). - ThenByDescending(o => o.DepthStart) - .First(); - - compositeOperation.IdWell = 0; - compositeOperation.Day = compositeDay + compositeOperation.DurationHours; - - if (compositeDepth > compositeOperation.DepthStart) - compositeOperation.DepthStart = compositeDepth; - - compositeOperations.Add(compositeOperation); - - compositeDepth = compositeOperation.DepthStart; - compositeDay = compositeOperation.Day; - } - - var groupedByWellOperations = operations - .GroupBy(o => o.IdWell) - .ToDictionary(o => o.Key, o => o.ToArray()) - .Select(o => new WellCompositeOperationSourceDto() - { - Operations = o.Value, - Well = new WellDto() - { - Id = o.Key, - Caption = wellsDict[o.Key].Caption, - } - }); - - result.WellOperationsComposite = compositeOperations; - result.WellCompositeSourceOperations = groupedByWellOperations; - - return result; + newDto.IdWellSectionType = wellSectionProductionString; + newDto.WellSectionTypeCaption = sectionTypes[newDto.IdWellSectionType]; } - private static WellOperationDto CreateCompositeOperation( - WellOperationDto dto, - IDictionary sectionTypes, - IDictionary operationCategories) + if ((SettingsForSectionCategoryChange.TryGetValue((newDto.IdWellSectionType, newDto.IdCategory), out int newIdCategory))) { - var newDto = dto.Adapt(); - if (newDto.IdWellSectionType == wellSectionTransportTable) - { - newDto.IdWellSectionType = wellSectionProductionString; - newDto.WellSectionTypeCaption = sectionTypes[newDto.IdWellSectionType]; - } - - if ((SettingsForSectionCategoryChange.TryGetValue((newDto.IdWellSectionType, newDto.IdCategory), out int newIdCategory))) - { - newDto.IdCategory = newIdCategory; - newDto.OperationCategoryName = operationCategories[newDto.IdCategory] ?? string.Empty; - } - - return newDto; + newDto.IdCategory = newIdCategory; + newDto.OperationCategoryName = operationCategories[newDto.IdCategory] ?? string.Empty; } + + return newDto; } } diff --git a/AsbCloudWebApi/Controllers/SAUB/GtrWitsController.cs b/AsbCloudWebApi/Controllers/SAUB/GtrWitsController.cs index a339f1b8..f234d33c 100644 --- a/AsbCloudWebApi/Controllers/SAUB/GtrWitsController.cs +++ b/AsbCloudWebApi/Controllers/SAUB/GtrWitsController.cs @@ -11,6 +11,9 @@ using System.Threading.Tasks; using AsbCloudApp.Exceptions; using AsbCloudApp.Requests; using Microsoft.AspNetCore.Http; +using AsbCloudApp.Data; +using System; +using Org.BouncyCastle.Asn1.Ocsp; namespace AsbCloudWebApi.Controllers.SAUB { @@ -58,6 +61,31 @@ namespace AsbCloudWebApi.Controllers.SAUB return Ok(dtos); } + /// + /// Возвращает диапазон дат за которые есть телеметрия за период времени + /// + /// + /// + /// + /// + /// + [HttpGet("{idWell}/dateRange")] + [ProducesResponseType(typeof(DatesRangeDto), (int)System.Net.HttpStatusCode.OK)] + [ProducesResponseType((int)System.Net.HttpStatusCode.NotFound)] + [ProducesResponseType((int)System.Net.HttpStatusCode.NoContent)] + public virtual async Task> GetRangeAsync( + [FromRoute] int idWell, + DateTimeOffset? geDate, + DateTimeOffset? leDate, + CancellationToken token) + { + await AssertUserHasAccessToWellAsync(idWell, token); + + var range = await gtrRepository.GetRangeAsync(idWell, geDate, leDate, token); + + return Ok(range); + } + /// /// Получить загруженные данные ГТИ по скважине /// @@ -81,7 +109,7 @@ namespace AsbCloudWebApi.Controllers.SAUB if (!isCompanyOwnsWell) return Forbid(); - var content = await gtrRepository.GetAsync(idWell, request.Begin, + var content = await gtrRepository.GetAsync(idWell, request.Begin?.DateTime, request.IntervalSec, request.ApproxPointsCount, token).ConfigureAwait(false); return Ok(content);