From be8287f041b6e9109fcaa667d16cb6141686b590 Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Thu, 28 Apr 2022 15:04:13 +0500 Subject: [PATCH] Add detected operations controller --- AsbCloudApp/Data/DetectedOperationDto.cs | 65 ++++++ .../Requests/DetectedOperationRequest.cs | 65 ++++++ .../Services/IDetectedOperationService.cs | 15 ++ AsbCloudDb/EFExtentionOrderBy.cs | 206 ++++++++++++++++++ AsbCloudDb/EFExtentions.cs | 9 +- AsbCloudInfrastructure/DependencyInjection.cs | 4 +- .../DetectedOperationService.cs | 120 ++++++++++ .../DetectOperations/DetectorAbstract.cs | 23 +- .../Detectors/DetectorDrillingRotor.cs | 8 +- .../Detectors/DetectorDrillingSlide.cs | 8 +- .../Detectors/DetectorSlipsTime.cs | 5 +- ...=> OperationDetectionBackgroundService.cs} | 41 ++-- .../Services/DetectOperations/Readme.md.txt | 1 + .../Specifications/Время в клиньях.md | 36 +++ .../WellOperationService.cs | 1 - .../SAUB/DetectedOperationController.cs | 89 ++++++++ ConsoleApp1/Program.cs | 18 +- 17 files changed, 655 insertions(+), 59 deletions(-) create mode 100644 AsbCloudApp/Data/DetectedOperationDto.cs create mode 100644 AsbCloudApp/Requests/DetectedOperationRequest.cs create mode 100644 AsbCloudApp/Services/IDetectedOperationService.cs create mode 100644 AsbCloudDb/EFExtentionOrderBy.cs create mode 100644 AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs rename AsbCloudInfrastructure/Services/DetectOperations/{OperationDetectorBackgroundService.cs => OperationDetectionBackgroundService.cs} (87%) create mode 100644 AsbCloudInfrastructure/Services/DetectOperations/Readme.md.txt create mode 100644 AsbCloudInfrastructure/Services/DetectOperations/Specifications/Время в клиньях.md create mode 100644 AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs diff --git a/AsbCloudApp/Data/DetectedOperationDto.cs b/AsbCloudApp/Data/DetectedOperationDto.cs new file mode 100644 index 00000000..bdcb50b4 --- /dev/null +++ b/AsbCloudApp/Data/DetectedOperationDto.cs @@ -0,0 +1,65 @@ +using System; + +namespace AsbCloudApp.Data +{ + /// + /// Автоматически определяемая операция + /// + public class DetectedOperationDto + { + /// + /// Id в БД + /// + public int Id { get; set; } + + /// + /// Id скважины + /// + public int IdWell { get; set; } + + /// + /// Id названия/описания операции + /// + public int IdCategory { get; set; } + + /// + /// Id пользователя панели + /// + public int IdUsersAtStart { get; set; } + + /// + /// Дата начала операции в часовом поясе скважины + /// + public DateTime DateStart { get; set; } + + /// + /// Дата завершения операции в часовом поясе скважины + /// + public DateTime DateEnd { get; set; } + + /// + /// Продолжительность операции в минутах + /// + public double DurationMinutes => (DateEnd - DateStart).TotalMinutes; + + /// + /// глубина на начало операции, м + /// + public double DepthStart { get; set; } + + /// + /// глубина на завершения операции, м + /// + public double DepthEnd { get; set; } + + /// + /// название/описание операции + /// + public WellOperationCategoryDto OperationCategory { get; set; } + + /// + /// Пользователь панели оператора + /// + public string TelemetryUserName { get; set; } + } +} diff --git a/AsbCloudApp/Requests/DetectedOperationRequest.cs b/AsbCloudApp/Requests/DetectedOperationRequest.cs new file mode 100644 index 00000000..073a2bdc --- /dev/null +++ b/AsbCloudApp/Requests/DetectedOperationRequest.cs @@ -0,0 +1,65 @@ +using System.Collections.Generic; +using System; + +namespace AsbCloudApp.Requests +{ + /// + /// Параметры запроса на получение операций определенных по телеметрии + /// + public class DetectedOperationRequest: RequestBase + { + /// + /// категории операций + /// + public IEnumerable CategoryIds { get; set; } + + /// + /// Больше или равно дате + /// + public DateTime? GtDate { get; set; } + + /// + /// Меньше или равно дате + /// + public DateTime? LtDate { get; set; } + + /// + /// Больше или равно глубины забоя + /// + public double? GtDepth { get; set; } + + /// + /// Меньше или равно глубины забоя + /// + public double? LtDepth { get; set; } + + /// + /// Фильтр по пользователю панели + /// + public int? EqIdTelemetryUser { get; set; } + + } + + /// + /// Базовые параметры запроса + /// + public class RequestBase + { + /// + /// Параметр пагинации + /// + public int? Skip { get; set; } + + /// + /// Параметр пагинации + /// + public int? Take { get; set; } + + /// + /// Сортировки: + /// Содержат список названий полей сортировки + /// Указать направление сортировки можно через пробел "asc" или "desc" + /// + public IEnumerable SortFields { get; set; } + } +} diff --git a/AsbCloudApp/Services/IDetectedOperationService.cs b/AsbCloudApp/Services/IDetectedOperationService.cs new file mode 100644 index 00000000..143a1379 --- /dev/null +++ b/AsbCloudApp/Services/IDetectedOperationService.cs @@ -0,0 +1,15 @@ +using AsbCloudApp.Data; +using AsbCloudApp.Requests; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudApp.Services +{ + public interface IDetectedOperationService + { + Task> GetCategoriesAsync(CancellationToken token); + Task> GetAsync(int idWell, Requests.DetectedOperationRequest request, CancellationToken token); + Task DeleteAsync(int idWell, DetectedOperationRequest request, CancellationToken token); + } +} diff --git a/AsbCloudDb/EFExtentionOrderBy.cs b/AsbCloudDb/EFExtentionOrderBy.cs new file mode 100644 index 00000000..8178bb33 --- /dev/null +++ b/AsbCloudDb/EFExtentionOrderBy.cs @@ -0,0 +1,206 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudDb +{ +#nullable enable + public static class EFExtentionsSortBy + { + class TypeAcessor + { + public LambdaExpression KeySelector { get; set; } + public MethodInfo OrderBy { get; set; } + public MethodInfo OrderByDescending { get; set; } + public MethodInfo ThenBy { get; set; } + public MethodInfo ThenByDescending { get; set; } + } + + static ConcurrentDictionary> TypePropSelectors { get; set; } = new(); + + static MethodInfo methodOrderBy = GetExtOrderMethod("OrderBy"); + + static MethodInfo methodOrderByDescending = GetExtOrderMethod("OrderByDescending"); + + static MethodInfo methodThenBy = GetExtOrderMethod("ThenBy"); + + static MethodInfo methodThenByDescending = GetExtOrderMethod("ThenByDescending"); + + static MethodInfo GetExtOrderMethod(string methodName) + => typeof(System.Linq.Queryable) + .GetMethods() + .Where(m => m.Name == methodName && + m.IsGenericMethodDefinition && + m.GetParameters().Length == 2 && + m.GetParameters()[1].ParameterType.IsAssignableTo(typeof(LambdaExpression))) + .Single(); + + private static Dictionary MakeTypeAcessors (Type type) + { + var propContainer = new Dictionary(); + var properties = type.GetProperties(); + foreach (var propertyInfo in properties) + { + var name = propertyInfo.Name.ToLower(); + ParameterExpression arg = Expression.Parameter(type, "x"); + MemberExpression property = Expression.Property(arg, propertyInfo.Name); + var selector = Expression.Lambda(property, new ParameterExpression[] { arg }); + var typeAccessor = new TypeAcessor + { + KeySelector = selector, + OrderBy = methodOrderBy.MakeGenericMethod(type, propertyInfo.PropertyType), + OrderByDescending = methodOrderByDescending.MakeGenericMethod(type, propertyInfo.PropertyType), + ThenBy = methodThenBy.MakeGenericMethod(type, propertyInfo.PropertyType), + ThenByDescending = methodThenByDescending.MakeGenericMethod(type, propertyInfo.PropertyType), + }; + + propContainer.Add(name, typeAccessor); + } + return propContainer; + } + + /// + /// Добавить в запрос сортировку по возрастанию или убыванию. + /// + /// + /// + /// + /// Свойство сортировки. + /// Состоит из названия свойства (в любом регистре) + /// и опционально указания направления сортировки "asc" или "desc" + /// + /// + /// var query = query("Date desc"); + /// + /// Запрос с примененной сортировкой + public static IOrderedQueryable SortBy( + this IQueryable query, + IEnumerable? propertySorts) + { + if (propertySorts?.Any() != true) + return (IOrderedQueryable)query; + + var sortEnum = propertySorts.GetEnumerator(); + sortEnum.MoveNext(); + var orderedQuery = query.SortBy(sortEnum.Current); + + while(sortEnum.MoveNext()) + orderedQuery = orderedQuery.ThenSortBy(sortEnum.Current); + + return orderedQuery; + } + + /// + /// Добавить в запрос сортировку по возрастанию или убыванию. + /// Этот метод сбросит ранее наложенные сортировки. + /// + /// + /// + /// + /// Свойство сортировки. + /// Состоит из названия свойства (в любом регистре) + /// и опционально указания направления сортировки "asc" или "desc" + /// + /// + /// var query = query("Date desc"); + /// + /// Запрос с примененной сортировкой + public static IOrderedQueryable SortBy( + this IQueryable query, + string propertySort) + { + var parts = propertySort.Split(" ", 2, StringSplitOptions.RemoveEmptyEntries); + var isDesc = parts.Length >= 2 && parts[1].ToLower().Trim() == "desc"; + var propertyName = parts[0]; + + var newQuery = query.SortBy(propertyName, isDesc); + return newQuery; + } + + /// + /// Добавить в запрос дополнительную сортировку по возрастанию или убыванию. + /// + /// + /// + /// + /// Свойство сортировки. + /// Состоит из названия свойства (в любом регистре) + /// и опционально указания направления сортировки "asc" или "desc" + /// + /// + /// var query = query("Date desc"); + /// + /// Запрос с примененной сортировкой + public static IOrderedQueryable ThenSortBy( + this IOrderedQueryable query, + string propertySort) + { + var parts = propertySort.Split(" ", 2, StringSplitOptions.RemoveEmptyEntries); + var isDesc = parts.Length >= 2 && parts[1].ToLower().Trim() == "desc"; + var propertyName = parts[0]; + + var newQuery = query.ThenSortBy(propertyName, isDesc); + return newQuery; + } + + /// + /// Добавить в запрос сортировку по возрастанию или убыванию + /// + /// + /// + /// Название свойства (в любом регистре) + /// Сортировать по убыванию + /// Запрос с примененной сортировкой + public static IOrderedQueryable SortBy( + this IQueryable query, + string propertyName, + bool isDesc) + { + var typePropSelector = TypePropSelectors.GetOrAdd(typeof(TSource), MakeTypeAcessors); + var propertyNamelower = propertyName.ToLower(); + var typeAccessor = typePropSelector[propertyNamelower]; + + var genericMethod = isDesc + ? typeAccessor.OrderByDescending + : typeAccessor.OrderBy; + + var newQuery = (IOrderedQueryable)genericMethod + .Invoke(genericMethod, new object[] { query, typeAccessor.KeySelector })!; + return newQuery; + } + + /// + /// Добавить в запрос дополнительную сортировку по возрастанию или убыванию + /// + /// + /// + /// Название свойства (в любом регистре) + /// Сортировать по убыванию + /// Запрос с примененной сортировкой + public static IOrderedQueryable ThenSortBy( + this IOrderedQueryable query, + string propertyName, + bool isDesc) + { + var typePropSelector = TypePropSelectors.GetOrAdd(typeof(TSource), MakeTypeAcessors); + var propertyNamelower = propertyName.ToLower(); + var typeAccessor = typePropSelector[propertyNamelower]; + var genericMethod = isDesc + ? typeAccessor.ThenByDescending + : typeAccessor.ThenBy; + + var newQuery = (IOrderedQueryable)genericMethod + .Invoke(genericMethod, new object[] { query, typeAccessor.KeySelector })!; + return newQuery; + } + } +#nullable disable +} diff --git a/AsbCloudDb/EFExtentions.cs b/AsbCloudDb/EFExtentions.cs index 3024e9ae..8d26b468 100644 --- a/AsbCloudDb/EFExtentions.cs +++ b/AsbCloudDb/EFExtentions.cs @@ -12,7 +12,7 @@ namespace AsbCloudDb public static class EFExtentions { static Dictionary QueryFactories { get; set; } = new(); - + static QueryStringFactory GetQueryStringFactory(DbSet dbSet) where T : class { @@ -82,13 +82,6 @@ namespace AsbCloudDb public string MakeInsertOrUpdateSql(IEnumerable items) { - /* EXAMPLE: - INSERT INTO the_table (id, column_1, column_2) - VALUES (1, 'A', 'X'), (2, 'B', 'Y'), (3, 'C', 'Z') - ON CONFLICT (id) DO UPDATE - SET column_1 = excluded.column_1, - column_2 = excluded.column_2; - */ var builder = new StringBuilder(insertHeader, 7); BuildRows(builder, items); if (string.IsNullOrEmpty(pk)) diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index d92e3c67..eeafa2bd 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -5,6 +5,7 @@ using AsbCloudDb.Model; using AsbCloudInfrastructure.Services; using AsbCloudInfrastructure.Services.Analysis; using AsbCloudInfrastructure.Services.Cache; +using AsbCloudInfrastructure.Services.DetectOperations; using AsbCloudInfrastructure.Services.DrillingProgram; using AsbCloudInfrastructure.Services.SAUB; using AsbCloudInfrastructure.Services.WellOperationService; @@ -53,7 +54,7 @@ namespace AsbCloudInfrastructure services.AddScoped(); services.AddScoped(); - services.AddHostedService();// replace by BackgroundWorkerService + services.AddHostedService(); services.AddSingleton(new WitsInfoService()); services.AddSingleton(new CacheDb()); @@ -85,6 +86,7 @@ namespace AsbCloudInfrastructure services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); // admin crud services: services.AddTransient, CrudServiceBase>(); // может быть включен в сервис TelemetryService diff --git a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs new file mode 100644 index 00000000..76068849 --- /dev/null +++ b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs @@ -0,0 +1,120 @@ +using AsbCloudApp.Data; +using AsbCloudApp.Requests; +using AsbCloudApp.Services; +using AsbCloudDb; +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.Services.DetectOperations +{ + public class DetectedOperationService: IDetectedOperationService + { + private readonly IAsbCloudDbContext db; + private readonly IWellService wellService; + + public DetectedOperationService(IAsbCloudDbContext db, IWellService wellService) + { + this.db = db; + this.wellService = wellService; + } + + public async Task> GetAsync(int idWell, DetectedOperationRequest request, CancellationToken token) + { + var well = await wellService.GetAsync(idWell, token); + if (well?.IdTelemetry is null || well.Timezone is null) + return null; + + var query = BuildQuery(well, request) + .AsNoTracking(); + + var data = await query.ToListAsync(token); + var dtos = data.Select(o => Convert(o, well)); + return dtos; + } + + public async Task DeleteAsync(int idWell, DetectedOperationRequest request, CancellationToken token) + { + var well = await wellService.GetAsync(idWell, token); + if (well?.IdTelemetry is null || well.Timezone is null) + return 0; + + var query = BuildQuery(well, request); + db.DetectedOperations.RemoveRange(query); + return await db.SaveChangesAsync(token); + } + + private IQueryable BuildQuery(WellDto well, DetectedOperationRequest request) + { + if (well?.IdTelemetry is null || well.Timezone is null) + return null; + + var query = db.Set() + .Include(o => o.OperationCategory) + .Where(o => o.IdTelemetry == well.IdTelemetry); + + if (request is not null) + { + if (request.CategoryIds is not null) + query = query.Where(o => request.CategoryIds.Contains(o.IdCategory)); + + if (request.GtDate is not null) + query = query.Where(o => o.DateStart >= request.GtDate.Value.ToUtcDateTimeOffset(well.Timezone.Hours)); + + if (request.LtDate is not null) + query = query.Where(o => o.DateStart <= request.LtDate.Value.ToUtcDateTimeOffset(well.Timezone.Hours)); + + if (request.GtDepth is not null) + query = query.Where(o => o.DepthStart >= request.GtDepth); + + if (request.LtDepth is not null) + query = query.Where(o => o.DepthStart <= request.LtDepth); + + if (request.EqIdTelemetryUser is not null) + query = query.Where(o => o.IdUsersAtStart == request.EqIdTelemetryUser); + } + + if (request?.SortFields?.Any() == true) + { + query = query.SortBy(request.SortFields); + } + else + query = query + .OrderBy(o => o.DateStart) + .ThenBy(o => o.DepthStart); + + if (request?.Skip > 0) + query = query.Skip((int)request.Skip); + + if (request?.Take > 0) + query = query.Take((int)request.Take); + + return query; + } + + private static DetectedOperationDto Convert(DetectedOperation operation, WellDto well) + { + var dto = operation.Adapt(); + dto.IdWell = well.Id; + dto.DateStart = operation.DateStart.ToRemoteDateTime(well.Timezone.Hours); + dto.DateEnd = operation.DateEnd.ToRemoteDateTime(well.Timezone.Hours); + return dto; + } + + public async Task> GetCategoriesAsync(CancellationToken token) + { + var result = await db.WellOperationCategories + .Where(c => c.Id < 1000) + .Select(c => c.Adapt()) + .ToArrayAsync(token); + + return result; + } + + } +} diff --git a/AsbCloudInfrastructure/Services/DetectOperations/DetectorAbstract.cs b/AsbCloudInfrastructure/Services/DetectOperations/DetectorAbstract.cs index 24911a25..256fa268 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/DetectorAbstract.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/DetectorAbstract.cs @@ -9,28 +9,25 @@ namespace AsbCloudInfrastructure.Services.DetectOperations { #nullable enable + abstract class DetectorAbstract { - /* ## Концепт определения операций - * Есть словарь детекторов. - * Определить начальную позицию архива. - * Пройти детекторами до конца архива. - * Каждый детектор на небольшому фрагменту определяет начало операции. - * При нахождении начала ищет конец и смещает положение поиска - */ public int StepLength { get; set; } = 3; public int FragmentLength { get; set; } = 6; - public int IdOperationType { get; } - public string OperationName { get; } + public int IdCategory { get; } // TODO: assert MaxDurationSeconds and MinDurationSeconds public double MaxDurationSeconds { get; } = 31 * 24 * 60 * 60; public double MinDurationSeconds { get; } = 3; - public DetectorAbstract(int idType, string operationName) + /// + /// Конструктор детектора. + /// Словарь с IdCategory в дефолтных данных AsbCloudDbContext для таблицы WellOperationCategory + /// + /// ключ названия/описания операции из таблицы WellOperationCategory + public DetectorAbstract(int IdCategory) { - IdOperationType = idType; - OperationName = operationName; + this.IdCategory = IdCategory; } public virtual DetectedOperation? DetectOrDefault(DetectableTelemetry[] telemetry, ref int position) @@ -44,7 +41,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations { var result = new DetectedOperation { - IdCategory = IdOperationType, + IdCategory = IdCategory, IdUsersAtStart = telemetry[position].IdUser ?? -1, DateStart = telemetry[position].DateTime, DateEnd = telemetry[skip].DateTime, diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrillingRotor.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrillingRotor.cs index 10412dc4..019b6ba4 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrillingRotor.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrillingRotor.cs @@ -5,7 +5,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors #nullable enable class DetectorDrillingRotor : DetectorAbstract { - public DetectorDrillingRotor() : base(10_002, "Бурение в роторе") + public DetectorDrillingRotor() : base(1) { FragmentLength = 15; StepLength = 10; @@ -13,6 +13,12 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors protected override bool DetectStart(DetectableTelemetry[] telemetry, int position) { + var firstItem = telemetry[position]; + var delta = firstItem.WellDepth - firstItem.BitDepth; + if (delta is not null && + System.Math.Abs((float)delta) > 1d) + return false; + var fragment = telemetry[position .. (position + FragmentLength)]; const double minRop = 5; //м/час diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrillingSlide.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrillingSlide.cs index ff20f6c9..ef96ed84 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrillingSlide.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrillingSlide.cs @@ -5,13 +5,19 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors #nullable enable class DetectorDrillingSlide : DetectorAbstract { - public DetectorDrillingSlide() : base(10_003, "Бурение в слайде") + public DetectorDrillingSlide() : base(3) { FragmentLength = 10; } protected override bool DetectStart(DetectableTelemetry[] telemetry, int position) { + var firstItem = telemetry[position]; + var delta = firstItem.WellDepth - firstItem.BitDepth; + if (delta is not null && + System.Math.Abs((float)delta) > 1d) + return false; + var fragment = telemetry[position .. (position + FragmentLength)]; const double minRop = 5; //м/час diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlipsTime.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlipsTime.cs index d04ccbc3..764d65cc 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlipsTime.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlipsTime.cs @@ -3,10 +3,7 @@ #nullable enable class DetectorSlipsTime : DetectorAbstract { - public DetectorSlipsTime() : base(10_001, "Удержание в клиньях") - { - } - + public DetectorSlipsTime() :base(14) {} public double HookWeightSP { get; set; } = 20; public double PressureSP { get; set; } = 15; public double PosisionSP { get; set; } = 8; diff --git a/AsbCloudInfrastructure/Services/DetectOperations/OperationDetectorBackgroundService.cs b/AsbCloudInfrastructure/Services/DetectOperations/OperationDetectionBackgroundService.cs similarity index 87% rename from AsbCloudInfrastructure/Services/DetectOperations/OperationDetectorBackgroundService.cs rename to AsbCloudInfrastructure/Services/DetectOperations/OperationDetectionBackgroundService.cs index 6301c36d..2834a1fd 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/OperationDetectorBackgroundService.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/OperationDetectionBackgroundService.cs @@ -11,7 +11,7 @@ using System.Diagnostics; namespace AsbCloudInfrastructure.Services.DetectOperations { - public class OperationDetectorBackgroundService : BackgroundService + public class OperationDetectionBackgroundService : BackgroundService { private readonly IEnumerable detectors = new List { @@ -25,7 +25,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations private readonly string connectionString; private readonly TimeSpan period = TimeSpan.FromHours(1); - public OperationDetectorBackgroundService(IConfiguration configuration) + public OperationDetectionBackgroundService(IConfiguration configuration) { minStepLength = detectors.Min(d => d.StepLength); minStepLength = minStepLength > 0 ? minStepLength : 3; @@ -75,40 +75,38 @@ namespace AsbCloudInfrastructure.Services.DetectOperations private async Task DetectedAllTelemetriesAsync(IAsbCloudDbContext db, CancellationToken token) { - var lastDetectedDatesQuery = db.DetectedOperations + var lastDetectedDates = await db.DetectedOperations .GroupBy(o => o.IdTelemetry) .Select(g => new { IdTelemetry = g.Key, LastDate = g.Max(o => o.DateEnd) - }); + }) + .ToListAsync(token); - var lastDetectedDates = await db.Telemetries - .Join(lastDetectedDatesQuery, - t => t.Id, + var telemetryIds = await db.Telemetries + .Where(t => t.Info != null && t.TimeZone != null) + .Select(t => t.Id) + .ToListAsync(token); + + var JounedlastDetectedDates = telemetryIds + .GroupJoin(lastDetectedDates, + t => t, o => o.IdTelemetry, (outer, inner) => new { - IdTelemetry = outer.Id, - inner.LastDate - }) - .ToListAsync(token); + IdTelemetry = outer, + LastDate = inner.SingleOrDefault()?.LastDate , + }); - bool isAnyAdded = false; - - foreach (var item in lastDetectedDates) + foreach (var item in JounedlastDetectedDates) { - var newOperations = await DetectOperationsAsync(item.IdTelemetry, item.LastDate, db, token); + var newOperations = await DetectOperationsAsync(item.IdTelemetry, item.LastDate??DateTimeOffset.MinValue, db, token); if (newOperations.Any()) - { db.DetectedOperations.AddRange(newOperations); - isAnyAdded = true; - } } - return isAnyAdded - ? await db.SaveChangesAsync(token) - : 0; + return await db.SaveChangesAsync(token); } private async Task> DetectOperationsAsync(int idTelemetry, DateTimeOffset begin, IAsbCloudDbContext db, CancellationToken token) @@ -171,6 +169,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations { isDetected1 = true; isDetected = true; + detectedOperation.IdTelemetry = idTelemetry; detectedOperations.Add(detectedOperation); startDate = detectedOperation.DateEnd; break; diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Readme.md.txt b/AsbCloudInfrastructure/Services/DetectOperations/Readme.md.txt new file mode 100644 index 00000000..5f282702 --- /dev/null +++ b/AsbCloudInfrastructure/Services/DetectOperations/Readme.md.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Specifications/Время в клиньях.md b/AsbCloudInfrastructure/Services/DetectOperations/Specifications/Время в клиньях.md new file mode 100644 index 00000000..c6f29158 --- /dev/null +++ b/AsbCloudInfrastructure/Services/DetectOperations/Specifications/Время в клиньях.md @@ -0,0 +1,36 @@ +> Из писма Гранова А.П. от 19.04.2022 13:29 "Алгоритм определения наращивания БИ.docx" + +# Алгоритм определения времени в клиньях + +## Описание: +Наращивание бурильного инструмента – операция, во время которой после добуривания очередной трубы/ свечи +циркуляция выключается, инструмент разгружается в клиньях (остается только вес крюкоблока и ВСП), +происходит развинчивание трубы от верхнего силового привода, берется очередная труба/ свеча, +свинчивается с инструментом в клиньях, свинчивается с верхним силовым приводом, происходит подъем инструмента, +вес на крюке увеличивается. Далее включается циркуляция и происходит механическое бурение. + +Наращиванию предшествует механическое бурение (наличие давления, увеличение глубины забоя) и +после наращивания механическое бурение возобновляется (наличие давления, Увеличение глубины забоя). + +> Это не учитывать в методе, так как предыдущая и последующая операция могут быть определены не корректно. + + Наращивается определяется как время между: +- разгрузкой инструмента на клинья (остается только вес крюкоблока и ВСП). + При этом давление менее 15 атм. В случае давления более 15 атм считать началом операции как + снижение давления менее 15 атм и началом движения талевого блока вверх. +- снятие инструмента с клиньев (вес увеличивается более, чем на 1т). +- При этом движение талевого блока происходит вверх. + +## Метод определения: + +> Исправлено на совещании от 19.04.2022 16:50 + +``` +Признак начала операции = +(параметр «вес на крюке» < 22 тонн) И +(давление < 15 атм) И +(положение талевого блока < 8) +Признак окончания операции = +(вес на крюке > 22 ) И +(давление > 15 атм) +``` \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/WellOperationService/WellOperationService.cs b/AsbCloudInfrastructure/Services/WellOperationService/WellOperationService.cs index dbaee017..b552717e 100644 --- a/AsbCloudInfrastructure/Services/WellOperationService/WellOperationService.cs +++ b/AsbCloudInfrastructure/Services/WellOperationService/WellOperationService.cs @@ -94,7 +94,6 @@ namespace AsbCloudInfrastructure.Services.WellOperationService if (end != default) { - var endOffset = end.ToUtcDateTimeOffset(timezone.Hours); query = query.Where(e => e.DateStart <= endOffset); } diff --git a/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs b/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs new file mode 100644 index 00000000..70547e8f --- /dev/null +++ b/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs @@ -0,0 +1,89 @@ +using AsbCloudApp.Data; +using AsbCloudApp.Services; +using Microsoft.AspNetCore.Mvc; +using System.Collections.Generic; +using System.Threading.Tasks; +using System.Threading; +using AsbCloudApp.Requests; + +namespace AsbCloudWebApi.Controllers.SAUB +{ + /// + /// Операции определенные по телеметрии САУБ + /// + [Route("api/well/{idWell}/[controller]")] + [ApiController] + public class DetectedOperationController : ControllerBase + { + private readonly IDetectedOperationService detectedOperationService; + private readonly IWellService wellService; + + public DetectedOperationController(IDetectedOperationService detectedOperationService, IWellService wellService) + { + this.detectedOperationService = detectedOperationService; + this.wellService = wellService; + } + + /// + /// получить справочник операций. Отличается от операций заводимых вручную + /// + /// + /// + [HttpGet("categories")] + [ProducesResponseType(typeof(IEnumerable), (int)System.Net.HttpStatusCode.OK)] + public async Task GetCategoriesAsync(CancellationToken token = default) + { + var result = await detectedOperationService.GetCategoriesAsync(token); + return Ok(result); + } + + /// + /// Получить фильтрованный список операций по телеметрии САУБ + /// + /// + /// + /// + /// + [HttpGet] + [ProducesResponseType(typeof(IEnumerable), (int)System.Net.HttpStatusCode.OK)] + public async Task GetAsync( + int idWell, + [FromQuery] DetectedOperationRequest request, + CancellationToken token = default) + { + int? idCompany = User.GetCompanyId(); + if (idCompany is null || !await wellService.IsCompanyInvolvedInWellAsync((int)idCompany, + idWell, token).ConfigureAwait(false)) + return Forbid(); + + var result = await detectedOperationService.GetAsync(idWell, request, token); + return Ok(result); + } + + /// + /// Удалить операции. + /// Удаленные операции будут определены повторно сервисом автоматизированного определения операций. + /// Может потребоваться при изменении алгоритмов определения + /// + /// + /// + /// + /// + [HttpDelete] + [Permission] + [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)] + public async Task DeleteAsync( + int idWell, + [FromQuery] DetectedOperationRequest request, + CancellationToken token = default) + { + int? idCompany = User.GetCompanyId(); + if (idCompany is null || !await wellService.IsCompanyInvolvedInWellAsync((int)idCompany, + idWell, token).ConfigureAwait(false)) + return Forbid(); + + var result = await detectedOperationService.DeleteAsync(idWell, request, token); + return Ok(result); + } + } +} diff --git a/ConsoleApp1/Program.cs b/ConsoleApp1/Program.cs index 9da430b2..7ab6a89f 100644 --- a/ConsoleApp1/Program.cs +++ b/ConsoleApp1/Program.cs @@ -1,6 +1,7 @@ -using System; +using AsbCloudDb; +using Microsoft.EntityFrameworkCore; +using System; using System.Linq; -using System.Threading; namespace ConsoleApp1 { @@ -9,14 +10,13 @@ namespace ConsoleApp1 static void Main(/*string[] args*/) { // use ServiceFactory to make services - var db = ServiceFactory.MakeContext(); - var service = new AsbCloudInfrastructure.Services.DetectOperations.DetectorService(); - var start = new DateTimeOffset(2021, 1, 30, 23, 00, 00, TimeSpan.FromHours(0)); - var operations = service.DetectOperationsAsync(146, start, db, CancellationToken.None).Result; - foreach (var operation in operations) - Console.WriteLine(operation); - + var q = db.DetectedOperations + .Include(o => o.OperationCategory) + .Where(o => true) + .SortBy(new string[] { "DateStart desc", "depthstart" }); + var sql = q.ToQueryString(); + var items = q.ToList(); Console.WriteLine("End of program"); Console.ReadLine(); }