forked from ddrilling/AsbCloudServer
merge detected operations
This commit is contained in:
commit
0764e9d2a3
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; }
|
||||||
|
}
|
||||||
|
}
|
@ -1,26 +0,0 @@
|
|||||||
namespace AsbCloudApp.Data
|
|
||||||
{
|
|
||||||
public class TelemetryAnalysisDto : IId
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
public int IdTelemetry { get; set; }
|
|
||||||
public int IdOperation { get; set; }
|
|
||||||
public long UnixDate { get; set; }
|
|
||||||
public int DurationSec { get; set; }
|
|
||||||
public double? OperationStartDepth { get; set; }
|
|
||||||
public double? OperationEndDepth { get; set; }
|
|
||||||
public bool IsWellDepthIncreasing { get; set; }
|
|
||||||
public bool IsWellDepthDecreasing { get; set; }
|
|
||||||
public bool IsBitPositionIncreasing { get; set; }
|
|
||||||
public bool IsBitPositionDecreasing { get; set; }
|
|
||||||
public bool IsBitPositionLt20 { get; set; }
|
|
||||||
public bool IsBlockPositionIncreasing { get; set; }
|
|
||||||
public bool IsBlockPositionDecreasing { get; set; }
|
|
||||||
public bool IsRotorSpeedLt3 { get; set; }
|
|
||||||
public bool IsRotorSpeedGt3 { get; set; }
|
|
||||||
public bool IsPressureLt20 { get; set; }
|
|
||||||
public bool IsPressureGt20 { get; set; }
|
|
||||||
public bool IsHookWeightNotChanges { get; set; }
|
|
||||||
public bool IsHookWeightLt3 { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
namespace AsbCloudApp.Data
|
|
||||||
{
|
|
||||||
public class TelemetryOperationDetailsDto
|
|
||||||
{
|
|
||||||
public string OperationName { get; set; }
|
|
||||||
public int DurationSec { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace AsbCloudApp.Data
|
|
||||||
{
|
|
||||||
public class TelemetryOperationDto : IId
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
public string Name { get; set; }
|
|
||||||
public DateTime BeginDate { get; set; }
|
|
||||||
public DateTime EndDate { get; set; }
|
|
||||||
public double StartWellDepth { get; set; }
|
|
||||||
public double EndWellDepth { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
namespace AsbCloudApp.Data
|
|
||||||
{
|
|
||||||
public class TelemetryOperationDurationDto
|
|
||||||
{
|
|
||||||
public string OperationName { get; set; }
|
|
||||||
public double Duration { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace AsbCloudApp.Data
|
|
||||||
{
|
|
||||||
public class TelemetryOperationInfoDto
|
|
||||||
{
|
|
||||||
public DateTime IntervalBegin { get; set; }
|
|
||||||
public IList<TelemetryOperationDetailsDto> Operations { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace AsbCloudApp.Data
|
|
||||||
{
|
|
||||||
public class WellDepthToDayDto
|
|
||||||
{
|
|
||||||
public double WellDepth { get; set; }
|
|
||||||
public double BitDepth { get; set; }
|
|
||||||
public DateTime Date { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace AsbCloudApp.Data
|
|
||||||
{
|
|
||||||
public class WellDepthToIntervalDto
|
|
||||||
{
|
|
||||||
public DateTime IntervalStartDate { get; set; }
|
|
||||||
public double IntervalDepthProgress { 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);
|
||||||
|
}
|
||||||
|
}
|
@ -1,30 +0,0 @@
|
|||||||
using AsbCloudApp.Data;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace AsbCloudApp.Services
|
|
||||||
{
|
|
||||||
public interface ITelemetryAnalyticsService
|
|
||||||
{
|
|
||||||
Task<PaginationContainer<TelemetryOperationDto>> GetOperationsByWellAsync(int idWell,
|
|
||||||
IEnumerable<int> categoryids = default, DateTime begin = default,
|
|
||||||
DateTime end = default, int skip = 0, int take = 32,
|
|
||||||
CancellationToken token = default);
|
|
||||||
Task<IEnumerable<WellDepthToDayDto>> GetWellDepthToDayAsync(int idWell,
|
|
||||||
CancellationToken token = default);
|
|
||||||
Task<IEnumerable<WellDepthToIntervalDto>> GetWellDepthToIntervalAsync(int idWell,
|
|
||||||
int intervalHoursTimestamp, int shiftStartSec,
|
|
||||||
CancellationToken token = default);
|
|
||||||
Task<IEnumerable<TelemetryOperationDurationDto>> GetOperationsSummaryAsync(int idWell,
|
|
||||||
DateTime begin = default, DateTime end = default,
|
|
||||||
CancellationToken token = default);
|
|
||||||
Task<IEnumerable<TelemetryOperationInfoDto>> GetOperationsToIntervalAsync(int idWell,
|
|
||||||
int intervalHoursTimestamp, int workBeginTimestamp,
|
|
||||||
CancellationToken token = default);
|
|
||||||
Task AnalyzeAndSaveTelemetriesAsync(CancellationToken token = default);
|
|
||||||
Task<DatesRangeDto> GetOperationsDateRangeAsync(int idWell,
|
|
||||||
CancellationToken token = default);
|
|
||||||
}
|
|
||||||
}
|
|
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))
|
||||||
|
5844
AsbCloudDb/Migrations/20220425110530_Add_DetectedOperations.Designer.cs
generated
Normal file
5844
AsbCloudDb/Migrations/20220425110530_Add_DetectedOperations.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,62 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace AsbCloudDb.Migrations
|
||||||
|
{
|
||||||
|
public partial class Add_DetectedOperations : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "t_detected_operation",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
id_telemetry = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
id_category = table.Column<int>(type: "integer", nullable: false, comment: "Id категории операции"),
|
||||||
|
id_user = table.Column<int>(type: "integer", nullable: false, comment: "Id пользователя по телеметрии на момент начала операции"),
|
||||||
|
date_start = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата начала операции"),
|
||||||
|
date_end = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата начала операции"),
|
||||||
|
depth_start = table.Column<double>(type: "double precision", nullable: false, comment: "Глубина на начало операции, м"),
|
||||||
|
depth_end = table.Column<double>(type: "double precision", nullable: false, comment: "Глубина после завершения операции, м")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_t_detected_operation", x => x.id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_t_detected_operation_t_telemetry_id_telemetry",
|
||||||
|
column: x => x.id_telemetry,
|
||||||
|
principalTable: "t_telemetry",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_t_detected_operation_t_well_operation_category_id_category",
|
||||||
|
column: x => x.id_category,
|
||||||
|
principalTable: "t_well_operation_category",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
},
|
||||||
|
comment: "автоматически определенные операции по телеметрии");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_t_detected_operation_id_category",
|
||||||
|
table: "t_detected_operation",
|
||||||
|
column: "id_category");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_t_detected_operation_id_telemetry",
|
||||||
|
table: "t_detected_operation",
|
||||||
|
column: "id_telemetry");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "t_detected_operation");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5835
AsbCloudDb/Migrations/20220429075025_Remove_old_analytics_stage1.Designer.cs
generated
Normal file
5835
AsbCloudDb/Migrations/20220429075025_Remove_old_analytics_stage1.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,63 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace AsbCloudDb.Migrations
|
||||||
|
{
|
||||||
|
public partial class Remove_old_analytics_stage1 : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "t_analysis_t_operation_id_fk",
|
||||||
|
table: "t_telemetry_analysis");
|
||||||
|
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "t_analysis_t_telemetry_id_fk",
|
||||||
|
table: "t_telemetry_analysis");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_t_telemetry_analysis_t_telemetry_id_telemetry",
|
||||||
|
table: "t_telemetry_analysis",
|
||||||
|
column: "id_telemetry",
|
||||||
|
principalTable: "t_telemetry",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_t_telemetry_analysis_t_well_operation_category_id_operation",
|
||||||
|
table: "t_telemetry_analysis",
|
||||||
|
column: "id_operation",
|
||||||
|
principalTable: "t_well_operation_category",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_t_telemetry_analysis_t_telemetry_id_telemetry",
|
||||||
|
table: "t_telemetry_analysis");
|
||||||
|
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_t_telemetry_analysis_t_well_operation_category_id_operation",
|
||||||
|
table: "t_telemetry_analysis");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "t_analysis_t_operation_id_fk",
|
||||||
|
table: "t_telemetry_analysis",
|
||||||
|
column: "id_operation",
|
||||||
|
principalTable: "t_well_operation_category",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.SetNull);
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "t_analysis_t_telemetry_id_fk",
|
||||||
|
table: "t_telemetry_analysis",
|
||||||
|
column: "id_telemetry",
|
||||||
|
principalTable: "t_telemetry",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5812
AsbCloudDb/Migrations/20220429081059_Remove_old_analytics_stage2.Designer.cs
generated
Normal file
5812
AsbCloudDb/Migrations/20220429081059_Remove_old_analytics_stage2.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,57 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace AsbCloudDb.Migrations
|
||||||
|
{
|
||||||
|
public partial class Remove_old_analytics_stage2 : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_t_telemetry_analysis_t_telemetry_id_telemetry",
|
||||||
|
table: "t_telemetry_analysis");
|
||||||
|
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_t_telemetry_analysis_t_well_operation_category_id_operation",
|
||||||
|
table: "t_telemetry_analysis");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_t_telemetry_analysis_id_operation",
|
||||||
|
table: "t_telemetry_analysis");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_t_telemetry_analysis_id_telemetry",
|
||||||
|
table: "t_telemetry_analysis");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_t_telemetry_analysis_id_operation",
|
||||||
|
table: "t_telemetry_analysis",
|
||||||
|
column: "id_operation");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_t_telemetry_analysis_id_telemetry",
|
||||||
|
table: "t_telemetry_analysis",
|
||||||
|
column: "id_telemetry");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_t_telemetry_analysis_t_telemetry_id_telemetry",
|
||||||
|
table: "t_telemetry_analysis",
|
||||||
|
column: "id_telemetry",
|
||||||
|
principalTable: "t_telemetry",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_t_telemetry_analysis_t_well_operation_category_id_operation",
|
||||||
|
table: "t_telemetry_analysis",
|
||||||
|
column: "id_operation",
|
||||||
|
principalTable: "t_well_operation_category",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5703
AsbCloudDb/Migrations/20220429081205_Remove_old_analytics_stage3.Designer.cs
generated
Normal file
5703
AsbCloudDb/Migrations/20220429081205_Remove_old_analytics_stage3.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,51 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace AsbCloudDb.Migrations
|
||||||
|
{
|
||||||
|
public partial class Remove_old_analytics_stage3 : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "t_telemetry_analysis");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "t_telemetry_analysis",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
duration_sec = table.Column<int>(type: "integer", nullable: false, comment: "Кол-во секунд после предыдущей операции"),
|
||||||
|
id_operation = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
id_telemetry = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
is_bit_position_decreasing = table.Column<bool>(type: "boolean", nullable: false, comment: "Долото поднимается"),
|
||||||
|
is_bit_position_increasing = table.Column<bool>(type: "boolean", nullable: false, comment: "Долото спускается"),
|
||||||
|
is_bit_posision_lt_20 = table.Column<bool>(type: "boolean", nullable: false, comment: "Положение долота меньше 20м"),
|
||||||
|
is_block_posision_decresing = table.Column<bool>(type: "boolean", nullable: false, comment: "Талевый блок поднимается"),
|
||||||
|
is_block_posision_incresing = table.Column<bool>(type: "boolean", nullable: false, comment: "Талевый блок спускается"),
|
||||||
|
is_hook_weight_lt_3 = table.Column<bool>(type: "boolean", nullable: false, comment: "Вес на крюке менее 3т"),
|
||||||
|
is_hook_weight_not_changes = table.Column<bool>(type: "boolean", nullable: false, comment: "Вес на крюке не меняется"),
|
||||||
|
is_pressure_gt_20 = table.Column<bool>(type: "boolean", nullable: false, comment: "Давление более 20"),
|
||||||
|
is_pressure_lt_20 = table.Column<bool>(type: "boolean", nullable: false, comment: "Давление менее 20"),
|
||||||
|
is_rotor_speed_gt_3 = table.Column<bool>(type: "boolean", nullable: false, comment: "Обороты ротора выше 3"),
|
||||||
|
is_rotor_speed_lt_3 = table.Column<bool>(type: "boolean", nullable: false, comment: "Обороты ротора ниже 3"),
|
||||||
|
is_well_depth_decreasing = table.Column<bool>(type: "boolean", nullable: false, comment: "Глубина забоя не увеличивается"),
|
||||||
|
is_well_depth_increasing = table.Column<bool>(type: "boolean", nullable: false, comment: "Глубина забоя увеличивается"),
|
||||||
|
operation_end_depth = table.Column<double>(type: "double precision", nullable: true, comment: "Глубина, на которой закончилась операция"),
|
||||||
|
operation_start_depth = table.Column<double>(type: "double precision", nullable: true, comment: "Глубина, на которой началась операция"),
|
||||||
|
unix_date = table.Column<long>(type: "bigint", nullable: false, comment: "Unix timestamp для Linq запросов с вычислением дат")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_t_telemetry_analysis", x => x.id);
|
||||||
|
},
|
||||||
|
comment: "События на скважине");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -196,6 +196,60 @@ namespace AsbCloudDb.Migrations
|
|||||||
b.HasComment("Месторождение");
|
b.HasComment("Месторождение");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("AsbCloudDb.Model.DetectedOperation", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("DateEnd")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("date_end")
|
||||||
|
.HasComment("Дата начала операции");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("DateStart")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("date_start")
|
||||||
|
.HasComment("Дата начала операции");
|
||||||
|
|
||||||
|
b.Property<double>("DepthEnd")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("depth_end")
|
||||||
|
.HasComment("Глубина после завершения операции, м");
|
||||||
|
|
||||||
|
b.Property<double>("DepthStart")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("depth_start")
|
||||||
|
.HasComment("Глубина на начало операции, м");
|
||||||
|
|
||||||
|
b.Property<int>("IdCategory")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id_category")
|
||||||
|
.HasComment("Id категории операции");
|
||||||
|
|
||||||
|
b.Property<int>("IdTelemetry")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id_telemetry");
|
||||||
|
|
||||||
|
b.Property<int>("IdUsersAtStart")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id_user")
|
||||||
|
.HasComment("Id пользователя по телеметрии на момент начала операции");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("IdCategory");
|
||||||
|
|
||||||
|
b.HasIndex("IdTelemetry");
|
||||||
|
|
||||||
|
b.ToTable("t_detected_operation");
|
||||||
|
|
||||||
|
b.HasComment("автоматически определенные операции по телеметрии");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("AsbCloudDb.Model.DrillFlowChart", b =>
|
modelBuilder.Entity("AsbCloudDb.Model.DrillFlowChart", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@ -2468,119 +2522,6 @@ namespace AsbCloudDb.Migrations
|
|||||||
b.HasComment("таблица привязки телеметрии от комплектов к конкретной скважине.");
|
b.HasComment("таблица привязки телеметрии от комплектов к конкретной скважине.");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("AsbCloudDb.Model.TelemetryAnalysis", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<int>("DurationSec")
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasColumnName("duration_sec")
|
|
||||||
.HasComment("Кол-во секунд после предыдущей операции");
|
|
||||||
|
|
||||||
b.Property<int>("IdOperation")
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasColumnName("id_operation");
|
|
||||||
|
|
||||||
b.Property<int>("IdTelemetry")
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasColumnName("id_telemetry");
|
|
||||||
|
|
||||||
b.Property<bool>("IsBitPositionDecreasing")
|
|
||||||
.HasColumnType("boolean")
|
|
||||||
.HasColumnName("is_bit_position_decreasing")
|
|
||||||
.HasComment("Долото поднимается");
|
|
||||||
|
|
||||||
b.Property<bool>("IsBitPositionIncreasing")
|
|
||||||
.HasColumnType("boolean")
|
|
||||||
.HasColumnName("is_bit_position_increasing")
|
|
||||||
.HasComment("Долото спускается");
|
|
||||||
|
|
||||||
b.Property<bool>("IsBitPositionLt20")
|
|
||||||
.HasColumnType("boolean")
|
|
||||||
.HasColumnName("is_bit_posision_lt_20")
|
|
||||||
.HasComment("Положение долота меньше 20м");
|
|
||||||
|
|
||||||
b.Property<bool>("IsBlockPositionDecreasing")
|
|
||||||
.HasColumnType("boolean")
|
|
||||||
.HasColumnName("is_block_posision_decresing")
|
|
||||||
.HasComment("Талевый блок поднимается");
|
|
||||||
|
|
||||||
b.Property<bool>("IsBlockPositionIncreasing")
|
|
||||||
.HasColumnType("boolean")
|
|
||||||
.HasColumnName("is_block_posision_incresing")
|
|
||||||
.HasComment("Талевый блок спускается");
|
|
||||||
|
|
||||||
b.Property<bool>("IsHookWeightLt3")
|
|
||||||
.HasColumnType("boolean")
|
|
||||||
.HasColumnName("is_hook_weight_lt_3")
|
|
||||||
.HasComment("Вес на крюке менее 3т");
|
|
||||||
|
|
||||||
b.Property<bool>("IsHookWeightNotChanges")
|
|
||||||
.HasColumnType("boolean")
|
|
||||||
.HasColumnName("is_hook_weight_not_changes")
|
|
||||||
.HasComment("Вес на крюке не меняется");
|
|
||||||
|
|
||||||
b.Property<bool>("IsPressureGt20")
|
|
||||||
.HasColumnType("boolean")
|
|
||||||
.HasColumnName("is_pressure_gt_20")
|
|
||||||
.HasComment("Давление более 20");
|
|
||||||
|
|
||||||
b.Property<bool>("IsPressureLt20")
|
|
||||||
.HasColumnType("boolean")
|
|
||||||
.HasColumnName("is_pressure_lt_20")
|
|
||||||
.HasComment("Давление менее 20");
|
|
||||||
|
|
||||||
b.Property<bool>("IsRotorSpeedGt5")
|
|
||||||
.HasColumnType("boolean")
|
|
||||||
.HasColumnName("is_rotor_speed_gt_3")
|
|
||||||
.HasComment("Обороты ротора выше 3");
|
|
||||||
|
|
||||||
b.Property<bool>("IsRotorSpeedLt5")
|
|
||||||
.HasColumnType("boolean")
|
|
||||||
.HasColumnName("is_rotor_speed_lt_3")
|
|
||||||
.HasComment("Обороты ротора ниже 3");
|
|
||||||
|
|
||||||
b.Property<bool>("IsWellDepthDecreasing")
|
|
||||||
.HasColumnType("boolean")
|
|
||||||
.HasColumnName("is_well_depth_decreasing")
|
|
||||||
.HasComment("Глубина забоя не увеличивается");
|
|
||||||
|
|
||||||
b.Property<bool>("IsWellDepthIncreasing")
|
|
||||||
.HasColumnType("boolean")
|
|
||||||
.HasColumnName("is_well_depth_increasing")
|
|
||||||
.HasComment("Глубина забоя увеличивается");
|
|
||||||
|
|
||||||
b.Property<double?>("OperationEndDepth")
|
|
||||||
.HasColumnType("double precision")
|
|
||||||
.HasColumnName("operation_end_depth")
|
|
||||||
.HasComment("Глубина, на которой закончилась операция");
|
|
||||||
|
|
||||||
b.Property<double?>("OperationStartDepth")
|
|
||||||
.HasColumnType("double precision")
|
|
||||||
.HasColumnName("operation_start_depth")
|
|
||||||
.HasComment("Глубина, на которой началась операция");
|
|
||||||
|
|
||||||
b.Property<long>("UnixDate")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("unix_date")
|
|
||||||
.HasComment("Unix timestamp для Linq запросов с вычислением дат");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("IdOperation");
|
|
||||||
|
|
||||||
b.HasIndex("IdTelemetry");
|
|
||||||
|
|
||||||
b.ToTable("t_telemetry_analysis");
|
|
||||||
|
|
||||||
b.HasComment("События на скважине");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AsbCloudDb.Model.TelemetryDataSaub", b =>
|
modelBuilder.Entity("AsbCloudDb.Model.TelemetryDataSaub", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("IdTelemetry")
|
b.Property<int>("IdTelemetry")
|
||||||
@ -5428,27 +5369,6 @@ namespace AsbCloudDb.Migrations
|
|||||||
b.Navigation("Well");
|
b.Navigation("Well");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("AsbCloudDb.Model.TelemetryAnalysis", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("AsbCloudDb.Model.WellOperationCategory", "Operation")
|
|
||||||
.WithMany("Analysis")
|
|
||||||
.HasForeignKey("IdOperation")
|
|
||||||
.OnDelete(DeleteBehavior.SetNull)
|
|
||||||
.IsRequired()
|
|
||||||
.HasConstraintName("t_analysis_t_operation_id_fk");
|
|
||||||
|
|
||||||
b.HasOne("AsbCloudDb.Model.Telemetry", "Telemetry")
|
|
||||||
.WithMany("Analysis")
|
|
||||||
.HasForeignKey("IdTelemetry")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired()
|
|
||||||
.HasConstraintName("t_analysis_t_telemetry_id_fk");
|
|
||||||
|
|
||||||
b.Navigation("Operation");
|
|
||||||
|
|
||||||
b.Navigation("Telemetry");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AsbCloudDb.Model.TelemetryDataSaub", b =>
|
modelBuilder.Entity("AsbCloudDb.Model.TelemetryDataSaub", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("AsbCloudDb.Model.Telemetry", "Telemetry")
|
b.HasOne("AsbCloudDb.Model.Telemetry", "Telemetry")
|
||||||
@ -5735,8 +5655,6 @@ namespace AsbCloudDb.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("AsbCloudDb.Model.Telemetry", b =>
|
modelBuilder.Entity("AsbCloudDb.Model.Telemetry", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Analysis");
|
|
||||||
|
|
||||||
b.Navigation("DataSaub");
|
b.Navigation("DataSaub");
|
||||||
|
|
||||||
b.Navigation("DataSpin");
|
b.Navigation("DataSpin");
|
||||||
@ -5781,11 +5699,6 @@ namespace AsbCloudDb.Migrations
|
|||||||
b.Navigation("WellOperations");
|
b.Navigation("WellOperations");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("AsbCloudDb.Model.WellOperationCategory", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Analysis");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AsbCloudDb.Model.WellSectionType", b =>
|
modelBuilder.Entity("AsbCloudDb.Model.WellSectionType", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("DrillParamsCollection");
|
b.Navigation("DrillParamsCollection");
|
||||||
|
@ -12,6 +12,7 @@ namespace AsbCloudDb.Model
|
|||||||
public virtual DbSet<Company> Companies { get; set; }
|
public virtual DbSet<Company> Companies { get; set; }
|
||||||
public virtual DbSet<CompanyType> CompaniesTypes { get; set; }
|
public virtual DbSet<CompanyType> CompaniesTypes { get; set; }
|
||||||
public virtual DbSet<Deposit> Deposits { get; set; }
|
public virtual DbSet<Deposit> Deposits { get; set; }
|
||||||
|
public virtual DbSet<DetectedOperation> DetectedOperations { get; set; }
|
||||||
public virtual DbSet<DrillingProgramPart> DrillingProgramParts { get; set; }
|
public virtual DbSet<DrillingProgramPart> DrillingProgramParts { get; set; }
|
||||||
public virtual DbSet<FileCategory> FileCategories { get; set; }
|
public virtual DbSet<FileCategory> FileCategories { get; set; }
|
||||||
public virtual DbSet<FileInfo> Files { get; set; }
|
public virtual DbSet<FileInfo> Files { get; set; }
|
||||||
@ -23,7 +24,6 @@ namespace AsbCloudDb.Model
|
|||||||
public virtual DbSet<Telemetry> Telemetries { get; set; }
|
public virtual DbSet<Telemetry> Telemetries { get; set; }
|
||||||
public virtual DbSet<TelemetryDataSaub> TelemetryDataSaub { get; set; }
|
public virtual DbSet<TelemetryDataSaub> TelemetryDataSaub { get; set; }
|
||||||
public virtual DbSet<TelemetryDataSpin> TelemetryDataSpin { get; set; }
|
public virtual DbSet<TelemetryDataSpin> TelemetryDataSpin { get; set; }
|
||||||
public virtual DbSet<TelemetryAnalysis> TelemetryAnalysis { get; set; }
|
|
||||||
public virtual DbSet<TelemetryEvent> TelemetryEvents { get; set; }
|
public virtual DbSet<TelemetryEvent> TelemetryEvents { get; set; }
|
||||||
public virtual DbSet<TelemetryMessage> TelemetryMessages { get; set; }
|
public virtual DbSet<TelemetryMessage> TelemetryMessages { get; set; }
|
||||||
public virtual DbSet<TelemetryUser> TelemetryUsers { get; set; }
|
public virtual DbSet<TelemetryUser> TelemetryUsers { get; set; }
|
||||||
@ -164,21 +164,6 @@ namespace AsbCloudDb.Model
|
|||||||
.HasConstraintName("t_telemetry_user_t_telemetry_id_fk");
|
.HasConstraintName("t_telemetry_user_t_telemetry_id_fk");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity<TelemetryAnalysis>(entity =>
|
|
||||||
{
|
|
||||||
entity.HasOne(d => d.Telemetry)
|
|
||||||
.WithMany(p => p.Analysis)
|
|
||||||
.HasForeignKey(d => d.IdTelemetry)
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.HasConstraintName("t_analysis_t_telemetry_id_fk");
|
|
||||||
|
|
||||||
entity.HasOne(d => d.Operation)
|
|
||||||
.WithMany(p => p.Analysis)
|
|
||||||
.HasForeignKey(d => d.IdOperation)
|
|
||||||
.OnDelete(DeleteBehavior.SetNull)
|
|
||||||
.HasConstraintName("t_analysis_t_operation_id_fk");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity<TelemetryEvent>(entity =>
|
modelBuilder.Entity<TelemetryEvent>(entity =>
|
||||||
{
|
{
|
||||||
entity.HasKey(nameof(TelemetryEvent.IdTelemetry), nameof(TelemetryEvent.IdEvent));
|
entity.HasKey(nameof(TelemetryEvent.IdTelemetry), nameof(TelemetryEvent.IdEvent));
|
||||||
|
52
AsbCloudDb/Model/DetectedOperation.cs
Normal file
52
AsbCloudDb/Model/DetectedOperation.cs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace AsbCloudDb.Model
|
||||||
|
{
|
||||||
|
[Table("t_detected_operation"), Comment("автоматически определенные операции по телеметрии")]
|
||||||
|
public class DetectedOperation
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
[Column("id")]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[Column("id_telemetry")]
|
||||||
|
public int IdTelemetry { get; set; }
|
||||||
|
|
||||||
|
[Column("id_category"), Comment("Id категории операции")]
|
||||||
|
public int IdCategory { get; set; }
|
||||||
|
|
||||||
|
[Column("id_user"), Comment("Id пользователя по телеметрии на момент начала операции")]
|
||||||
|
public int IdUsersAtStart { get; set; }
|
||||||
|
|
||||||
|
[Column("date_start", TypeName = "timestamp with time zone"), Comment("Дата начала операции")]
|
||||||
|
public DateTimeOffset DateStart { get; set; }
|
||||||
|
|
||||||
|
[Column("date_end", TypeName = "timestamp with time zone"), Comment("Дата начала операции")]
|
||||||
|
public DateTimeOffset DateEnd { get; set; }
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public double DurationMinutes => (DateEnd - DateStart).TotalMinutes;
|
||||||
|
|
||||||
|
[Column("depth_start"), Comment("Глубина на начало операции, м")]
|
||||||
|
public double DepthStart { get; set; }
|
||||||
|
|
||||||
|
[Column("depth_end"), Comment("Глубина после завершения операции, м")]
|
||||||
|
public double DepthEnd { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
[ForeignKey(nameof(IdTelemetry))]
|
||||||
|
public virtual Telemetry Telemetry { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
[ForeignKey(nameof(IdCategory))]
|
||||||
|
public virtual WellOperationCategory OperationCategory { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
=> $"{IdCategory}\t{DateStart:G}\t{DateEnd:G}\t{DurationMinutes:#0.#}\t{DepthStart:#0.#}\t{DepthEnd:#0.#}";
|
||||||
|
//=> $"{{\"type\": {IdType},\t\"begin\":\"{Begin:G}\",\t\"end\":\"{End:G}\",\t\"depthBegin\":\"{BeginWellDepth:#0.#}\",\t\"depthEnd\":\"{EndWellDepth:#0.#}\"}}";
|
||||||
|
}
|
||||||
|
}
|
@ -27,7 +27,7 @@ namespace AsbCloudDb.Model
|
|||||||
DbSet<RelationUserUserRole> RelationUserUserRoles { get; set; }
|
DbSet<RelationUserUserRole> RelationUserUserRoles { get; set; }
|
||||||
DbSet<ReportProperty> ReportProperties { get; set; }
|
DbSet<ReportProperty> ReportProperties { get; set; }
|
||||||
DbSet<Telemetry> Telemetries { get; set; }
|
DbSet<Telemetry> Telemetries { get; set; }
|
||||||
DbSet<TelemetryAnalysis> TelemetryAnalysis { get; set; }
|
DbSet<DetectedOperation> DetectedOperations { get; set; }
|
||||||
DbSet<TelemetryDataSaub> TelemetryDataSaub { get; set; }
|
DbSet<TelemetryDataSaub> TelemetryDataSaub { get; set; }
|
||||||
DbSet<TelemetryDataSpin> TelemetryDataSpin { get; set; }
|
DbSet<TelemetryDataSpin> TelemetryDataSpin { get; set; }
|
||||||
DbSet<TelemetryEvent> TelemetryEvents { get; set; }
|
DbSet<TelemetryEvent> TelemetryEvents { get; set; }
|
||||||
|
@ -48,7 +48,5 @@ namespace AsbCloudDb.Model
|
|||||||
[InverseProperty(nameof(TelemetryEvent.Telemetry))]
|
[InverseProperty(nameof(TelemetryEvent.Telemetry))]
|
||||||
public virtual ICollection<TelemetryEvent> Events { get; set; }
|
public virtual ICollection<TelemetryEvent> Events { get; set; }
|
||||||
|
|
||||||
[InverseProperty(nameof(TelemetryAnalysis.Telemetry))]
|
|
||||||
public virtual ICollection<TelemetryAnalysis> Analysis { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,83 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace AsbCloudDb.Model
|
|
||||||
{
|
|
||||||
|
|
||||||
[Table("t_telemetry_analysis"), Comment("События на скважине")]
|
|
||||||
public class TelemetryAnalysis : IId
|
|
||||||
{
|
|
||||||
[Key]
|
|
||||||
[Column("id")]
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
[Column("id_telemetry")]
|
|
||||||
public int IdTelemetry { get; set; }
|
|
||||||
|
|
||||||
[Column("id_operation")]
|
|
||||||
public int IdOperation { get; set; }
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
[ForeignKey(nameof(IdTelemetry))]
|
|
||||||
[InverseProperty(nameof(Model.Telemetry.Analysis))]
|
|
||||||
public virtual Telemetry Telemetry { get; set; }
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
[ForeignKey(nameof(IdOperation))]
|
|
||||||
[InverseProperty(nameof(Model.WellOperationCategory.Analysis))]
|
|
||||||
public virtual WellOperationCategory Operation { get; set; }
|
|
||||||
|
|
||||||
[Column("unix_date", TypeName = "bigint"), Comment("Unix timestamp для Linq запросов с вычислением дат")]
|
|
||||||
public long UnixDate { get; set; }
|
|
||||||
|
|
||||||
[Column("duration_sec"), Comment("Кол-во секунд после предыдущей операции")]
|
|
||||||
public int DurationSec { get; set; }
|
|
||||||
|
|
||||||
[Column("operation_start_depth"), Comment("Глубина, на которой началась операция")]
|
|
||||||
public double? OperationStartDepth { get; set; }
|
|
||||||
|
|
||||||
[Column("operation_end_depth"), Comment("Глубина, на которой закончилась операция")]
|
|
||||||
public double? OperationEndDepth { get; set; }
|
|
||||||
|
|
||||||
[Column("is_well_depth_increasing"), Comment("Глубина забоя увеличивается")]
|
|
||||||
public bool IsWellDepthIncreasing { get; set; }
|
|
||||||
|
|
||||||
[Column("is_well_depth_decreasing"), Comment("Глубина забоя не увеличивается")]
|
|
||||||
public bool IsWellDepthDecreasing { get; set; }
|
|
||||||
|
|
||||||
[Column("is_bit_position_increasing"), Comment("Долото спускается")]
|
|
||||||
public bool IsBitPositionIncreasing { get; set; }
|
|
||||||
|
|
||||||
[Column("is_bit_position_decreasing"), Comment("Долото поднимается")]
|
|
||||||
public bool IsBitPositionDecreasing { get; set; }
|
|
||||||
|
|
||||||
[Column("is_bit_posision_lt_20"), Comment("Положение долота меньше 20м")]
|
|
||||||
public bool IsBitPositionLt20 { get; set; }
|
|
||||||
|
|
||||||
[Column("is_block_posision_incresing"), Comment("Талевый блок спускается")]
|
|
||||||
public bool IsBlockPositionIncreasing { get; set; }
|
|
||||||
|
|
||||||
[Column("is_block_posision_decresing"), Comment("Талевый блок поднимается")]
|
|
||||||
public bool IsBlockPositionDecreasing { get; set; }
|
|
||||||
|
|
||||||
[Column("is_rotor_speed_lt_3"), Comment("Обороты ротора ниже 3")]
|
|
||||||
public bool IsRotorSpeedLt5 { get; set; }
|
|
||||||
|
|
||||||
[Column("is_rotor_speed_gt_3"), Comment("Обороты ротора выше 3")]
|
|
||||||
public bool IsRotorSpeedGt5 { get; set; }
|
|
||||||
|
|
||||||
[Column("is_pressure_lt_20"), Comment("Давление менее 20")]
|
|
||||||
public bool IsPressureLt20 { get; set; }
|
|
||||||
|
|
||||||
[Column("is_pressure_gt_20"), Comment("Давление более 20")]
|
|
||||||
public bool IsPressureGt20 { get; set; }
|
|
||||||
|
|
||||||
[Column("is_hook_weight_not_changes"), Comment("Вес на крюке не меняется")]
|
|
||||||
public bool IsHookWeightNotChanges { get; set; }
|
|
||||||
|
|
||||||
[Column("is_hook_weight_lt_3"), Comment("Вес на крюке менее 3т")]
|
|
||||||
public bool IsHookWeightLt3 { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -18,7 +18,5 @@ namespace AsbCloudDb.Model
|
|||||||
[Column("code"), Comment("Код операции")]
|
[Column("code"), Comment("Код операции")]
|
||||||
public int Code { get; set; }
|
public int Code { get; set; }
|
||||||
|
|
||||||
[InverseProperty(nameof(TelemetryAnalysis.Operation))]
|
|
||||||
public virtual ICollection<TelemetryAnalysis> Analysis { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,9 @@ using AsbCloudApp.Data.SAUB;
|
|||||||
using AsbCloudApp.Services;
|
using AsbCloudApp.Services;
|
||||||
using AsbCloudDb.Model;
|
using AsbCloudDb.Model;
|
||||||
using AsbCloudInfrastructure.Services;
|
using AsbCloudInfrastructure.Services;
|
||||||
using AsbCloudInfrastructure.Services.Analysis;
|
|
||||||
using AsbCloudInfrastructure.Services.Cache;
|
using AsbCloudInfrastructure.Services.Cache;
|
||||||
using AsbCloudInfrastructure.Services.DailyReport;
|
using AsbCloudInfrastructure.Services.DailyReport;
|
||||||
|
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;
|
||||||
@ -54,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());
|
||||||
@ -75,7 +75,6 @@ namespace AsbCloudInfrastructure
|
|||||||
services.AddTransient<IOperationsStatService, OperationsStatService>();
|
services.AddTransient<IOperationsStatService, OperationsStatService>();
|
||||||
services.AddTransient<IReportService, ReportService>();
|
services.AddTransient<IReportService, ReportService>();
|
||||||
services.AddTransient<ISetpointsService, SetpointsService>();
|
services.AddTransient<ISetpointsService, SetpointsService>();
|
||||||
services.AddTransient<ITelemetryAnalyticsService, TelemetryAnalyticsService>();
|
|
||||||
services.AddTransient<ITelemetryService, TelemetryService>();
|
services.AddTransient<ITelemetryService, TelemetryService>();
|
||||||
services.AddTransient<ITelemetryUserService, TelemetryUserService>();
|
services.AddTransient<ITelemetryUserService, TelemetryUserService>();
|
||||||
services.AddTransient<ITimezoneService, TimezoneService>();
|
services.AddTransient<ITimezoneService, TimezoneService>();
|
||||||
@ -87,6 +86,7 @@ namespace AsbCloudInfrastructure
|
|||||||
services.AddTransient<IWellOperationService, WellOperationService>();
|
services.AddTransient<IWellOperationService, WellOperationService>();
|
||||||
services.AddTransient<IScheduleReportService, ScheduleReportService>();
|
services.AddTransient<IScheduleReportService, ScheduleReportService>();
|
||||||
services.AddTransient<IDailyReportService, DailyReportService>();
|
services.AddTransient<IDailyReportService, DailyReportService>();
|
||||||
|
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
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace AsbCloudInfrastructure.Services.Analysis
|
|
||||||
{
|
|
||||||
class DataSaubAnalyse
|
|
||||||
{
|
|
||||||
public int IdTelemetry { get; internal set; }
|
|
||||||
public DateTimeOffset Date { get; internal set; }
|
|
||||||
public double WellDepth { get; internal set; }
|
|
||||||
public double BitDepth { get; internal set; }
|
|
||||||
public double BlockPosition { get; internal set; }
|
|
||||||
public double RotorSpeed { get; internal set; }
|
|
||||||
public double Pressure { get; internal set; }
|
|
||||||
public double HookWeight { get; internal set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
using AsbCloudApp.Services;
|
|
||||||
using AsbCloudDb.Model;
|
|
||||||
using AsbCloudInfrastructure.Services.Cache;
|
|
||||||
using AsbCloudInfrastructure.Services.SAUB;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace AsbCloudInfrastructure.Services.Analysis
|
|
||||||
{
|
|
||||||
public class TelemetryAnalyticsBackgroundService : BackgroundService
|
|
||||||
{
|
|
||||||
private readonly CacheDb cacheDb;
|
|
||||||
private readonly ITelemetryTracker telemetryTracker;
|
|
||||||
private readonly string connectionString;
|
|
||||||
private readonly TimeSpan period = TimeSpan.FromHours(1);
|
|
||||||
|
|
||||||
public TelemetryAnalyticsBackgroundService(CacheDb cacheDb, ITelemetryTracker telemetryTracker, IConfiguration configuration)
|
|
||||||
{
|
|
||||||
this.cacheDb = cacheDb;
|
|
||||||
this.telemetryTracker = telemetryTracker;
|
|
||||||
connectionString = configuration.GetConnectionString("DefaultConnection");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task ExecuteAsync(CancellationToken token = default)
|
|
||||||
{
|
|
||||||
var timeToStartAnalysis = DateTime.Now;
|
|
||||||
var options = new DbContextOptionsBuilder<AsbCloudDbContext>()
|
|
||||||
.UseNpgsql(connectionString)
|
|
||||||
.Options;
|
|
||||||
|
|
||||||
while (!token.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
if (DateTime.Now > timeToStartAnalysis)
|
|
||||||
{
|
|
||||||
timeToStartAnalysis = DateTime.Now + period;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var context = new AsbCloudDbContext(options);
|
|
||||||
var timezoneService = new TimezoneService();
|
|
||||||
var telemetryService = new TelemetryService(context, telemetryTracker, timezoneService, cacheDb);
|
|
||||||
var analyticsService = new TelemetryAnalyticsService(context,
|
|
||||||
telemetryService, cacheDb);
|
|
||||||
|
|
||||||
await analyticsService.AnalyzeAndSaveTelemetriesAsync(token).ConfigureAwait(false);
|
|
||||||
context.Dispose();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Trace.TraceError(ex.Message);
|
|
||||||
Console.WriteLine(ex.Message);
|
|
||||||
}
|
|
||||||
GC.Collect();
|
|
||||||
}
|
|
||||||
|
|
||||||
var ms = (int)(timeToStartAnalysis - DateTime.Now).TotalMilliseconds;
|
|
||||||
ms = ms > 100 ? ms : 100;
|
|
||||||
await Task.Delay(ms, token).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task StopAsync(CancellationToken token)
|
|
||||||
{
|
|
||||||
await base.StopAsync(token).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,533 +0,0 @@
|
|||||||
using AsbCloudApp.Data;
|
|
||||||
using AsbCloudApp.Services;
|
|
||||||
using AsbCloudDb.Model;
|
|
||||||
using AsbCloudInfrastructure.Services.Cache;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace AsbCloudInfrastructure.Services.Analysis
|
|
||||||
{
|
|
||||||
public class TelemetryAnalyticsService : ITelemetryAnalyticsService
|
|
||||||
{
|
|
||||||
private readonly IAsbCloudDbContext db;
|
|
||||||
private readonly ITelemetryService telemetryService;
|
|
||||||
private readonly CacheTable<WellOperationCategory> cacheOperations;
|
|
||||||
private readonly TelemetryOperationDetectorService operationDetectorService;
|
|
||||||
private readonly IEnumerable<WellOperationCategory> operations;
|
|
||||||
|
|
||||||
private const int countOfRecordsForInterpolation = 12 * 60 * 60;
|
|
||||||
|
|
||||||
public TelemetryAnalyticsService(IAsbCloudDbContext db, ITelemetryService telemetryService,
|
|
||||||
CacheDb cacheDb)
|
|
||||||
{
|
|
||||||
this.db = db;
|
|
||||||
this.telemetryService = telemetryService;
|
|
||||||
cacheOperations = cacheDb.GetCachedTable<WellOperationCategory>((AsbCloudDbContext)db);
|
|
||||||
operations = cacheOperations.Where();
|
|
||||||
operationDetectorService = new TelemetryOperationDetectorService(operations);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<WellDepthToDayDto>> GetWellDepthToDayAsync(int idWell, CancellationToken token = default)
|
|
||||||
{
|
|
||||||
var idTelemetry = telemetryService.GetIdTelemetryByIdWell(idWell);
|
|
||||||
|
|
||||||
if (idTelemetry is null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var timezone = telemetryService.GetTimezone((int)idTelemetry);
|
|
||||||
var depthToTimeData = (from d in db.TelemetryDataSaub
|
|
||||||
where d.IdTelemetry == idTelemetry
|
|
||||||
select new
|
|
||||||
{
|
|
||||||
d.WellDepth,
|
|
||||||
d.BitDepth,
|
|
||||||
d.DateTime
|
|
||||||
});
|
|
||||||
|
|
||||||
var m = (int)Math.Round(1d * depthToTimeData.Count() / 2048);
|
|
||||||
|
|
||||||
if (m > 1)
|
|
||||||
depthToTimeData = depthToTimeData.Where((d, i) => (((d.DateTime.DayOfYear * 24 + d.DateTime.Hour) * 60 + d.DateTime.Minute) * 60 + d.DateTime.Second) % m == 0);
|
|
||||||
|
|
||||||
return await depthToTimeData.Select(d => new WellDepthToDayDto
|
|
||||||
{
|
|
||||||
WellDepth = d.WellDepth ?? 0.0,
|
|
||||||
BitDepth = d.BitDepth ?? 0.0,
|
|
||||||
Date = d.DateTime.ToRemoteDateTime(timezone.Hours),
|
|
||||||
}).AsNoTracking().ToListAsync(token).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<WellDepthToIntervalDto>> GetWellDepthToIntervalAsync(int idWell,
|
|
||||||
int intervalSeconds, int shiftStartSec, CancellationToken token = default)
|
|
||||||
{
|
|
||||||
intervalSeconds = intervalSeconds == 0 ? 86400 : intervalSeconds;
|
|
||||||
|
|
||||||
var telemetryId = telemetryService.GetIdTelemetryByIdWell(idWell);
|
|
||||||
|
|
||||||
if (telemetryId is null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var timezone = telemetryService.GetTimezone((int)telemetryId);
|
|
||||||
|
|
||||||
var drillingPeriodsInfo = await db.TelemetryDataSaub
|
|
||||||
.Where(t => t.IdTelemetry == telemetryId)
|
|
||||||
.GroupBy(t => Math.Floor((((t.DateTime.DayOfYear * 24 + t.DateTime.Hour) * 60 + t.DateTime.Minute) * 60 + t.DateTime.Second + timezone.Hours - shiftStartSec) / intervalSeconds))
|
|
||||||
.Select(g => new
|
|
||||||
{
|
|
||||||
WellDepthMin = g.Min(t => t.WellDepth),
|
|
||||||
WellDepthMax = g.Max(t => t.WellDepth),
|
|
||||||
DateMin = g.Min(t => t.DateTime),
|
|
||||||
DateMax = g.Max(t => t.DateTime),
|
|
||||||
})
|
|
||||||
.OrderBy(g => g.DateMin)
|
|
||||||
.ToListAsync(token);
|
|
||||||
|
|
||||||
var wellDepthToIntervalData = drillingPeriodsInfo.Select(d => new WellDepthToIntervalDto
|
|
||||||
{
|
|
||||||
IntervalStartDate = d.DateMin.ToRemoteDateTime(timezone.Hours),
|
|
||||||
IntervalDepthProgress = (d.WellDepthMax - d.WellDepthMin) ?? 0.0
|
|
||||||
// / (d.DateMax - d.DateMin).TotalHours,
|
|
||||||
}).OrderBy(d => d.IntervalStartDate).ToList();
|
|
||||||
|
|
||||||
return wellDepthToIntervalData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<PaginationContainer<TelemetryOperationDto>> GetOperationsByWellAsync(int idWell,
|
|
||||||
IEnumerable<int> categoryIds = default, DateTime begin = default,
|
|
||||||
DateTime end = default, int skip = 0, int take = 32, CancellationToken token = default)
|
|
||||||
{
|
|
||||||
var telemetryId = telemetryService.GetIdTelemetryByIdWell(idWell);
|
|
||||||
|
|
||||||
if (telemetryId is null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var operations = from a in db.TelemetryAnalysis.Include(t => t.Operation)
|
|
||||||
where a.IdTelemetry == telemetryId
|
|
||||||
select a;
|
|
||||||
|
|
||||||
if ((categoryIds != default) && (categoryIds.Any()))
|
|
||||||
operations = operations.Where(o => categoryIds.Contains(o.IdOperation));
|
|
||||||
|
|
||||||
var result = new PaginationContainer<TelemetryOperationDto>
|
|
||||||
{
|
|
||||||
Skip = skip,
|
|
||||||
Take = take
|
|
||||||
};
|
|
||||||
|
|
||||||
operations = operations.OrderBy(o => o.UnixDate);
|
|
||||||
|
|
||||||
if (begin != default)
|
|
||||||
{
|
|
||||||
var unixBegin = (begin - new DateTime(1970, 1, 1)).TotalSeconds;
|
|
||||||
operations = operations.Where(o => o.UnixDate >= unixBegin);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (end != default)
|
|
||||||
{
|
|
||||||
var unixEnd = (end - new DateTime(1970, 1, 1)).TotalSeconds;
|
|
||||||
operations = operations.Where(m => (m.UnixDate + m.DurationSec) <= unixEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Count = await operations.CountAsync(token).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (skip > 0)
|
|
||||||
operations = operations.Skip(skip);
|
|
||||||
|
|
||||||
var operationsList = await operations.Take(take)
|
|
||||||
.AsNoTracking()
|
|
||||||
.ToListAsync(token)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (operationsList.Count == 0)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
foreach (var operation in operations)
|
|
||||||
{
|
|
||||||
var operationDto = new TelemetryOperationDto
|
|
||||||
{
|
|
||||||
Id = operation.Id,
|
|
||||||
Name = operation.Operation.Name,
|
|
||||||
BeginDate = DateTimeOffset.FromUnixTimeSeconds(operation.UnixDate).DateTime,
|
|
||||||
EndDate = DateTimeOffset.FromUnixTimeSeconds(operation.UnixDate + operation.DurationSec).DateTime,
|
|
||||||
StartWellDepth = operation.OperationStartDepth ?? 0.0,
|
|
||||||
EndWellDepth = operation.OperationEndDepth ?? 0.0
|
|
||||||
};
|
|
||||||
|
|
||||||
result.Items.Add(operationDto);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<TelemetryOperationDurationDto>> GetOperationsSummaryAsync(int idWell,
|
|
||||||
DateTime begin = default, DateTime end = default, CancellationToken token = default)
|
|
||||||
{
|
|
||||||
var telemetryId = telemetryService.GetIdTelemetryByIdWell(idWell);
|
|
||||||
|
|
||||||
if (telemetryId is null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var unixBegin = begin == default
|
|
||||||
? 0
|
|
||||||
: (begin - new DateTime(1970, 1, 1)).TotalSeconds;
|
|
||||||
var unixEnd = end == default
|
|
||||||
? (DateTime.Now - new DateTime(1970, 1, 1)).TotalSeconds
|
|
||||||
: (end - new DateTime(1970, 1, 1)).TotalSeconds;
|
|
||||||
|
|
||||||
return await (from a in db.TelemetryAnalysis
|
|
||||||
where a.IdTelemetry == telemetryId &&
|
|
||||||
a.UnixDate > unixBegin && a.UnixDate < unixEnd
|
|
||||||
join o in db.WellOperationCategories on a.IdOperation equals o.Id
|
|
||||||
group a by new { a.IdOperation, o.Name } into g
|
|
||||||
select new TelemetryOperationDurationDto
|
|
||||||
{
|
|
||||||
OperationName = g.Key.Name,
|
|
||||||
Duration = g.Where(g => g.DurationSec > 0)
|
|
||||||
.Sum(a => a.DurationSec)
|
|
||||||
}).AsNoTracking().ToListAsync(token)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This method is not finished (only half done). It returns not correct Dtos.
|
|
||||||
public async Task<IEnumerable<TelemetryOperationInfoDto>> GetOperationsToIntervalAsync(int idWell,
|
|
||||||
int intervalSeconds, int workBeginSeconds, CancellationToken token = default)
|
|
||||||
{
|
|
||||||
intervalSeconds = intervalSeconds == 0 ? 86400 : intervalSeconds;
|
|
||||||
|
|
||||||
var telemetryId = telemetryService.GetIdTelemetryByIdWell(idWell);
|
|
||||||
|
|
||||||
if (telemetryId is null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var timezone = telemetryService.GetTimezone((int)telemetryId);
|
|
||||||
|
|
||||||
// Get'n'Group all operations only by start date and by name (if there were several operations in interval).
|
|
||||||
// Without dividing these operations duration by given interval
|
|
||||||
var ops = await (from a in db.TelemetryAnalysis
|
|
||||||
where a.IdTelemetry == telemetryId
|
|
||||||
join o in db.WellOperationCategories on a.IdOperation equals o.Id
|
|
||||||
group a by new
|
|
||||||
{
|
|
||||||
Interval = Math.Floor((a.UnixDate - workBeginSeconds + timezone.Hours) / intervalSeconds),
|
|
||||||
o.Name
|
|
||||||
} into g
|
|
||||||
select new
|
|
||||||
{
|
|
||||||
IntervalStart = g.Min(d => d.UnixDate),
|
|
||||||
OperationName = g.Key.Name,
|
|
||||||
OperationDuration = g.Sum(an => an.DurationSec)
|
|
||||||
}).AsNoTracking()
|
|
||||||
.OrderBy(op => op.IntervalStart)
|
|
||||||
.ToListAsync(token)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
var groupedOperationsList = new List<TelemetryOperationInfoDto>();
|
|
||||||
|
|
||||||
if (operations is not null && operations.Any())
|
|
||||||
{
|
|
||||||
var operations = ops.Select(o => (o.IntervalStart, o.OperationName, o.OperationDuration));
|
|
||||||
|
|
||||||
var splittedOperationsByInterval = DivideOperationsByIntervalLength(operations, intervalSeconds); // divides good
|
|
||||||
|
|
||||||
groupedOperationsList = UniteOperationsInDto(splittedOperationsByInterval, intervalSeconds, timezone.Hours).ToList(); // unites not good
|
|
||||||
}
|
|
||||||
|
|
||||||
return groupedOperationsList;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task AnalyzeAndSaveTelemetriesAsync(CancellationToken token = default)
|
|
||||||
{
|
|
||||||
var allTelemetryIds = await db.Telemetries.Select(t => t.Id).ToListAsync(token).ConfigureAwait(false);
|
|
||||||
|
|
||||||
foreach (var idTelemetry in allTelemetryIds)
|
|
||||||
{
|
|
||||||
var analyzeStartDate = await GetLastAnalysisDateAsync(idTelemetry, token).ConfigureAwait(false);
|
|
||||||
await AnalyseAndSaveTelemetryAsync(idTelemetry, analyzeStartDate, token).ConfigureAwait(false);
|
|
||||||
GC.Collect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task AnalyseAndSaveTelemetryAsync(int idTelemetry, DateTimeOffset analyzeStartDate, CancellationToken token = default)
|
|
||||||
{
|
|
||||||
const int step = 10;
|
|
||||||
const int take = step * 2;
|
|
||||||
|
|
||||||
TelemetryAnalysis currentAnalysis = null;
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
var dataSaubPart = await GetDataSaubPartOrDefaultAsync(idTelemetry, analyzeStartDate, token).ConfigureAwait(false);
|
|
||||||
if (dataSaubPart is null)
|
|
||||||
break;
|
|
||||||
|
|
||||||
var count = dataSaubPart.Count;
|
|
||||||
var skip = 0;
|
|
||||||
|
|
||||||
if (step > count)
|
|
||||||
break;
|
|
||||||
|
|
||||||
analyzeStartDate = dataSaubPart.Last().Date;
|
|
||||||
for (; (skip + step) < count; skip += step)
|
|
||||||
{
|
|
||||||
var dataSaubPartOfPart = dataSaubPart.Skip(skip).Take(take);
|
|
||||||
var telemetryAnalysis = GetDrillingAnalysis(dataSaubPartOfPart);
|
|
||||||
|
|
||||||
if (currentAnalysis is not null)
|
|
||||||
{
|
|
||||||
if (currentAnalysis.IdOperation == telemetryAnalysis.IdOperation)
|
|
||||||
currentAnalysis.DurationSec += telemetryAnalysis.DurationSec;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
currentAnalysis.OperationEndDepth = dataSaubPartOfPart.LastOrDefault()?.WellDepth;
|
|
||||||
db.TelemetryAnalysis.Add(currentAnalysis);
|
|
||||||
currentAnalysis = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentAnalysis is null)
|
|
||||||
{
|
|
||||||
currentAnalysis = telemetryAnalysis;
|
|
||||||
currentAnalysis.OperationStartDepth = dataSaubPartOfPart.FirstOrDefault()?.WellDepth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await db.SaveChangesAsync(token).ConfigureAwait(false);
|
|
||||||
GC.Collect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<DatesRangeDto> GetOperationsDateRangeAsync(int idWell, CancellationToken token = default)
|
|
||||||
{
|
|
||||||
var idTelemetry = telemetryService.GetIdTelemetryByIdWell(idWell);
|
|
||||||
|
|
||||||
if (idTelemetry is null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var timezone = telemetryService.GetTimezone((int)idTelemetry);
|
|
||||||
|
|
||||||
var datesRange = await (from d in db.TelemetryAnalysis
|
|
||||||
where d.IdTelemetry == idTelemetry
|
|
||||||
select d.UnixDate).DefaultIfEmpty()
|
|
||||||
.GroupBy(g => true)
|
|
||||||
.AsNoTracking()
|
|
||||||
.Select(g => new
|
|
||||||
{
|
|
||||||
From = g.Min(),
|
|
||||||
To = g.Max()
|
|
||||||
}).OrderBy(gr => gr.From)
|
|
||||||
.FirstOrDefaultAsync(token)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
var result = new DatesRangeDto
|
|
||||||
{
|
|
||||||
From = DateTimeOffset.FromUnixTimeSeconds(datesRange.From).ToRemoteDateTime(timezone.Hours),
|
|
||||||
To = (datesRange.To == default
|
|
||||||
? DateTime.MaxValue
|
|
||||||
: DateTimeOffset.FromUnixTimeSeconds(datesRange.To)).ToRemoteDateTime(timezone.Hours),
|
|
||||||
};
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<DateTime> GetLastAnalysisDateAsync(int idTelemetry, CancellationToken token = default)
|
|
||||||
{
|
|
||||||
var lastAnalysisInDb = await (from analysis in db.TelemetryAnalysis
|
|
||||||
where analysis.IdTelemetry == idTelemetry
|
|
||||||
orderby analysis.UnixDate
|
|
||||||
select analysis)
|
|
||||||
.LastOrDefaultAsync(token)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
DateTime lastAnalysisDate = new DateTime(0, DateTimeKind.Utc);
|
|
||||||
|
|
||||||
if (lastAnalysisInDb is not null)
|
|
||||||
lastAnalysisDate = DateTime.UnixEpoch.AddSeconds(lastAnalysisInDb.DurationSec + lastAnalysisInDb.UnixDate);
|
|
||||||
|
|
||||||
return lastAnalysisDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task<List<DataSaubAnalyse>> GetDataSaubPartOrDefaultAsync(int idTelemetry, DateTimeOffset analyzeStartDate, CancellationToken token) =>
|
|
||||||
db.TelemetryDataSaub
|
|
||||||
.Where(d =>
|
|
||||||
d.IdTelemetry == idTelemetry &&
|
|
||||||
d.DateTime > analyzeStartDate &&
|
|
||||||
d.BitDepth != null &&
|
|
||||||
d.BlockPosition != null &&
|
|
||||||
d.HookWeight != null &&
|
|
||||||
d.Pressure != null &&
|
|
||||||
d.RotorSpeed != null &&
|
|
||||||
d.WellDepth != null
|
|
||||||
)
|
|
||||||
.OrderBy(d => d.DateTime)
|
|
||||||
.Take(countOfRecordsForInterpolation)
|
|
||||||
.Select(d => new DataSaubAnalyse
|
|
||||||
{
|
|
||||||
IdTelemetry = d.IdTelemetry,
|
|
||||||
Date = d.DateTime,
|
|
||||||
BitDepth = d.BitDepth ?? 0,
|
|
||||||
BlockPosition = d.BlockPosition ?? 0,
|
|
||||||
HookWeight = d.HookWeight ?? 0,
|
|
||||||
Pressure = d.Pressure ?? 0,
|
|
||||||
RotorSpeed = d.RotorSpeed ?? 0,
|
|
||||||
WellDepth = d.WellDepth ?? 0,
|
|
||||||
})
|
|
||||||
.ToListAsync(token);
|
|
||||||
|
|
||||||
private static IEnumerable<(long IntervalStart, string OperationName, int OperationDuration)> DivideOperationsByIntervalLength(
|
|
||||||
IEnumerable<(long IntervalStart, string OperationName, int OperationDuration)> operations, int intervalSeconds)
|
|
||||||
{
|
|
||||||
var splittedOperationsByInterval = new List<(long IntervalStart, string OperationName, int OperationDuration)>();
|
|
||||||
|
|
||||||
var operationDurationTimeCounter = 0;
|
|
||||||
|
|
||||||
foreach (var (IntervalStart, OperationName, OperationDuration) in operations)
|
|
||||||
{
|
|
||||||
if (OperationDuration < (intervalSeconds - operationDurationTimeCounter))
|
|
||||||
{
|
|
||||||
splittedOperationsByInterval.Add((IntervalStart, OperationName, OperationDuration));
|
|
||||||
operationDurationTimeCounter += OperationDuration;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{ // if operation duration overflows current interval it should be divided into 2 or more parts for this and next intervals
|
|
||||||
var remainingIntervalTime = intervalSeconds - operationDurationTimeCounter;
|
|
||||||
splittedOperationsByInterval.Add((IntervalStart, OperationName, remainingIntervalTime)); // first part of long operation
|
|
||||||
|
|
||||||
var operationDurationAfterDividing = OperationDuration - remainingIntervalTime; // second part of long operation. Can be less or more than interval
|
|
||||||
|
|
||||||
// If operation duration even after dividing is still more than interval,
|
|
||||||
// it should be divided several times to several intervals.
|
|
||||||
if (operationDurationAfterDividing > intervalSeconds)
|
|
||||||
{
|
|
||||||
var counter = 0;
|
|
||||||
var updatedIntervalStartTime = IntervalStart + remainingIntervalTime;
|
|
||||||
|
|
||||||
while (operationDurationAfterDividing > intervalSeconds)
|
|
||||||
{
|
|
||||||
splittedOperationsByInterval.Add((updatedIntervalStartTime + intervalSeconds * counter, OperationName, intervalSeconds));
|
|
||||||
operationDurationAfterDividing -= intervalSeconds;
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
splittedOperationsByInterval.Add((updatedIntervalStartTime + operationDurationAfterDividing, OperationName, operationDurationAfterDividing));
|
|
||||||
|
|
||||||
operationDurationTimeCounter = operationDurationAfterDividing;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
splittedOperationsByInterval.Add((IntervalStart, OperationName, operationDurationAfterDividing));
|
|
||||||
operationDurationTimeCounter = operationDurationAfterDividing;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return splittedOperationsByInterval;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IEnumerable<TelemetryOperationInfoDto> UniteOperationsInDto(
|
|
||||||
IEnumerable<(long IntervalStart, string OperationName, int OperationDuration)> operations, int intervalSeconds, double timezoneOffset)
|
|
||||||
{
|
|
||||||
var groupedOperationsList = new List<TelemetryOperationInfoDto>();
|
|
||||||
|
|
||||||
var groupedOperationsObj = new TelemetryOperationInfoDto
|
|
||||||
{
|
|
||||||
IntervalBegin = DateTimeOffset.FromUnixTimeSeconds(operations.First().IntervalStart)
|
|
||||||
.ToRemoteDateTime(timezoneOffset),
|
|
||||||
Operations = new List<TelemetryOperationDetailsDto>()
|
|
||||||
};
|
|
||||||
|
|
||||||
var intervalEndDate = operations.First().IntervalStart + intervalSeconds;
|
|
||||||
|
|
||||||
foreach (var (IntervalStart, OperationName, OperationDuration) in operations)
|
|
||||||
{
|
|
||||||
if (IntervalStart < intervalEndDate)
|
|
||||||
{
|
|
||||||
groupedOperationsObj.Operations.Add(new TelemetryOperationDetailsDto
|
|
||||||
{
|
|
||||||
OperationName = OperationName,
|
|
||||||
DurationSec = OperationDuration
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
groupedOperationsList.Add(groupedOperationsObj);
|
|
||||||
|
|
||||||
intervalEndDate = IntervalStart + intervalSeconds;
|
|
||||||
groupedOperationsObj = new TelemetryOperationInfoDto
|
|
||||||
{
|
|
||||||
IntervalBegin = DateTimeOffset.FromUnixTimeSeconds(IntervalStart)
|
|
||||||
.ToRemoteDateTime(timezoneOffset),
|
|
||||||
Operations = new List<TelemetryOperationDetailsDto>()
|
|
||||||
};
|
|
||||||
|
|
||||||
groupedOperationsObj.Operations.Add(new TelemetryOperationDetailsDto
|
|
||||||
{
|
|
||||||
OperationName = OperationName,
|
|
||||||
DurationSec = OperationDuration
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
groupedOperationsList.Add(groupedOperationsObj);
|
|
||||||
|
|
||||||
return groupedOperationsList;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TelemetryAnalysis GetDrillingAnalysis(IEnumerable<DataSaubAnalyse> dataSaubPartOfPart)
|
|
||||||
{
|
|
||||||
var dataSaubFirst = dataSaubPartOfPart.First();
|
|
||||||
var dataSaubLast = dataSaubPartOfPart.Last();
|
|
||||||
|
|
||||||
var saubWellDepths = dataSaubPartOfPart.Select(s => (Y: (double)s.WellDepth,
|
|
||||||
X: (s.Date - dataSaubFirst.Date).TotalSeconds));
|
|
||||||
var saubBitDepths = dataSaubPartOfPart.Select(s => (Y: (double)s.BitDepth,
|
|
||||||
X: (s.Date - dataSaubFirst.Date).TotalSeconds));
|
|
||||||
var saubBlockPositions = dataSaubPartOfPart.Select(s => (Y: (double)s.BlockPosition,
|
|
||||||
X: (s.Date - dataSaubFirst.Date).TotalSeconds));
|
|
||||||
var saubRotorSpeeds = dataSaubPartOfPart.Select(s => (Y: (double)s.RotorSpeed,
|
|
||||||
X: (s.Date - dataSaubFirst.Date).TotalSeconds));
|
|
||||||
var saubPressures = dataSaubPartOfPart.Select(s => (Y: (double)s.Pressure,
|
|
||||||
X: (s.Date - dataSaubFirst.Date).TotalSeconds));
|
|
||||||
var saubHookWeights = dataSaubPartOfPart.Select(s => (Y: (double)s.HookWeight,
|
|
||||||
X: (s.Date - dataSaubFirst.Date).TotalSeconds));
|
|
||||||
|
|
||||||
var wellDepthLine = new InterpolationLine(saubWellDepths);
|
|
||||||
var bitPositionLine = new InterpolationLine(saubBitDepths);
|
|
||||||
var blockPositionLine = new InterpolationLine(saubBlockPositions);
|
|
||||||
var rotorSpeedLine = new InterpolationLine(saubRotorSpeeds);
|
|
||||||
var pressureLine = new InterpolationLine(saubPressures);
|
|
||||||
var hookWeightLine = new InterpolationLine(saubHookWeights);
|
|
||||||
|
|
||||||
var drillingAnalysis = new TelemetryAnalysis
|
|
||||||
{
|
|
||||||
IdTelemetry = dataSaubFirst.IdTelemetry,
|
|
||||||
UnixDate = (long)(dataSaubFirst.Date - DateTime.UnixEpoch).TotalSeconds,
|
|
||||||
DurationSec = (int)(dataSaubLast.Date - dataSaubFirst.Date).TotalSeconds,
|
|
||||||
OperationStartDepth = null,
|
|
||||||
OperationEndDepth = null,
|
|
||||||
IsWellDepthDecreasing = wellDepthLine.IsYDecreases(-0.0001),
|
|
||||||
IsWellDepthIncreasing = wellDepthLine.IsYIncreases(0.0001),
|
|
||||||
IsBitPositionDecreasing = bitPositionLine.IsYDecreases(-0.0001),
|
|
||||||
IsBitPositionIncreasing = bitPositionLine.IsYIncreases(0.0001),
|
|
||||||
IsBitPositionLt20 = bitPositionLine.IsAverageYLessThanBound(20),
|
|
||||||
IsBlockPositionDecreasing = blockPositionLine.IsYDecreases(-0.0001),
|
|
||||||
IsBlockPositionIncreasing = blockPositionLine.IsYIncreases(0.0001),
|
|
||||||
IsRotorSpeedLt5 = rotorSpeedLine.IsAverageYLessThanBound(5),
|
|
||||||
IsRotorSpeedGt5 = rotorSpeedLine.IsAverageYMoreThanBound(5),
|
|
||||||
IsPressureLt20 = pressureLine.IsAverageYLessThanBound(20),
|
|
||||||
IsPressureGt20 = pressureLine.IsAverageYMoreThanBound(20),
|
|
||||||
IsHookWeightNotChanges = hookWeightLine.IsYNotChanges(0.0001, -0.0001),
|
|
||||||
IsHookWeightLt3 = hookWeightLine.IsAverageYLessThanBound(3),
|
|
||||||
IdOperation = default,
|
|
||||||
};
|
|
||||||
|
|
||||||
drillingAnalysis.IdOperation =
|
|
||||||
operationDetectorService.DetectOperation(drillingAnalysis).Id;
|
|
||||||
|
|
||||||
return drillingAnalysis;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
using AsbCloudDb.Model;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace AsbCloudInfrastructure.Services.Analysis
|
|
||||||
{
|
|
||||||
class TelemetryOperationDetector
|
|
||||||
{
|
|
||||||
public int Order { get; set; }
|
|
||||||
public WellOperationCategory Operation { get; set; }
|
|
||||||
public Func<TelemetryAnalysis, bool> Detect { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,205 +0,0 @@
|
|||||||
using AsbCloudDb.Model;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace AsbCloudInfrastructure.Services.Analysis
|
|
||||||
{
|
|
||||||
class TelemetryOperationDetectorService
|
|
||||||
{
|
|
||||||
private readonly IEnumerable<TelemetryOperationDetector> detectors;
|
|
||||||
|
|
||||||
public TelemetryOperationDetectorService(IEnumerable<WellOperationCategory> operations)
|
|
||||||
{
|
|
||||||
detectors = new List<TelemetryOperationDetector>()
|
|
||||||
{
|
|
||||||
new TelemetryOperationDetector
|
|
||||||
{
|
|
||||||
Order = 1,
|
|
||||||
Operation = operations.FirstOrDefault(o => o.Name.Equals("Роторное бурение")),
|
|
||||||
Detect = (data) =>
|
|
||||||
{
|
|
||||||
return data.IsWellDepthIncreasing && data.IsBitPositionIncreasing &&
|
|
||||||
data.IsBlockPositionIncreasing && data.IsRotorSpeedGt5;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TelemetryOperationDetector
|
|
||||||
{
|
|
||||||
Order = 2,
|
|
||||||
Operation = operations.FirstOrDefault(o => o.Name.Equals("Слайдирование")),
|
|
||||||
Detect = (data) =>
|
|
||||||
{
|
|
||||||
return data.IsWellDepthIncreasing && data.IsBitPositionIncreasing &&
|
|
||||||
data.IsBlockPositionIncreasing && data.IsRotorSpeedLt5;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TelemetryOperationDetector
|
|
||||||
{
|
|
||||||
Order = 3,
|
|
||||||
Operation = operations.FirstOrDefault(o => o.Name.Equals("На поверхности")),
|
|
||||||
Detect = (data) =>
|
|
||||||
{
|
|
||||||
return data.IsBitPositionLt20 && data.IsHookWeightLt3;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TelemetryOperationDetector
|
|
||||||
{
|
|
||||||
Order = 4,
|
|
||||||
Operation = operations.FirstOrDefault(o => o.Name.Equals("Удержание в клиньях")),
|
|
||||||
Detect = (data) =>
|
|
||||||
{
|
|
||||||
return !data.IsWellDepthDecreasing && !data.IsWellDepthIncreasing &&
|
|
||||||
!data.IsBitPositionIncreasing && !data.IsBitPositionDecreasing &&
|
|
||||||
data.IsBlockPositionDecreasing && !data.IsBlockPositionIncreasing &&
|
|
||||||
data.IsHookWeightLt3;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TelemetryOperationDetector
|
|
||||||
{
|
|
||||||
Order = 5,
|
|
||||||
Operation = operations.FirstOrDefault(o => o.Name.Equals("Подъем с проработкой")),
|
|
||||||
Detect = (data) =>
|
|
||||||
{
|
|
||||||
return !data.IsWellDepthDecreasing && !data.IsWellDepthIncreasing &&
|
|
||||||
data.IsBitPositionDecreasing && data.IsBlockPositionIncreasing &&
|
|
||||||
data.IsRotorSpeedGt5 && data.IsPressureGt20;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TelemetryOperationDetector
|
|
||||||
{
|
|
||||||
Order = 6,
|
|
||||||
Operation = operations.FirstOrDefault(o => o.Name.Equals("Спуск с проработкой")),
|
|
||||||
Detect = (data) =>
|
|
||||||
{
|
|
||||||
return !data.IsWellDepthDecreasing && !data.IsWellDepthIncreasing &&
|
|
||||||
data.IsBitPositionIncreasing && data.IsBlockPositionDecreasing &&
|
|
||||||
data.IsRotorSpeedGt5 && data.IsPressureGt20;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TelemetryOperationDetector
|
|
||||||
{
|
|
||||||
Order = 7,
|
|
||||||
Operation = operations.FirstOrDefault(o => o.Name.Equals("Подъем с промывкой")),
|
|
||||||
Detect = (data) =>
|
|
||||||
{
|
|
||||||
return !data.IsWellDepthDecreasing && !data.IsWellDepthIncreasing &&
|
|
||||||
data.IsBitPositionDecreasing && data.IsBlockPositionIncreasing &&
|
|
||||||
data.IsRotorSpeedLt5 && data.IsPressureGt20;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TelemetryOperationDetector
|
|
||||||
{
|
|
||||||
Order = 8,
|
|
||||||
Operation = operations.FirstOrDefault(o => o.Name.Equals("Спуск с промывкой")),
|
|
||||||
Detect = (data) =>
|
|
||||||
{
|
|
||||||
return !data.IsWellDepthDecreasing && !data.IsWellDepthIncreasing &&
|
|
||||||
data.IsBitPositionIncreasing && data.IsBlockPositionDecreasing &&
|
|
||||||
data.IsRotorSpeedLt5 && data.IsPressureGt20;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TelemetryOperationDetector
|
|
||||||
{
|
|
||||||
Order = 9,
|
|
||||||
Operation = operations.FirstOrDefault(o => o.Name.Equals("Спуск в скважину")),
|
|
||||||
Detect = (data) =>
|
|
||||||
{
|
|
||||||
return !data.IsWellDepthDecreasing && !data.IsWellDepthIncreasing &&
|
|
||||||
data.IsBitPositionIncreasing && data.IsBlockPositionDecreasing &&
|
|
||||||
data.IsRotorSpeedLt5 && data.IsPressureLt20;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TelemetryOperationDetector
|
|
||||||
{
|
|
||||||
Order = 10,
|
|
||||||
Operation = operations.FirstOrDefault(o => o.Name.Equals("Спуск с вращением")),
|
|
||||||
Detect = (data) =>
|
|
||||||
{
|
|
||||||
return !data.IsWellDepthDecreasing && !data.IsWellDepthIncreasing &&
|
|
||||||
data.IsBitPositionIncreasing && data.IsBlockPositionDecreasing &&
|
|
||||||
data.IsRotorSpeedGt5 && data.IsPressureLt20;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TelemetryOperationDetector
|
|
||||||
{
|
|
||||||
Order = 11,
|
|
||||||
Operation = operations.FirstOrDefault(o => o.Name.Equals("Подъем из скважины")),
|
|
||||||
Detect = (data) =>
|
|
||||||
{
|
|
||||||
return !data.IsWellDepthDecreasing && !data.IsWellDepthIncreasing &&
|
|
||||||
data.IsBitPositionDecreasing && data.IsBlockPositionIncreasing &&
|
|
||||||
data.IsRotorSpeedLt5 && data.IsPressureLt20;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TelemetryOperationDetector
|
|
||||||
{
|
|
||||||
Order = 12,
|
|
||||||
Operation = operations.FirstOrDefault(o => o.Name.Equals("Подъем с вращением")),
|
|
||||||
Detect = (data) =>
|
|
||||||
{
|
|
||||||
return !data.IsWellDepthDecreasing && !data.IsWellDepthIncreasing &&
|
|
||||||
data.IsBitPositionDecreasing && data.IsBlockPositionIncreasing &&
|
|
||||||
data.IsRotorSpeedGt5 && data.IsPressureLt20;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TelemetryOperationDetector
|
|
||||||
{
|
|
||||||
Order = 13,
|
|
||||||
Operation = operations.FirstOrDefault(o => o.Name.Equals("Промывка в покое")),
|
|
||||||
Detect = (data) =>
|
|
||||||
{
|
|
||||||
return !data.IsWellDepthDecreasing && !data.IsWellDepthIncreasing &&
|
|
||||||
!data.IsBitPositionDecreasing && !data.IsBitPositionIncreasing &&
|
|
||||||
!data.IsBlockPositionDecreasing && !data.IsBlockPositionIncreasing &&
|
|
||||||
data.IsRotorSpeedLt5 && data.IsPressureGt20;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TelemetryOperationDetector
|
|
||||||
{
|
|
||||||
Order = 14,
|
|
||||||
Operation = operations.FirstOrDefault(o => o.Name.Equals("Промывка с вращением")),
|
|
||||||
Detect = (data) =>
|
|
||||||
{
|
|
||||||
return !data.IsWellDepthDecreasing && !data.IsWellDepthIncreasing &&
|
|
||||||
!data.IsBitPositionDecreasing && !data.IsBitPositionIncreasing &&
|
|
||||||
!data.IsBlockPositionDecreasing && !data.IsBlockPositionIncreasing &&
|
|
||||||
data.IsRotorSpeedGt5 && data.IsPressureGt20;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TelemetryOperationDetector
|
|
||||||
{
|
|
||||||
Order = 15,
|
|
||||||
Operation = operations.FirstOrDefault(o => o.Name.Equals("Неподвижное состояние")),
|
|
||||||
Detect = (data) =>
|
|
||||||
{
|
|
||||||
return !data.IsWellDepthDecreasing && !data.IsWellDepthIncreasing &&
|
|
||||||
!data.IsBitPositionDecreasing && !data.IsBitPositionIncreasing &&
|
|
||||||
!data.IsBlockPositionDecreasing && !data.IsBlockPositionIncreasing &&
|
|
||||||
data.IsRotorSpeedLt5 && data.IsPressureLt20 && data.IsHookWeightNotChanges;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TelemetryOperationDetector
|
|
||||||
{
|
|
||||||
Order = 16,
|
|
||||||
Operation = operations.FirstOrDefault(o => o.Name.Equals("Вращение без циркуляции")),
|
|
||||||
Detect = (data) =>
|
|
||||||
{
|
|
||||||
return !data.IsWellDepthDecreasing && !data.IsWellDepthIncreasing &&
|
|
||||||
!data.IsBitPositionDecreasing && !data.IsBitPositionIncreasing &&
|
|
||||||
!data.IsBlockPositionDecreasing && !data.IsBlockPositionIncreasing &&
|
|
||||||
data.IsRotorSpeedGt5 && data.IsPressureLt20;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TelemetryOperationDetector
|
|
||||||
{
|
|
||||||
Order = 17,
|
|
||||||
Operation = operations.FirstOrDefault(o => o.Name.Equals("Невозможно определить операцию")),
|
|
||||||
Detect = (data) => true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public WellOperationCategory DetectOperation(TelemetryAnalysis data) =>
|
|
||||||
detectors.OrderBy(d => d.Order).First(o => o.Detect(data)).Operation
|
|
||||||
?? new WellOperationCategory { Id = 1, Name = "Невозможно определить операцию" };
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace AsbCloudInfrastructure.Services.DetectOperations
|
||||||
|
{
|
||||||
|
public class DetectableTelemetry
|
||||||
|
{
|
||||||
|
public DateTimeOffset DateTime { get; set; }
|
||||||
|
public int? IdUser { get; set; }
|
||||||
|
public float? WellDepth { get; set; }
|
||||||
|
public float? Pressure { get; set; }
|
||||||
|
public float? HookWeight { get; set; }
|
||||||
|
public float? BlockPosition { get; set; }
|
||||||
|
public float? BitDepth { get; set; }
|
||||||
|
public float? RotorSpeed { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -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.DateEnd <= 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.DepthEnd <= 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
using AsbCloudDb.Model;
|
||||||
|
|
||||||
|
namespace AsbCloudInfrastructure.Services.DetectOperations
|
||||||
|
{
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
abstract class DetectorAbstract
|
||||||
|
{
|
||||||
|
public int StepLength { get; set; } = 3;
|
||||||
|
public int FragmentLength { get; set; } = 6;
|
||||||
|
public int IdCategory { get; }
|
||||||
|
|
||||||
|
// TODO: assert MaxDurationSeconds and MinDurationSeconds
|
||||||
|
public double MaxDurationSeconds { get; } = 31 * 24 * 60 * 60;
|
||||||
|
public double MinDurationSeconds { get; } = 3;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Конструктор детектора.
|
||||||
|
/// Словарь с IdCategory в дефолтных данных AsbCloudDbContext для таблицы WellOperationCategory
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="IdCategory">ключ названия/описания операции из таблицы WellOperationCategory</param>
|
||||||
|
public DetectorAbstract(int IdCategory)
|
||||||
|
{
|
||||||
|
this.IdCategory = IdCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual DetectedOperation? DetectOrDefault(DetectableTelemetry[] telemetry, ref int position)
|
||||||
|
{
|
||||||
|
if ((telemetry.Length > position + FragmentLength + StepLength) && DetectStart(telemetry, position))
|
||||||
|
{
|
||||||
|
var skip = position + StepLength;
|
||||||
|
while (telemetry.Length > skip + FragmentLength)
|
||||||
|
{
|
||||||
|
if (DetectEnd(telemetry, skip))
|
||||||
|
{
|
||||||
|
var dateStart = telemetry[position].DateTime;
|
||||||
|
var dateEnd = telemetry[skip].DateTime;
|
||||||
|
var durationSec = (dateEnd - dateStart).TotalSeconds;
|
||||||
|
if (durationSec < MinDurationSeconds || durationSec > MaxDurationSeconds)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var result = new DetectedOperation
|
||||||
|
{
|
||||||
|
IdCategory = IdCategory,
|
||||||
|
IdUsersAtStart = telemetry[position].IdUser ?? -1,
|
||||||
|
DateStart = dateStart,
|
||||||
|
DateEnd = dateEnd,
|
||||||
|
DepthStart = telemetry[position].WellDepth ?? -1d,
|
||||||
|
DepthEnd = telemetry[skip].WellDepth ?? -1d,
|
||||||
|
};
|
||||||
|
position = skip + FragmentLength;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
skip = skip + StepLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract bool DetectStart(DetectableTelemetry[] telemetry, int position);
|
||||||
|
|
||||||
|
protected abstract bool DetectEnd(DetectableTelemetry[] telemetry, int position);
|
||||||
|
|
||||||
|
}
|
||||||
|
#nullable disable
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
|
||||||
|
{
|
||||||
|
#nullable enable
|
||||||
|
class DetectorDrillingRotor : DetectorAbstract
|
||||||
|
{
|
||||||
|
public DetectorDrillingRotor() : base(1)
|
||||||
|
{
|
||||||
|
FragmentLength = 15;
|
||||||
|
StepLength = 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; //м/час
|
||||||
|
const double minRotorSpeed = 5; //об/мин
|
||||||
|
const double ticksPerHour = 60 * 60 * 10_000_000d;
|
||||||
|
|
||||||
|
var lineBlockPosition = new InterpolationLine(fragment.Select(d => (d.BlockPosition ?? 0d, d.DateTime.Ticks / ticksPerHour)));
|
||||||
|
if (!lineBlockPosition.IsYDecreases(minRop))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var lineWellDepth = new InterpolationLine(fragment.Select(d => (d.WellDepth ?? 0d, d.DateTime.Ticks / ticksPerHour)));
|
||||||
|
if(!lineWellDepth.IsYIncreases(minRop))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var lineRotorSpeed = new InterpolationLine(fragment.Select(d => (d.RotorSpeed ?? 0d, d.DateTime.Ticks / ticksPerHour)));
|
||||||
|
if (!lineRotorSpeed.IsAverageYLessThanBound(minRotorSpeed))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool DetectEnd(DetectableTelemetry[] telemetry, int position)
|
||||||
|
=> !DetectStart(telemetry, position);
|
||||||
|
|
||||||
|
}
|
||||||
|
#nullable disable
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
|
||||||
|
{
|
||||||
|
#nullable enable
|
||||||
|
class DetectorDrillingSlide : DetectorAbstract
|
||||||
|
{
|
||||||
|
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; //м/час
|
||||||
|
const double minRotorSpeed = 5; //об/мин
|
||||||
|
const double ticksPerHour = 60 * 60 * 10_000_000d;
|
||||||
|
|
||||||
|
var lineBlockPosition = new InterpolationLine(fragment.Select(d => (d.BlockPosition ?? 0d, d.DateTime.Ticks / ticksPerHour)));
|
||||||
|
if (!lineBlockPosition.IsYDecreases(minRop))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var lineWellDepth = new InterpolationLine(fragment.Select(d => (d.WellDepth ?? 0d, d.DateTime.Ticks / ticksPerHour)));
|
||||||
|
if(!lineWellDepth.IsYIncreases(minRop))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var lineRotorSpeed = new InterpolationLine(fragment.Select(d => (d.RotorSpeed ?? 0d, d.DateTime.Ticks / ticksPerHour)));
|
||||||
|
if (!lineRotorSpeed.IsAverageYMoreThanBound(minRotorSpeed))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool DetectEnd(DetectableTelemetry[] telemetry, int position)
|
||||||
|
=> !DetectStart(telemetry, position);
|
||||||
|
|
||||||
|
}
|
||||||
|
#nullable disable
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
|
||||||
|
{
|
||||||
|
#nullable enable
|
||||||
|
class DetectorSlipsTime : DetectorAbstract
|
||||||
|
{
|
||||||
|
public DetectorSlipsTime() :base(14) {}
|
||||||
|
public double HookWeightSP { get; set; } = 20;
|
||||||
|
public double PressureSP { get; set; } = 15;
|
||||||
|
public double PosisionSP { get; set; } = 8;
|
||||||
|
|
||||||
|
protected override bool DetectStart(DetectableTelemetry[] telemetry, int position)
|
||||||
|
{
|
||||||
|
var item = telemetry[position];
|
||||||
|
|
||||||
|
var result = item.HookWeight < HookWeightSP &&
|
||||||
|
item.Pressure < PressureSP &&
|
||||||
|
item.BlockPosition < PosisionSP;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool DetectEnd(DetectableTelemetry[] telemetry, int position)
|
||||||
|
{
|
||||||
|
var item = telemetry[position];
|
||||||
|
|
||||||
|
var result = item.Pressure > PressureSP &&
|
||||||
|
item.BlockPosition > PosisionSP;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#nullable disable
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace AsbCloudInfrastructure.Services.Analysis
|
namespace AsbCloudInfrastructure.Services.DetectOperations
|
||||||
{
|
{
|
||||||
public class InterpolationLine
|
public class InterpolationLine
|
||||||
{
|
{
|
||||||
@ -23,10 +23,16 @@ namespace AsbCloudInfrastructure.Services.Analysis
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// tan(alpha)
|
||||||
|
/// </summary>
|
||||||
public double A =>
|
public double A =>
|
||||||
(xSum * ySum - count * xySum) /
|
(xSum * ySum - count * xySum) /
|
||||||
(xSum * xSum - count * x2Sum);
|
(xSum * xSum - count * x2Sum);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// y offset
|
||||||
|
/// </summary>
|
||||||
public double B =>
|
public double B =>
|
||||||
(xSum * xySum - x2Sum * ySum) /
|
(xSum * xySum - x2Sum * ySum) /
|
||||||
(xSum * xSum - count * x2Sum);
|
(xSum * xSum - count * x2Sum);
|
@ -0,0 +1,199 @@
|
|||||||
|
using AsbCloudDb.Model;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace AsbCloudInfrastructure.Services.DetectOperations
|
||||||
|
{
|
||||||
|
public class OperationDetectionBackgroundService : BackgroundService
|
||||||
|
{
|
||||||
|
private readonly IEnumerable<DetectorAbstract> detectors = new List<DetectorAbstract>
|
||||||
|
{
|
||||||
|
new Detectors.DetectorSlipsTime(),
|
||||||
|
// new Detectors.DetectorDrillingRotor(),
|
||||||
|
// new Detectors.DetectorDrillingSlide(),
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly int minStepLength;
|
||||||
|
private readonly int minFragmentLength;
|
||||||
|
private readonly string connectionString;
|
||||||
|
private readonly TimeSpan period = TimeSpan.FromHours(1);
|
||||||
|
|
||||||
|
public OperationDetectionBackgroundService(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
minStepLength = detectors.Min(d => d.StepLength);
|
||||||
|
minStepLength = minStepLength > 0 ? minStepLength : 3;
|
||||||
|
|
||||||
|
minFragmentLength = detectors.Min(d => d.FragmentLength);
|
||||||
|
minFragmentLength = minFragmentLength > 0 ? minFragmentLength : 6;
|
||||||
|
|
||||||
|
connectionString = configuration.GetConnectionString("DefaultConnection");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken token = default)
|
||||||
|
{
|
||||||
|
var timeToStartAnalysis = DateTime.Now;
|
||||||
|
var options = new DbContextOptionsBuilder<AsbCloudDbContext>()
|
||||||
|
.UseNpgsql(connectionString)
|
||||||
|
.Options;
|
||||||
|
|
||||||
|
while (!token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
if (DateTime.Now > timeToStartAnalysis)
|
||||||
|
{
|
||||||
|
timeToStartAnalysis = DateTime.Now + period;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var context = new AsbCloudDbContext(options);
|
||||||
|
var added = await DetectedAllTelemetriesAsync(context, token);
|
||||||
|
Trace.TraceInformation($"Total detection complete. Added {added} operations.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Trace.TraceError(ex.Message);
|
||||||
|
Console.WriteLine(ex.Message);
|
||||||
|
}
|
||||||
|
GC.Collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
var ms = (int)(timeToStartAnalysis - DateTime.Now).TotalMilliseconds;
|
||||||
|
ms = ms > 100 ? ms : 100;
|
||||||
|
await Task.Delay(ms, token).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task StopAsync(CancellationToken token)
|
||||||
|
{
|
||||||
|
await base.StopAsync(token).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<int> DetectedAllTelemetriesAsync(IAsbCloudDbContext db, CancellationToken token)
|
||||||
|
{
|
||||||
|
var lastDetectedDates = await db.DetectedOperations
|
||||||
|
.GroupBy(o => o.IdTelemetry)
|
||||||
|
.Select(g => new
|
||||||
|
{
|
||||||
|
IdTelemetry = g.Key,
|
||||||
|
LastDate = g.Max(o => o.DateEnd)
|
||||||
|
})
|
||||||
|
.ToListAsync(token);
|
||||||
|
|
||||||
|
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,
|
||||||
|
LastDate = inner.SingleOrDefault()?.LastDate ,
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach (var item in JounedlastDetectedDates)
|
||||||
|
{
|
||||||
|
var newOperations = await DetectOperationsAsync(item.IdTelemetry, item.LastDate??DateTimeOffset.MinValue, db, token);
|
||||||
|
if (newOperations.Any())
|
||||||
|
db.DetectedOperations.AddRange(newOperations);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await db.SaveChangesAsync(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IEnumerable<DetectedOperation>> DetectOperationsAsync(int idTelemetry, DateTimeOffset begin, IAsbCloudDbContext db, CancellationToken token)
|
||||||
|
{
|
||||||
|
var query = db.TelemetryDataSaub
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(d => d.IdTelemetry == idTelemetry)
|
||||||
|
.Select(d => new DetectableTelemetry{
|
||||||
|
DateTime = d.DateTime,
|
||||||
|
IdUser = d.IdUser,
|
||||||
|
WellDepth = d.WellDepth,
|
||||||
|
Pressure = d.Pressure,
|
||||||
|
HookWeight = d.HookWeight,
|
||||||
|
BlockPosition = d.BlockPosition,
|
||||||
|
BitDepth = d.BitDepth,
|
||||||
|
RotorSpeed = d.RotorSpeed,
|
||||||
|
})
|
||||||
|
.OrderBy(d => d.DateTime);
|
||||||
|
|
||||||
|
var take = 4 * 86_400;
|
||||||
|
var startDate = begin;
|
||||||
|
var detectedOperations = new List<DetectedOperation>(8);
|
||||||
|
|
||||||
|
var dbRequests_ = 0;
|
||||||
|
var dbTime_ = 0d;
|
||||||
|
var sw_ = new System.Diagnostics.Stopwatch();
|
||||||
|
var otherTime_ = 0d;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
sw_.Restart();
|
||||||
|
var data = await query
|
||||||
|
.Where(d => d.DateTime > startDate)
|
||||||
|
.Take(take)
|
||||||
|
.ToArrayAsync(token);
|
||||||
|
|
||||||
|
sw_.Stop();
|
||||||
|
dbTime_ += sw_.ElapsedMilliseconds;
|
||||||
|
dbRequests_++;
|
||||||
|
sw_.Restart();
|
||||||
|
|
||||||
|
if (data.Length < minFragmentLength)
|
||||||
|
break;
|
||||||
|
|
||||||
|
var skip = 0;
|
||||||
|
|
||||||
|
var isDetected = false;
|
||||||
|
|
||||||
|
while (data.Length > skip + minFragmentLength)
|
||||||
|
{
|
||||||
|
var isDetected1 = false;
|
||||||
|
|
||||||
|
foreach (var detector in detectors)
|
||||||
|
{
|
||||||
|
if(data.Length < skip + detector.StepLength + detector.FragmentLength)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var detectedOperation = detector.DetectOrDefault(data, ref skip);
|
||||||
|
if (detectedOperation is not null)
|
||||||
|
{
|
||||||
|
isDetected1 = true;
|
||||||
|
isDetected = true;
|
||||||
|
detectedOperation.IdTelemetry = idTelemetry;
|
||||||
|
detectedOperations.Add(detectedOperation);
|
||||||
|
startDate = detectedOperation.DateEnd;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isDetected1)
|
||||||
|
skip += minStepLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
sw_.Stop();
|
||||||
|
otherTime_ += sw_.ElapsedMilliseconds;
|
||||||
|
|
||||||
|
if (!isDetected)
|
||||||
|
{
|
||||||
|
if (data.Length < take)
|
||||||
|
break;
|
||||||
|
|
||||||
|
var lastPartDate = data.Last().DateTime;
|
||||||
|
startDate = startDate + (0.75 * (lastPartDate - startDate));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return detectedOperations;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
# Сервис определения операций по данным телеметрии
|
||||||
|
|
||||||
|
Классы:
|
||||||
|
|
||||||
|
**DetectableTelemetry** - обертка над классами телеметрий, которые можно проанализировать.
|
@ -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,205 +0,0 @@
|
|||||||
using AsbCloudApp.Data;
|
|
||||||
using AsbCloudApp.Services;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace AsbCloudWebApi.Controllers.SAUB
|
|
||||||
{
|
|
||||||
[Route("api/well/{idWell}/telemetryAnalytics")]
|
|
||||||
[ApiController]
|
|
||||||
[Authorize]
|
|
||||||
public class TelemetryAnalyticsController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly ITelemetryAnalyticsService analyticsService;
|
|
||||||
private readonly IWellService wellService;
|
|
||||||
|
|
||||||
public TelemetryAnalyticsController(ITelemetryAnalyticsService analyticsService, IWellService wellService)
|
|
||||||
{
|
|
||||||
this.analyticsService = analyticsService;
|
|
||||||
this.wellService = wellService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Возвращает список операций на скважине за все время
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="idWell">id скважины</param>
|
|
||||||
/// <param name="categoryIds">список категорий</param>
|
|
||||||
/// <param name="begin">дата начала</param>
|
|
||||||
/// <param name="end">окончание</param>
|
|
||||||
/// <param name="skip">для пагинации кол-во записей пропустить</param>
|
|
||||||
/// <param name="take">для пагинации кол-во записей </param>
|
|
||||||
/// <param name="token"> Токен отмены задачи </param>
|
|
||||||
/// <returns>Список операций на скважине за все время</returns>
|
|
||||||
[HttpGet]
|
|
||||||
[Route("operationsByWell")]
|
|
||||||
[Permission]
|
|
||||||
[ProducesResponseType(typeof(PaginationContainer<TelemetryOperationDto>), (int)System.Net.HttpStatusCode.OK)]
|
|
||||||
public async Task<IActionResult> GetOperationsByWellAsync(int idWell, int skip = 0, int take = 32,
|
|
||||||
[FromQuery] IEnumerable<int> categoryIds = default, DateTime begin = default, DateTime end = default,
|
|
||||||
CancellationToken token = default)
|
|
||||||
{
|
|
||||||
int? idCompany = User.GetCompanyId();
|
|
||||||
|
|
||||||
if (idCompany is null || !await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
|
|
||||||
idWell, token).ConfigureAwait(false))
|
|
||||||
return Forbid();
|
|
||||||
|
|
||||||
var analytics = await analyticsService.GetOperationsByWellAsync(idWell, categoryIds, begin, end, skip, take, token)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (analytics is null || analytics.Count == 0)
|
|
||||||
return NoContent();
|
|
||||||
|
|
||||||
return Ok(analytics);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Возвращает данные по скважине "глубина-день"
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="idWell">id скважины</param>
|
|
||||||
/// <param name="token">Токен отмены задачи</param>
|
|
||||||
/// <returns>Коллекцию данных по скважине "глубина-день"</returns>
|
|
||||||
[HttpGet]
|
|
||||||
[Route("wellDepthToDay")]
|
|
||||||
[Permission]
|
|
||||||
[ProducesResponseType(typeof(IEnumerable<WellDepthToDayDto>), (int)System.Net.HttpStatusCode.OK)]
|
|
||||||
public async Task<IActionResult> GetWellDepthToDayAsync(int idWell,
|
|
||||||
CancellationToken token = default)
|
|
||||||
{
|
|
||||||
int? idCompany = User.GetCompanyId();
|
|
||||||
|
|
||||||
if (idCompany is null || !await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
|
|
||||||
idWell, token).ConfigureAwait(false))
|
|
||||||
return Forbid();
|
|
||||||
|
|
||||||
var wellDepthToDayData = await analyticsService.GetWellDepthToDayAsync(idWell, token)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (wellDepthToDayData is null || !wellDepthToDayData.Any())
|
|
||||||
return NoContent();
|
|
||||||
|
|
||||||
return Ok(wellDepthToDayData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Возвращает данные по глубине скважины за период
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="idWell">id скважины</param>
|
|
||||||
/// <param name="intervalSeconds">количество секунд в необходимом интервале времени</param>
|
|
||||||
/// <param name="shiftStartSec">количество секунд в времени начала смены</param>
|
|
||||||
/// <param name="token">Токен отмены задачи</param>
|
|
||||||
/// <returns>Коллекцию данных по глубине скважины за период</returns>
|
|
||||||
[HttpGet]
|
|
||||||
[Route("wellDepthToInterval")]
|
|
||||||
[Permission]
|
|
||||||
[ProducesResponseType(typeof(IEnumerable<WellDepthToIntervalDto>), (int)System.Net.HttpStatusCode.OK)]
|
|
||||||
public async Task<IActionResult> GetWellDepthToIntervalAsync(int idWell,
|
|
||||||
int intervalSeconds, int shiftStartSec, CancellationToken token = default)
|
|
||||||
{
|
|
||||||
int? idCompany = User.GetCompanyId();
|
|
||||||
|
|
||||||
if (idCompany is null || !await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
|
|
||||||
idWell, token).ConfigureAwait(false))
|
|
||||||
return Forbid();
|
|
||||||
|
|
||||||
var wellDepthToIntervalData = await analyticsService.GetWellDepthToIntervalAsync(idWell,
|
|
||||||
intervalSeconds, shiftStartSec, token).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (wellDepthToIntervalData is null || !wellDepthToIntervalData.Any())
|
|
||||||
return NoContent();
|
|
||||||
|
|
||||||
return Ok(wellDepthToIntervalData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Возвращает данные по операциям на скважине "операции-время"
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="idWell">id скважины</param>
|
|
||||||
/// <param name="begin">дата начала интервала</param>
|
|
||||||
/// <param name="end">дата окончания интервала</param>
|
|
||||||
/// <param name="token">Токен отмены задачи</param>
|
|
||||||
/// <returns>Коллекцию операций на скважине</returns>
|
|
||||||
[HttpGet]
|
|
||||||
[Route("operationsSummary")]
|
|
||||||
[Permission]
|
|
||||||
[ProducesResponseType(typeof(IEnumerable<TelemetryOperationDurationDto>), (int)System.Net.HttpStatusCode.OK)]
|
|
||||||
public async Task<IActionResult> GetOperationsSummaryAsync(int idWell, DateTime begin = default,
|
|
||||||
DateTime end = default, CancellationToken token = default)
|
|
||||||
{
|
|
||||||
int? idCompany = User.GetCompanyId();
|
|
||||||
|
|
||||||
if (idCompany is null || !await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
|
|
||||||
idWell, token).ConfigureAwait(false))
|
|
||||||
return Forbid();
|
|
||||||
|
|
||||||
var analytics = await analyticsService.GetOperationsSummaryAsync(idWell,
|
|
||||||
begin, end, token).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (analytics is null || !analytics.Any())
|
|
||||||
return NoContent();
|
|
||||||
|
|
||||||
return Ok(analytics);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Возвращает детальные данные по операциям на скважине за период
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="idWell">id скважины</param>
|
|
||||||
/// <param name="intervalSeconds">количество секунд в необходимом интервале времени</param>
|
|
||||||
/// <param name="workBeginSeconds">количество секунд в времени начала смены</param>
|
|
||||||
/// <param name="token">Токен отмены задачи</param>
|
|
||||||
/// <returns>Коллекцию операций на скважине</returns>
|
|
||||||
[HttpGet]
|
|
||||||
[Route("operationsToInterval")]
|
|
||||||
[Permission]
|
|
||||||
[ProducesResponseType(typeof(IEnumerable<TelemetryOperationDurationDto>), (int)System.Net.HttpStatusCode.OK)]
|
|
||||||
public async Task<IActionResult> GetOperationsToIntervalAsync(int idWell,
|
|
||||||
int intervalSeconds, int workBeginSeconds, CancellationToken token = default)
|
|
||||||
{
|
|
||||||
int? idCompany = User.GetCompanyId();
|
|
||||||
|
|
||||||
if (idCompany is null || !await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
|
|
||||||
idWell, token).ConfigureAwait(false))
|
|
||||||
return Forbid();
|
|
||||||
|
|
||||||
var analytics = await analyticsService.GetOperationsToIntervalAsync(idWell,
|
|
||||||
intervalSeconds, workBeginSeconds, token).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (analytics is null || !analytics.Any())
|
|
||||||
return NoContent();
|
|
||||||
|
|
||||||
return Ok(analytics);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Возвращает даты первой и последней операций на скважине
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="idWell">id скважины</param>
|
|
||||||
/// <param name="token">Токен для отмены задачи</param>
|
|
||||||
/// <returns>Даты самой первой и самой последней операций на скважине</returns>
|
|
||||||
[HttpGet]
|
|
||||||
[Route("datesRange")]
|
|
||||||
[Permission]
|
|
||||||
[ProducesResponseType(typeof(DatesRangeDto), (int)System.Net.HttpStatusCode.OK)]
|
|
||||||
public async Task<IActionResult> GetOperationsDateRangeAsync(int idWell, CancellationToken token = default)
|
|
||||||
{
|
|
||||||
int? idCompany = User.GetCompanyId();
|
|
||||||
|
|
||||||
if (idCompany is null)
|
|
||||||
return Forbid();
|
|
||||||
|
|
||||||
if (!await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
|
|
||||||
idWell, token).ConfigureAwait(false))
|
|
||||||
return Forbid();
|
|
||||||
|
|
||||||
var wellOperationsDatesRange = await analyticsService.GetOperationsDateRangeAsync(idWell, token).ConfigureAwait(false);
|
|
||||||
|
|
||||||
return Ok(wellOperationsDatesRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +1,9 @@
|
|||||||
using AsbCloudInfrastructure.Services.DailyReport;
|
using AsbCloudInfrastructure.Services.DailyReport;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace ConsoleApp1
|
namespace ConsoleApp1
|
||||||
{
|
{
|
||||||
|
|
||||||
class Program
|
class Program
|
||||||
{
|
{
|
||||||
static void Main(/*string[] args*/)
|
static void Main(/*string[] args*/)
|
||||||
|
@ -50,8 +50,14 @@ namespace ConsoleApp1
|
|||||||
static TimezoneService TimezoneService { get; } = new TimezoneService();
|
static TimezoneService TimezoneService { get; } = new TimezoneService();
|
||||||
public static AsbCloudDbContext Context { get; } = MakeContext();
|
public static AsbCloudDbContext Context { get; } = MakeContext();
|
||||||
public static AsbCloudDbContext MakeContext()
|
public static AsbCloudDbContext MakeContext()
|
||||||
|
=> MakeContext(options);
|
||||||
|
|
||||||
|
public static AsbCloudDbContext MakeContext(DbContextOptions<AsbCloudDbContext> options)
|
||||||
=> new AsbCloudDbContext(options);
|
=> new AsbCloudDbContext(options);
|
||||||
|
|
||||||
|
public static AsbCloudDbContext MakeContext(string cusomConnectionString)
|
||||||
|
=> MakeContext(new DbContextOptionsBuilder<AsbCloudDbContext>().UseNpgsql(cusomConnectionString).Options);
|
||||||
|
|
||||||
public static void MapsterSetup()
|
public static void MapsterSetup()
|
||||||
=> AsbCloudInfrastructure.DependencyInjection.MapsterSetup();
|
=> AsbCloudInfrastructure.DependencyInjection.MapsterSetup();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user