forked from ddrilling/AsbCloudServer
Add detected operations controller
This commit is contained in:
parent
45fb5ab8e8
commit
be8287f041
65
AsbCloudApp/Data/DetectedOperationDto.cs
Normal file
65
AsbCloudApp/Data/DetectedOperationDto.cs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace AsbCloudApp.Data
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Автоматически определяемая операция
|
||||||
|
/// </summary>
|
||||||
|
public class DetectedOperationDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Id в БД
|
||||||
|
/// </summary>
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Id скважины
|
||||||
|
/// </summary>
|
||||||
|
public int IdWell { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Id названия/описания операции
|
||||||
|
/// </summary>
|
||||||
|
public int IdCategory { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Id пользователя панели
|
||||||
|
/// </summary>
|
||||||
|
public int IdUsersAtStart { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Дата начала операции в часовом поясе скважины
|
||||||
|
/// </summary>
|
||||||
|
public DateTime DateStart { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Дата завершения операции в часовом поясе скважины
|
||||||
|
/// </summary>
|
||||||
|
public DateTime DateEnd { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Продолжительность операции в минутах
|
||||||
|
/// </summary>
|
||||||
|
public double DurationMinutes => (DateEnd - DateStart).TotalMinutes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// глубина на начало операции, м
|
||||||
|
/// </summary>
|
||||||
|
public double DepthStart { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// глубина на завершения операции, м
|
||||||
|
/// </summary>
|
||||||
|
public double DepthEnd { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// название/описание операции
|
||||||
|
/// </summary>
|
||||||
|
public WellOperationCategoryDto OperationCategory { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Пользователь панели оператора
|
||||||
|
/// </summary>
|
||||||
|
public string TelemetryUserName { get; set; }
|
||||||
|
}
|
||||||
|
}
|
65
AsbCloudApp/Requests/DetectedOperationRequest.cs
Normal file
65
AsbCloudApp/Requests/DetectedOperationRequest.cs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace AsbCloudApp.Requests
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Параметры запроса на получение операций определенных по телеметрии
|
||||||
|
/// </summary>
|
||||||
|
public class DetectedOperationRequest: RequestBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// категории операций
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<int> CategoryIds { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Больше или равно дате
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? GtDate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Меньше или равно дате
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? LtDate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Больше или равно глубины забоя
|
||||||
|
/// </summary>
|
||||||
|
public double? GtDepth { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Меньше или равно глубины забоя
|
||||||
|
/// </summary>
|
||||||
|
public double? LtDepth { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Фильтр по пользователю панели
|
||||||
|
/// </summary>
|
||||||
|
public int? EqIdTelemetryUser { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Базовые параметры запроса
|
||||||
|
/// </summary>
|
||||||
|
public class RequestBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Параметр пагинации
|
||||||
|
/// </summary>
|
||||||
|
public int? Skip { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Параметр пагинации
|
||||||
|
/// </summary>
|
||||||
|
public int? Take { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Сортировки:
|
||||||
|
/// Содержат список названий полей сортировки
|
||||||
|
/// Указать направление сортировки можно через пробел "asc" или "desc"
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<string> SortFields { get; set; }
|
||||||
|
}
|
||||||
|
}
|
15
AsbCloudApp/Services/IDetectedOperationService.cs
Normal file
15
AsbCloudApp/Services/IDetectedOperationService.cs
Normal file
@ -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<IEnumerable<WellOperationCategoryDto>> GetCategoriesAsync(CancellationToken token);
|
||||||
|
Task<IEnumerable<DetectedOperationDto>> GetAsync(int idWell, Requests.DetectedOperationRequest request, CancellationToken token);
|
||||||
|
Task<int> DeleteAsync(int idWell, DetectedOperationRequest request, CancellationToken token);
|
||||||
|
}
|
||||||
|
}
|
206
AsbCloudDb/EFExtentionOrderBy.cs
Normal file
206
AsbCloudDb/EFExtentionOrderBy.cs
Normal file
@ -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<Type, Dictionary<string, TypeAcessor>> 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<string, TypeAcessor> MakeTypeAcessors (Type type)
|
||||||
|
{
|
||||||
|
var propContainer = new Dictionary<string, TypeAcessor>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Добавить в запрос сортировку по возрастанию или убыванию.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TSource"></typeparam>
|
||||||
|
/// <param name="query"></param>
|
||||||
|
/// <param name="propertySort">
|
||||||
|
/// Свойство сортировки.
|
||||||
|
/// Состоит из названия свойства (в любом регистре)
|
||||||
|
/// и опционально указания направления сортировки "asc" или "desc"
|
||||||
|
/// </param>
|
||||||
|
/// <example>
|
||||||
|
/// var query = query("Date desc");
|
||||||
|
/// </example>
|
||||||
|
/// <returns>Запрос с примененной сортировкой</returns>
|
||||||
|
public static IOrderedQueryable<TSource> SortBy<TSource>(
|
||||||
|
this IQueryable<TSource> query,
|
||||||
|
IEnumerable<string>? propertySorts)
|
||||||
|
{
|
||||||
|
if (propertySorts?.Any() != true)
|
||||||
|
return (IOrderedQueryable<TSource>)query;
|
||||||
|
|
||||||
|
var sortEnum = propertySorts.GetEnumerator();
|
||||||
|
sortEnum.MoveNext();
|
||||||
|
var orderedQuery = query.SortBy(sortEnum.Current);
|
||||||
|
|
||||||
|
while(sortEnum.MoveNext())
|
||||||
|
orderedQuery = orderedQuery.ThenSortBy(sortEnum.Current);
|
||||||
|
|
||||||
|
return orderedQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Добавить в запрос сортировку по возрастанию или убыванию.
|
||||||
|
/// Этот метод сбросит ранее наложенные сортировки.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TSource"></typeparam>
|
||||||
|
/// <param name="query"></param>
|
||||||
|
/// <param name="propertySort">
|
||||||
|
/// Свойство сортировки.
|
||||||
|
/// Состоит из названия свойства (в любом регистре)
|
||||||
|
/// и опционально указания направления сортировки "asc" или "desc"
|
||||||
|
/// </param>
|
||||||
|
/// <example>
|
||||||
|
/// var query = query("Date desc");
|
||||||
|
/// </example>
|
||||||
|
/// <returns>Запрос с примененной сортировкой</returns>
|
||||||
|
public static IOrderedQueryable<TSource> SortBy<TSource>(
|
||||||
|
this IQueryable<TSource> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Добавить в запрос дополнительную сортировку по возрастанию или убыванию.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TSource"></typeparam>
|
||||||
|
/// <param name="query"></param>
|
||||||
|
/// <param name="propertySort">
|
||||||
|
/// Свойство сортировки.
|
||||||
|
/// Состоит из названия свойства (в любом регистре)
|
||||||
|
/// и опционально указания направления сортировки "asc" или "desc"
|
||||||
|
/// </param>
|
||||||
|
/// <example>
|
||||||
|
/// var query = query("Date desc");
|
||||||
|
/// </example>
|
||||||
|
/// <returns>Запрос с примененной сортировкой</returns>
|
||||||
|
public static IOrderedQueryable<TSource> ThenSortBy<TSource>(
|
||||||
|
this IOrderedQueryable<TSource> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Добавить в запрос сортировку по возрастанию или убыванию
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TSource"></typeparam>
|
||||||
|
/// <param name="query"></param>
|
||||||
|
/// <param name="propertyName">Название свойства (в любом регистре)</param>
|
||||||
|
/// <param name="isDesc">Сортировать по убыванию</param>
|
||||||
|
/// <returns>Запрос с примененной сортировкой</returns>
|
||||||
|
public static IOrderedQueryable<TSource> SortBy<TSource>(
|
||||||
|
this IQueryable<TSource> 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<TSource>)genericMethod
|
||||||
|
.Invoke(genericMethod, new object[] { query, typeAccessor.KeySelector })!;
|
||||||
|
return newQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Добавить в запрос дополнительную сортировку по возрастанию или убыванию
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TSource"></typeparam>
|
||||||
|
/// <param name="query"></param>
|
||||||
|
/// <param name="propertyName">Название свойства (в любом регистре)</param>
|
||||||
|
/// <param name="isDesc">Сортировать по убыванию</param>
|
||||||
|
/// <returns>Запрос с примененной сортировкой</returns>
|
||||||
|
public static IOrderedQueryable<TSource> ThenSortBy<TSource>(
|
||||||
|
this IOrderedQueryable<TSource> 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<TSource>)genericMethod
|
||||||
|
.Invoke(genericMethod, new object[] { query, typeAccessor.KeySelector })!;
|
||||||
|
return newQuery;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#nullable disable
|
||||||
|
}
|
@ -12,7 +12,7 @@ namespace AsbCloudDb
|
|||||||
public static class EFExtentions
|
public static class EFExtentions
|
||||||
{
|
{
|
||||||
static Dictionary<Type, IQueryStringFactory> QueryFactories { get; set; } = new();
|
static Dictionary<Type, IQueryStringFactory> QueryFactories { get; set; } = new();
|
||||||
|
|
||||||
static QueryStringFactory<T> GetQueryStringFactory<T>(DbSet<T> dbSet)
|
static QueryStringFactory<T> GetQueryStringFactory<T>(DbSet<T> dbSet)
|
||||||
where T : class
|
where T : class
|
||||||
{
|
{
|
||||||
@ -82,13 +82,6 @@ namespace AsbCloudDb
|
|||||||
|
|
||||||
public string MakeInsertOrUpdateSql(IEnumerable<T> items)
|
public string MakeInsertOrUpdateSql(IEnumerable<T> 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);
|
var builder = new StringBuilder(insertHeader, 7);
|
||||||
BuildRows(builder, items);
|
BuildRows(builder, items);
|
||||||
if (string.IsNullOrEmpty(pk))
|
if (string.IsNullOrEmpty(pk))
|
||||||
|
@ -5,6 +5,7 @@ using AsbCloudDb.Model;
|
|||||||
using AsbCloudInfrastructure.Services;
|
using AsbCloudInfrastructure.Services;
|
||||||
using AsbCloudInfrastructure.Services.Analysis;
|
using AsbCloudInfrastructure.Services.Analysis;
|
||||||
using AsbCloudInfrastructure.Services.Cache;
|
using AsbCloudInfrastructure.Services.Cache;
|
||||||
|
using AsbCloudInfrastructure.Services.DetectOperations;
|
||||||
using AsbCloudInfrastructure.Services.DrillingProgram;
|
using AsbCloudInfrastructure.Services.DrillingProgram;
|
||||||
using AsbCloudInfrastructure.Services.SAUB;
|
using AsbCloudInfrastructure.Services.SAUB;
|
||||||
using AsbCloudInfrastructure.Services.WellOperationService;
|
using AsbCloudInfrastructure.Services.WellOperationService;
|
||||||
@ -53,7 +54,7 @@ namespace AsbCloudInfrastructure
|
|||||||
services.AddScoped<IFileShareService, GoogleDriveService>();
|
services.AddScoped<IFileShareService, GoogleDriveService>();
|
||||||
services.AddScoped<IEmailService, EmailService>();
|
services.AddScoped<IEmailService, EmailService>();
|
||||||
|
|
||||||
services.AddHostedService<TelemetryAnalyticsBackgroundService>();// replace by BackgroundWorkerService
|
services.AddHostedService<OperationDetectionBackgroundService>();
|
||||||
|
|
||||||
services.AddSingleton(new WitsInfoService());
|
services.AddSingleton(new WitsInfoService());
|
||||||
services.AddSingleton(new CacheDb());
|
services.AddSingleton(new CacheDb());
|
||||||
@ -85,6 +86,7 @@ namespace AsbCloudInfrastructure
|
|||||||
services.AddTransient<IWellOperationImportService, WellOperationImportService>();
|
services.AddTransient<IWellOperationImportService, WellOperationImportService>();
|
||||||
services.AddTransient<IWellOperationService, WellOperationService>();
|
services.AddTransient<IWellOperationService, WellOperationService>();
|
||||||
services.AddTransient<IScheduleReportService, ScheduleReportService>();
|
services.AddTransient<IScheduleReportService, ScheduleReportService>();
|
||||||
|
services.AddTransient<IDetectedOperationService, DetectedOperationService>();
|
||||||
|
|
||||||
// admin crud services:
|
// admin crud services:
|
||||||
services.AddTransient<ICrudService<TelemetryDto>, CrudServiceBase<TelemetryDto, Telemetry>>(); // может быть включен в сервис TelemetryService
|
services.AddTransient<ICrudService<TelemetryDto>, CrudServiceBase<TelemetryDto, Telemetry>>(); // может быть включен в сервис TelemetryService
|
||||||
|
@ -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<IEnumerable<DetectedOperationDto>> 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<int> 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<DetectedOperation> BuildQuery(WellDto well, DetectedOperationRequest request)
|
||||||
|
{
|
||||||
|
if (well?.IdTelemetry is null || well.Timezone is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var query = db.Set<DetectedOperation>()
|
||||||
|
.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<DetectedOperationDto>();
|
||||||
|
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<IEnumerable<WellOperationCategoryDto>> GetCategoriesAsync(CancellationToken token)
|
||||||
|
{
|
||||||
|
var result = await db.WellOperationCategories
|
||||||
|
.Where(c => c.Id < 1000)
|
||||||
|
.Select(c => c.Adapt<WellOperationCategoryDto>())
|
||||||
|
.ToArrayAsync(token);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -9,28 +9,25 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
|
|||||||
{
|
{
|
||||||
|
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
abstract class DetectorAbstract
|
abstract class DetectorAbstract
|
||||||
{
|
{
|
||||||
/* ## Концепт определения операций
|
|
||||||
* Есть словарь детекторов.
|
|
||||||
* Определить начальную позицию архива.
|
|
||||||
* Пройти детекторами до конца архива.
|
|
||||||
* Каждый детектор на небольшому фрагменту определяет начало операции.
|
|
||||||
* При нахождении начала ищет конец и смещает положение поиска
|
|
||||||
*/
|
|
||||||
public int StepLength { get; set; } = 3;
|
public int StepLength { get; set; } = 3;
|
||||||
public int FragmentLength { get; set; } = 6;
|
public int FragmentLength { get; set; } = 6;
|
||||||
public int IdOperationType { get; }
|
public int IdCategory { get; }
|
||||||
public string OperationName { get; }
|
|
||||||
|
|
||||||
// TODO: assert MaxDurationSeconds and MinDurationSeconds
|
// TODO: assert MaxDurationSeconds and MinDurationSeconds
|
||||||
public double MaxDurationSeconds { get; } = 31 * 24 * 60 * 60;
|
public double MaxDurationSeconds { get; } = 31 * 24 * 60 * 60;
|
||||||
public double MinDurationSeconds { get; } = 3;
|
public double MinDurationSeconds { get; } = 3;
|
||||||
|
|
||||||
public DetectorAbstract(int idType, string operationName)
|
/// <summary>
|
||||||
|
/// Конструктор детектора.
|
||||||
|
/// Словарь с IdCategory в дефолтных данных AsbCloudDbContext для таблицы WellOperationCategory
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="IdCategory">ключ названия/описания операции из таблицы WellOperationCategory</param>
|
||||||
|
public DetectorAbstract(int IdCategory)
|
||||||
{
|
{
|
||||||
IdOperationType = idType;
|
this.IdCategory = IdCategory;
|
||||||
OperationName = operationName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual DetectedOperation? DetectOrDefault(DetectableTelemetry[] telemetry, ref int position)
|
public virtual DetectedOperation? DetectOrDefault(DetectableTelemetry[] telemetry, ref int position)
|
||||||
@ -44,7 +41,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
|
|||||||
{
|
{
|
||||||
var result = new DetectedOperation
|
var result = new DetectedOperation
|
||||||
{
|
{
|
||||||
IdCategory = IdOperationType,
|
IdCategory = IdCategory,
|
||||||
IdUsersAtStart = telemetry[position].IdUser ?? -1,
|
IdUsersAtStart = telemetry[position].IdUser ?? -1,
|
||||||
DateStart = telemetry[position].DateTime,
|
DateStart = telemetry[position].DateTime,
|
||||||
DateEnd = telemetry[skip].DateTime,
|
DateEnd = telemetry[skip].DateTime,
|
||||||
|
@ -5,7 +5,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
class DetectorDrillingRotor : DetectorAbstract
|
class DetectorDrillingRotor : DetectorAbstract
|
||||||
{
|
{
|
||||||
public DetectorDrillingRotor() : base(10_002, "Бурение в роторе")
|
public DetectorDrillingRotor() : base(1)
|
||||||
{
|
{
|
||||||
FragmentLength = 15;
|
FragmentLength = 15;
|
||||||
StepLength = 10;
|
StepLength = 10;
|
||||||
@ -13,6 +13,12 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
|
|||||||
|
|
||||||
protected override bool DetectStart(DetectableTelemetry[] telemetry, int position)
|
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)];
|
var fragment = telemetry[position .. (position + FragmentLength)];
|
||||||
|
|
||||||
const double minRop = 5; //м/час
|
const double minRop = 5; //м/час
|
||||||
|
@ -5,13 +5,19 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
class DetectorDrillingSlide : DetectorAbstract
|
class DetectorDrillingSlide : DetectorAbstract
|
||||||
{
|
{
|
||||||
public DetectorDrillingSlide() : base(10_003, "Бурение в слайде")
|
public DetectorDrillingSlide() : base(3)
|
||||||
{
|
{
|
||||||
FragmentLength = 10;
|
FragmentLength = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool DetectStart(DetectableTelemetry[] telemetry, int position)
|
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)];
|
var fragment = telemetry[position .. (position + FragmentLength)];
|
||||||
|
|
||||||
const double minRop = 5; //м/час
|
const double minRop = 5; //м/час
|
||||||
|
@ -3,10 +3,7 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
class DetectorSlipsTime : DetectorAbstract
|
class DetectorSlipsTime : DetectorAbstract
|
||||||
{
|
{
|
||||||
public DetectorSlipsTime() : base(10_001, "Удержание в клиньях")
|
public DetectorSlipsTime() :base(14) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public double HookWeightSP { get; set; } = 20;
|
public double HookWeightSP { get; set; } = 20;
|
||||||
public double PressureSP { get; set; } = 15;
|
public double PressureSP { get; set; } = 15;
|
||||||
public double PosisionSP { get; set; } = 8;
|
public double PosisionSP { get; set; } = 8;
|
||||||
|
@ -11,7 +11,7 @@ using System.Diagnostics;
|
|||||||
|
|
||||||
namespace AsbCloudInfrastructure.Services.DetectOperations
|
namespace AsbCloudInfrastructure.Services.DetectOperations
|
||||||
{
|
{
|
||||||
public class OperationDetectorBackgroundService : BackgroundService
|
public class OperationDetectionBackgroundService : BackgroundService
|
||||||
{
|
{
|
||||||
private readonly IEnumerable<DetectorAbstract> detectors = new List<DetectorAbstract>
|
private readonly IEnumerable<DetectorAbstract> detectors = new List<DetectorAbstract>
|
||||||
{
|
{
|
||||||
@ -25,7 +25,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
|
|||||||
private readonly string connectionString;
|
private readonly string connectionString;
|
||||||
private readonly TimeSpan period = TimeSpan.FromHours(1);
|
private readonly TimeSpan period = TimeSpan.FromHours(1);
|
||||||
|
|
||||||
public OperationDetectorBackgroundService(IConfiguration configuration)
|
public OperationDetectionBackgroundService(IConfiguration configuration)
|
||||||
{
|
{
|
||||||
minStepLength = detectors.Min(d => d.StepLength);
|
minStepLength = detectors.Min(d => d.StepLength);
|
||||||
minStepLength = minStepLength > 0 ? minStepLength : 3;
|
minStepLength = minStepLength > 0 ? minStepLength : 3;
|
||||||
@ -75,40 +75,38 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
|
|||||||
|
|
||||||
private async Task<int> DetectedAllTelemetriesAsync(IAsbCloudDbContext db, CancellationToken token)
|
private async Task<int> DetectedAllTelemetriesAsync(IAsbCloudDbContext db, CancellationToken token)
|
||||||
{
|
{
|
||||||
var lastDetectedDatesQuery = db.DetectedOperations
|
var lastDetectedDates = await db.DetectedOperations
|
||||||
.GroupBy(o => o.IdTelemetry)
|
.GroupBy(o => o.IdTelemetry)
|
||||||
.Select(g => new
|
.Select(g => new
|
||||||
{
|
{
|
||||||
IdTelemetry = g.Key,
|
IdTelemetry = g.Key,
|
||||||
LastDate = g.Max(o => o.DateEnd)
|
LastDate = g.Max(o => o.DateEnd)
|
||||||
});
|
})
|
||||||
|
.ToListAsync(token);
|
||||||
|
|
||||||
var lastDetectedDates = await db.Telemetries
|
var telemetryIds = await db.Telemetries
|
||||||
.Join(lastDetectedDatesQuery,
|
.Where(t => t.Info != null && t.TimeZone != null)
|
||||||
t => t.Id,
|
.Select(t => t.Id)
|
||||||
|
.ToListAsync(token);
|
||||||
|
|
||||||
|
var JounedlastDetectedDates = telemetryIds
|
||||||
|
.GroupJoin(lastDetectedDates,
|
||||||
|
t => t,
|
||||||
o => o.IdTelemetry,
|
o => o.IdTelemetry,
|
||||||
(outer, inner) => new
|
(outer, inner) => new
|
||||||
{
|
{
|
||||||
IdTelemetry = outer.Id,
|
IdTelemetry = outer,
|
||||||
inner.LastDate
|
LastDate = inner.SingleOrDefault()?.LastDate ,
|
||||||
})
|
});
|
||||||
.ToListAsync(token);
|
|
||||||
|
|
||||||
bool isAnyAdded = false;
|
foreach (var item in JounedlastDetectedDates)
|
||||||
|
|
||||||
foreach (var item in lastDetectedDates)
|
|
||||||
{
|
{
|
||||||
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())
|
if (newOperations.Any())
|
||||||
{
|
|
||||||
db.DetectedOperations.AddRange(newOperations);
|
db.DetectedOperations.AddRange(newOperations);
|
||||||
isAnyAdded = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return isAnyAdded
|
return await db.SaveChangesAsync(token);
|
||||||
? await db.SaveChangesAsync(token)
|
|
||||||
: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IEnumerable<DetectedOperation>> DetectOperationsAsync(int idTelemetry, DateTimeOffset begin, IAsbCloudDbContext db, CancellationToken token)
|
private async Task<IEnumerable<DetectedOperation>> DetectOperationsAsync(int idTelemetry, DateTimeOffset begin, IAsbCloudDbContext db, CancellationToken token)
|
||||||
@ -171,6 +169,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
|
|||||||
{
|
{
|
||||||
isDetected1 = true;
|
isDetected1 = true;
|
||||||
isDetected = true;
|
isDetected = true;
|
||||||
|
detectedOperation.IdTelemetry = idTelemetry;
|
||||||
detectedOperations.Add(detectedOperation);
|
detectedOperations.Add(detectedOperation);
|
||||||
startDate = detectedOperation.DateEnd;
|
startDate = detectedOperation.DateEnd;
|
||||||
break;
|
break;
|
@ -0,0 +1 @@
|
|||||||
|
|
@ -0,0 +1,36 @@
|
|||||||
|
> Из писма Гранова А.П. от 19.04.2022 13:29 "Алгоритм определения наращивания БИ.docx"
|
||||||
|
|
||||||
|
# Алгоритм определения времени в клиньях
|
||||||
|
|
||||||
|
## Описание:
|
||||||
|
Наращивание бурильного инструмента – операция, во время которой после добуривания очередной трубы/ свечи
|
||||||
|
циркуляция выключается, инструмент разгружается в клиньях (остается только вес крюкоблока и ВСП),
|
||||||
|
происходит развинчивание трубы от верхнего силового привода, берется очередная труба/ свеча,
|
||||||
|
свинчивается с инструментом в клиньях, свинчивается с верхним силовым приводом, происходит подъем инструмента,
|
||||||
|
вес на крюке увеличивается. Далее включается циркуляция и происходит механическое бурение.
|
||||||
|
|
||||||
|
Наращиванию предшествует механическое бурение (наличие давления, увеличение глубины забоя) и
|
||||||
|
после наращивания механическое бурение возобновляется (наличие давления, Увеличение глубины забоя).
|
||||||
|
|
||||||
|
> Это не учитывать в методе, так как предыдущая и последующая операция могут быть определены не корректно.
|
||||||
|
|
||||||
|
Наращивается определяется как время между:
|
||||||
|
- разгрузкой инструмента на клинья (остается только вес крюкоблока и ВСП).
|
||||||
|
При этом давление менее 15 атм. В случае давления более 15 атм считать началом операции как
|
||||||
|
снижение давления менее 15 атм и началом движения талевого блока вверх.
|
||||||
|
- снятие инструмента с клиньев (вес увеличивается более, чем на 1т).
|
||||||
|
- При этом движение талевого блока происходит вверх.
|
||||||
|
|
||||||
|
## Метод определения:
|
||||||
|
|
||||||
|
> Исправлено на совещании от 19.04.2022 16:50
|
||||||
|
|
||||||
|
```
|
||||||
|
Признак начала операции =
|
||||||
|
(параметр «вес на крюке» < 22 тонн) И
|
||||||
|
(давление < 15 атм) И
|
||||||
|
(положение талевого блока < 8)
|
||||||
|
Признак окончания операции =
|
||||||
|
(вес на крюке > 22 ) И
|
||||||
|
(давление > 15 атм)
|
||||||
|
```
|
@ -94,7 +94,6 @@ namespace AsbCloudInfrastructure.Services.WellOperationService
|
|||||||
|
|
||||||
if (end != default)
|
if (end != default)
|
||||||
{
|
{
|
||||||
|
|
||||||
var endOffset = end.ToUtcDateTimeOffset(timezone.Hours);
|
var endOffset = end.ToUtcDateTimeOffset(timezone.Hours);
|
||||||
query = query.Where(e => e.DateStart <= endOffset);
|
query = query.Where(e => e.DateStart <= endOffset);
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Операции определенные по телеметрии САУБ
|
||||||
|
/// </summary>
|
||||||
|
[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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// получить справочник операций. Отличается от операций заводимых вручную
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet("categories")]
|
||||||
|
[ProducesResponseType(typeof(IEnumerable<WellOperationCategoryDto>), (int)System.Net.HttpStatusCode.OK)]
|
||||||
|
public async Task<IActionResult> GetCategoriesAsync(CancellationToken token = default)
|
||||||
|
{
|
||||||
|
var result = await detectedOperationService.GetCategoriesAsync(token);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получить фильтрованный список операций по телеметрии САУБ
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idWell"></param>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet]
|
||||||
|
[ProducesResponseType(typeof(IEnumerable<DetectedOperationDto>), (int)System.Net.HttpStatusCode.OK)]
|
||||||
|
public async Task<IActionResult> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Удалить операции.
|
||||||
|
/// Удаленные операции будут определены повторно сервисом автоматизированного определения операций.
|
||||||
|
/// Может потребоваться при изменении алгоритмов определения
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idWell"></param>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpDelete]
|
||||||
|
[Permission]
|
||||||
|
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
|
||||||
|
public async Task<IActionResult> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using AsbCloudDb;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace ConsoleApp1
|
namespace ConsoleApp1
|
||||||
{
|
{
|
||||||
@ -9,14 +10,13 @@ namespace ConsoleApp1
|
|||||||
static void Main(/*string[] args*/)
|
static void Main(/*string[] args*/)
|
||||||
{
|
{
|
||||||
// use ServiceFactory to make services
|
// use ServiceFactory to make services
|
||||||
|
|
||||||
var db = ServiceFactory.MakeContext();
|
var db = ServiceFactory.MakeContext();
|
||||||
var service = new AsbCloudInfrastructure.Services.DetectOperations.DetectorService();
|
var q = db.DetectedOperations
|
||||||
var start = new DateTimeOffset(2021, 1, 30, 23, 00, 00, TimeSpan.FromHours(0));
|
.Include(o => o.OperationCategory)
|
||||||
var operations = service.DetectOperationsAsync(146, start, db, CancellationToken.None).Result;
|
.Where(o => true)
|
||||||
foreach (var operation in operations)
|
.SortBy(new string[] { "DateStart desc", "depthstart" });
|
||||||
Console.WriteLine(operation);
|
var sql = q.ToQueryString();
|
||||||
|
var items = q.ToList();
|
||||||
Console.WriteLine("End of program");
|
Console.WriteLine("End of program");
|
||||||
Console.ReadLine();
|
Console.ReadLine();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user