diff --git a/AsbCloudApp/Data/DetectedOperation/DetectedOperationDto.cs b/AsbCloudApp/Data/DetectedOperation/DetectedOperationDto.cs
index 0386e8b3..2b0fc768 100644
--- a/AsbCloudApp/Data/DetectedOperation/DetectedOperationDto.cs
+++ b/AsbCloudApp/Data/DetectedOperation/DetectedOperationDto.cs
@@ -1,100 +1,85 @@
using System;
using System.ComponentModel.DataAnnotations;
-namespace AsbCloudApp.Data.DetectedOperation
+namespace AsbCloudApp.Data.DetectedOperation;
+
+///
+/// Автоматически определенная операция
+///
+public class DetectedOperationDto: IId
{
+ ///
+ [Required]
+ public int Id { get; set; }
+
///
- /// Автоматически определяемая операция
+ /// Id телеметрии
///
- public class DetectedOperationDto : IId, IWellRelated
- {
- ///
- [Required]
- public int Id { get; set; }
+ [Required]
+ public int IdTelemetry { get; set; }
- ///
- [Required]
- public int IdWell { get; set; }
+ ///
+ /// Id названия/описания операции
+ ///
+ [Required]
+ public int IdCategory { get; set; }
- ///
- /// Id телеметрии
- ///
- [Required]
- public int IdTelemetry { get; set; }
+ ///
+ /// Id пользователя панели на момент начала операции
+ ///
+ [Required]
+ public int IdUserAtStart { get; set; }
- ///
- /// Id названия/описания операции
- ///
- [Required]
- public int IdCategory { get; set; }
+ ///
+ /// Дата завершения операции в часовом поясе скважины
+ ///
+ [Required]
+ public DateTimeOffset DateEnd { get; set; }
- ///
- /// Id пользователя панели
- ///
- [Required]
- public int IdUsersAtStart { get; set; }
+ ///
+ /// Дата начала операции в часовом поясе скважины
+ ///
+ [Required]
+ public DateTimeOffset DateStart { get; set; }
- ///
- /// Дата начала операции в часовом поясе скважины
- ///
- [Required]
- public DateTime DateStart { get; set; }
+ ///
+ /// глубина на завершения операции, м
+ ///
+ [Required]
+ public double DepthEnd { get; set; }
- ///
- /// Дата завершения операции в часовом поясе скважины
- ///
- [Required]
- public DateTime DateEnd { get; set; }
+ ///
+ /// глубина на начало операции, м
+ ///
+ [Required]
+ public double DepthStart { get; set; }
- ///
- /// Продолжительность операции в минутах
- ///
- [Required]
- public double DurationMinutes => (DateEnd - DateStart).TotalMinutes;
+ ///
+ /// Продолжительность операции в минутах
+ ///
+ [Required]
+ public double DurationMinutes => (DateEnd - DateStart).TotalMinutes;
- ///
- /// глубина на начало операции, м
- ///
- [Required]
- public double DepthStart { get; set; }
+ ///
+ /// Флаг включенной подсистемы
+ ///
+ [Required]
+ public int EnabledSubsystems { get; set; }
- ///
- /// глубина на завершения операции, м
- ///
- [Required]
- public double DepthEnd { get; set; }
+ ///
+ /// название/описание операции
+ ///
+ [Required]
+ public WellOperationCategoryDto OperationCategory { get; set; } = null!;
- ///
- /// название/описание операции
- ///
- [Required]
- public WellOperationCategoryDto OperationCategory { get; set; } = null!;
+ ///
+ /// Пользователь панели оператора
+ ///
+ public string? TelemetryUserName { get; set; }
- ///
- /// Пользователь панели оператора
- ///
- public string? TelemetryUserName { get; set; }
-
- ///
- /// Бурильщик
- ///
- public DrillerDto? Driller { get; set; }
-
- ///
- /// Целевые/нормативные показатели
- ///
- public OperationValueDto? OperationValue { get; set; }
-
- ///
- /// Ключевой параметр операции
- ///
- [Required]
- public double Value { get; set; }
-
- ///
- /// Флаг включенной подсистемы
- ///
- [Required]
- public int EnabledSubsystems { get; set; }
- }
-}
+ ///
+ /// Ключевой параметр операции
+ ///
+ [Required]
+ public double Value { get; set; }
+}
\ No newline at end of file
diff --git a/AsbCloudApp/Data/DetectedOperation/DetectedOperationListDto.cs b/AsbCloudApp/Data/DetectedOperation/DetectedOperationListDto.cs
index fef979bb..32dd6040 100644
--- a/AsbCloudApp/Data/DetectedOperation/DetectedOperationListDto.cs
+++ b/AsbCloudApp/Data/DetectedOperation/DetectedOperationListDto.cs
@@ -13,7 +13,7 @@ namespace AsbCloudApp.Data.DetectedOperation
/// Список всех операций
///
[Required]
- public IEnumerable Operations { get; set; } = Enumerable.Empty();
+ public IEnumerable Operations { get; set; } = Enumerable.Empty();
///
/// Статистика по бурильщикам
diff --git a/AsbCloudApp/Data/DetectedOperation/DetectedOperationWithDrillerDto.cs b/AsbCloudApp/Data/DetectedOperation/DetectedOperationWithDrillerDto.cs
new file mode 100644
index 00000000..612b079c
--- /dev/null
+++ b/AsbCloudApp/Data/DetectedOperation/DetectedOperationWithDrillerDto.cs
@@ -0,0 +1,18 @@
+namespace AsbCloudApp.Data.DetectedOperation
+{
+ ///
+ /// Автоматически определяемая операция
+ ///
+ public class DetectedOperationWithDrillerDto : DetectedOperationDto
+ {
+ ///
+ /// Бурильщик
+ ///
+ public DrillerDto? Driller { get; set; }
+
+ ///
+ /// Целевые/нормативные показатели
+ ///
+ public OperationValueDto? OperationValue { get; set; }
+ }
+}
diff --git a/AsbCloudApp/Data/SimpleTimezoneDto.cs b/AsbCloudApp/Data/SimpleTimezoneDto.cs
index 3ec75814..5d2e1f8f 100644
--- a/AsbCloudApp/Data/SimpleTimezoneDto.cs
+++ b/AsbCloudApp/Data/SimpleTimezoneDto.cs
@@ -1,25 +1,32 @@
+using System;
+
namespace AsbCloudApp.Data
{
///
- ///
+ /// временная зона
///
public class SimpleTimezoneDto
{
///
- /// UTC
+ /// смещение в часах относительно UTC
///
public double Hours { get; set; }
///
- ///
+ /// идентификатор часовой зоны
///
public string? TimezoneId { get; set; }
///
- ///
+ /// запрет на переопределение
///
public bool IsOverride { get; set; }
+ ///
+ /// Смещение часового пояса
+ ///
+ public TimeSpan Offset => TimeSpan.FromHours(Hours);
+
///
public override bool Equals(object? obj)
{
diff --git a/AsbCloudApp/Data/TimeDto.cs b/AsbCloudApp/Data/TimeDto.cs
index 6a3620f2..20ffa4ac 100644
--- a/AsbCloudApp/Data/TimeDto.cs
+++ b/AsbCloudApp/Data/TimeDto.cs
@@ -86,6 +86,14 @@ namespace AsbCloudApp.Data
second = fullDate.Second;
}
+ ///
+ public TimeDto(DateTimeOffset fullDate)
+ {
+ hour = fullDate.Hour;
+ minute = fullDate.Minute;
+ second = fullDate.Second;
+ }
+
///
/// Makes System.TimeOnly
///
diff --git a/AsbCloudApp/Exceptions/ArgumentInvalidException.cs b/AsbCloudApp/Exceptions/ArgumentInvalidException.cs
index 5db63313..641f18ce 100644
--- a/AsbCloudApp/Exceptions/ArgumentInvalidException.cs
+++ b/AsbCloudApp/Exceptions/ArgumentInvalidException.cs
@@ -14,6 +14,7 @@ namespace AsbCloudApp.Exceptions
///
public IDictionary ErrorState { get; } = null!;
+ // TODO: swap arguments, inherit from ArgumentException
///
/// конструктор
///
diff --git a/AsbCloudApp/Repositories/IDetectedOperationRepository.cs b/AsbCloudApp/Repositories/IDetectedOperationRepository.cs
new file mode 100644
index 00000000..5a52b455
--- /dev/null
+++ b/AsbCloudApp/Repositories/IDetectedOperationRepository.cs
@@ -0,0 +1,66 @@
+using AsbCloudApp.Data.DetectedOperation;
+using AsbCloudApp.Requests;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using System.Threading;
+
+namespace AsbCloudApp.Repositories;
+
+///
+/// Таблица автоматически определенных операций
+///
+public interface IDetectedOperationRepository
+{
+ ///
+ /// Добавление записей
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task Insert(int? idUser, IEnumerable dtos, CancellationToken token);
+
+ ///
+ /// Получить автоматически определенные операции по телеметрии
+ ///
+ ///
+ ///
+ ///
+ Task> Get(DetectedOperationByTelemetryRequest request, CancellationToken token);
+
+ ///
+ /// Редактирование записей
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task Update(int idUser, IEnumerable dtos, CancellationToken token);
+
+ ///
+ /// Добавляет Dto у которых id == 0, изменяет dto у которых id != 0
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task UpdateOrInsert(int idUser, IEnumerable dtos, CancellationToken token);
+
+ ///
+ /// Удалить операции
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task Delete(int idUser, DetectedOperationByTelemetryRequest request, CancellationToken token);
+
+ ///
+ /// Удаление записей
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task DeleteRange(int idUser, IEnumerable ids, CancellationToken token);
+}
diff --git a/AsbCloudApp/Repositories/IWellOperationCategoryRepository.cs b/AsbCloudApp/Repositories/IWellOperationCategoryRepository.cs
new file mode 100644
index 00000000..4e18c231
--- /dev/null
+++ b/AsbCloudApp/Repositories/IWellOperationCategoryRepository.cs
@@ -0,0 +1,17 @@
+using AsbCloudApp.Data;
+using System.Collections.Generic;
+
+namespace AsbCloudApp.Repositories
+{
+ ///
+ /// сервис операций по скважине
+ ///
+ public interface IWellOperationCategoryRepository
+ {
+ ///
+ /// список названий операций
+ ///
+ ///
+ IEnumerable Get(bool includeParents);
+ }
+}
\ No newline at end of file
diff --git a/AsbCloudApp/Repositories/IWellOperationRepository.cs b/AsbCloudApp/Repositories/IWellOperationRepository.cs
index 8cb313ac..ff3bfb8e 100644
--- a/AsbCloudApp/Repositories/IWellOperationRepository.cs
+++ b/AsbCloudApp/Repositories/IWellOperationRepository.cs
@@ -13,10 +13,12 @@ namespace AsbCloudApp.Repositories
///
public interface IWellOperationRepository
{
+ //TODO: replace all references
///
/// список названий операций
///
///
+ [Obsolete("use IWellOperationCategoryRepository.GetCategories(bool includeParents)")]
IEnumerable GetCategories(bool includeParents);
///
diff --git a/AsbCloudApp/Requests/DetectedOperationRequest.cs b/AsbCloudApp/Requests/DetectedOperationRequest.cs
index dcf66aab..243d6e9f 100644
--- a/AsbCloudApp/Requests/DetectedOperationRequest.cs
+++ b/AsbCloudApp/Requests/DetectedOperationRequest.cs
@@ -1,56 +1,114 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
-using System.Linq;
-namespace AsbCloudApp.Requests
+namespace AsbCloudApp.Requests;
+
+///
+/// Запрос на получение операций определенных по телеметрии
+///
+public class DetectedOperationByTelemetryRequest : DetectedOperationRequest
{
///
- /// Параметры запроса на получение операций определенных по телеметрии
+ /// id телеметрии
///
- public class DetectedOperationRequest : RequestBase
+ [Required]
+ public int IdTelemetry { get; set; }
+
+ ///
+ /// Запрос на получение операций определенных по id телеметрии
+ ///
+ public DetectedOperationByTelemetryRequest()
+ {}
+
+ ///
+ /// Запрос на получение операций определенных по id телеметрии. Copy
+ ///
+ ///
+ ///
+ public DetectedOperationByTelemetryRequest(int idTelemetry, DetectedOperationRequest request)
+ :base(request)
{
- ///
- /// категория операций
- ///
- [Required]
- public int IdWell { get; set; }
-
- ///
- /// Список id телеметрий
- /// пустой список - нет фильтрации
- ///
- public IEnumerable IdsTelemetries { get; set; } = Array.Empty();
-
- ///
- /// категории операций
- ///
- public IEnumerable IdsCategories { get; set; } = Array.Empty();
-
- ///
- /// Больше или равно дате
- ///
- public DateTimeOffset? GeDateStart { get; set; }
-
- ///
- /// Меньше или равно дате
- ///
- public DateTimeOffset? LeDateEnd { get; set; }
-
- ///
- /// Больше или равно глубины забоя
- ///
- public double? GeDepth { get; set; }
-
- ///
- /// Меньше или равно глубины забоя
- ///
- public double? LeDepth { get; set; }
-
- ///
- /// Фильтр по пользователю панели
- ///
- public int? IdTelemetryUser { get; set; }
-
+ IdTelemetry = idTelemetry;
+ }
+}
+
+///
+/// Запрос на получение операций определенных по id скважины
+///
+public class DetectedOperationByWellRequest : DetectedOperationRequest
+{
+ ///
+ /// id скважины
+ ///
+ [Required]
+ public int IdWell { get; set; }
+
+ ///
+ /// Запрос на получение операций определенных по id скважины
+ ///
+ public DetectedOperationByWellRequest()
+ {}
+
+ ///
+ /// Запрос на получение операций определенных по id скважины. Copy
+ ///
+ public DetectedOperationByWellRequest(int idWell, DetectedOperationRequest request)
+ : base(request)
+ {
+ IdWell = idWell;
+ }
+}
+
+///
+/// Запрос на получение операций определенных по телеметрии
+///
+public class DetectedOperationRequest : RequestBase
+{
+ ///
+ /// категории операций
+ ///
+ public IEnumerable IdsCategories { get; set; }
+
+ ///
+ /// Больше или равно дате
+ ///
+ public DateTimeOffset? GeDateStart { get; set; }
+
+ ///
+ /// Меньше или равно дате
+ ///
+ public DateTimeOffset? LeDateEnd { get; set; }
+
+ ///
+ /// Больше или равно глубины забоя
+ ///
+ public double? GeDepthStart { get; set; }
+
+ ///
+ /// Меньше или равно глубины забоя
+ ///
+ public double? LeDepthEnd { get; set; }
+
+ ///
+ /// Запрос на получение операций определенных по телеметрии
+ ///
+ public DetectedOperationRequest()
+ {
+ IdsCategories = new List();
+ }
+
+ ///
+ /// Запрос на получение операций определенных по телеметрии. Copy
+ ///
+ ///
+ public DetectedOperationRequest(DetectedOperationRequest request)
+ : base(request)
+ {
+ IdsCategories = request.IdsCategories;
+ GeDateStart = request.GeDateStart;
+ LeDateEnd = request.LeDateEnd;
+ GeDepthStart = request.GeDepthStart;
+ LeDepthEnd = request.LeDepthEnd;
}
}
diff --git a/AsbCloudApp/Requests/RequestBase.cs b/AsbCloudApp/Requests/RequestBase.cs
index 2ae96e54..c5d70a68 100644
--- a/AsbCloudApp/Requests/RequestBase.cs
+++ b/AsbCloudApp/Requests/RequestBase.cs
@@ -23,5 +23,23 @@ namespace AsbCloudApp.Requests
/// Указать направление сортировки можно через пробел "asc" или "desc"
///
public IEnumerable? SortFields { get; set; }
+
+ ///
+ /// Базовые параметры запроса
+ ///
+ public RequestBase()
+ {
+ }
+
+ ///
+ /// Базовые параметры запроса. Копирующий конструктор
+ ///
+ ///
+ public RequestBase(RequestBase request)
+ {
+ Skip = request.Skip;
+ Take = request.Take;
+ SortFields = request.SortFields;
+ }
}
}
diff --git a/AsbCloudApp/Services/IDetectedOperationService.cs b/AsbCloudApp/Services/IDetectedOperationService.cs
index e4f8f201..5109fa56 100644
--- a/AsbCloudApp/Services/IDetectedOperationService.cs
+++ b/AsbCloudApp/Services/IDetectedOperationService.cs
@@ -19,7 +19,7 @@ namespace AsbCloudApp.Services
///
///
///
- Task?> GetCategoriesAsync(int? idWell, CancellationToken token);
+ Task> GetCategoriesAsync(int? idWell, CancellationToken token);
///
/// Получить автоматически определенные по телеметрии операции с анализом по бурильщикам
@@ -27,7 +27,7 @@ namespace AsbCloudApp.Services
///
///
///
- Task GetAsync(DetectedOperationRequest request, CancellationToken token);
+ Task GetAsync(DetectedOperationByWellRequest request, CancellationToken token);
///
/// Получить автоматически определенные по телеметрии операции
@@ -35,7 +35,7 @@ namespace AsbCloudApp.Services
///
///
///
- Task> GetOperationsAsync(DetectedOperationRequest request, CancellationToken token);
+ Task> GetOperationsAsync(DetectedOperationByWellRequest request, CancellationToken token);
///
/// Удалить операции
@@ -43,7 +43,7 @@ namespace AsbCloudApp.Services
///
///
///
- Task DeleteAsync(DetectedOperationRequest request, CancellationToken token);
+ Task DeleteAsync(DetectedOperationByWellRequest request, CancellationToken token);
///
/// Статистика по операциям
@@ -51,6 +51,6 @@ namespace AsbCloudApp.Services
///
///
///
- Task?> GetOperationsStatAsync(DetectedOperationRequest request, CancellationToken token);
+ Task> GetOperationsStatAsync(DetectedOperationByWellRequest request, CancellationToken token);
}
}
diff --git a/AsbCloudApp/Services/ITelemetryDataSaubService.cs b/AsbCloudApp/Services/ITelemetryDataSaubService.cs
index a0bb271d..e13240c0 100644
--- a/AsbCloudApp/Services/ITelemetryDataSaubService.cs
+++ b/AsbCloudApp/Services/ITelemetryDataSaubService.cs
@@ -12,6 +12,18 @@ namespace AsbCloudApp.Services
///
public interface ITelemetryDataSaubService : ITelemetryDataService
{
+ ///
+ /// Получение телеметрии для РТК статистики
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task> Get(int idTelemetry, bool isBitOnBottom, DateTimeOffset geDate, DateTimeOffset leDate, int take, CancellationToken token);
+
///
/// усредненная статистика по 1м за весь период
///
diff --git a/AsbCloudDb/EFExtensionsExceptionHandling.cs b/AsbCloudDb/EFExtensionsExceptionHandling.cs
new file mode 100644
index 00000000..aa1996b5
--- /dev/null
+++ b/AsbCloudDb/EFExtensionsExceptionHandling.cs
@@ -0,0 +1,34 @@
+using AsbCloudDb.Model;
+using Microsoft.EntityFrameworkCore;
+using Npgsql;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace AsbCloudDb
+{
+ public static class EFExtensionsExceptionHandling
+ {
+ public static async Task SaveChangesWithExceptionHandling(this IAsbCloudDbContext db, CancellationToken token)
+ {
+ try
+ {
+ var result = await db.SaveChangesAsync(token);
+ return result;
+ }
+ catch (DbUpdateException ex)
+ {
+ if (ex.InnerException is PostgresException pgException)
+ TryConvertPostgresExceptionToValidateException(pgException);
+ throw;
+ }
+ }
+
+ private static void TryConvertPostgresExceptionToValidateException(PostgresException pgException)
+ {
+ if (pgException.SqlState == PostgresErrorCodes.ForeignKeyViolation)
+ // TODO: replace ArgumentException by new Exception
+ throw new ArgumentException(pgException.Message + "\r\n" + pgException.Detail, "dtos");
+ }
+ }
+}
diff --git a/AsbCloudDb/EFExtentionOrderBy.cs b/AsbCloudDb/EFExtensionsSortBy.cs
similarity index 99%
rename from AsbCloudDb/EFExtentionOrderBy.cs
rename to AsbCloudDb/EFExtensionsSortBy.cs
index 0b46c29b..933281c7 100644
--- a/AsbCloudDb/EFExtentionOrderBy.cs
+++ b/AsbCloudDb/EFExtensionsSortBy.cs
@@ -7,7 +7,7 @@ using System.Reflection;
namespace AsbCloudDb
{
- public static class EFExtentionsSortBy
+ public static class EFExtensionsSortBy
{
struct TypeAcessor
{
diff --git a/AsbCloudInfrastructure/Background/PeriodicWorks/WorkDataSaubStat.cs b/AsbCloudInfrastructure/Background/PeriodicWorks/WorkDataSaubStat.cs
index bc942353..d7daf9ad 100644
--- a/AsbCloudInfrastructure/Background/PeriodicWorks/WorkDataSaubStat.cs
+++ b/AsbCloudInfrastructure/Background/PeriodicWorks/WorkDataSaubStat.cs
@@ -1,10 +1,11 @@
using AsbCloudApp.Data;
+using AsbCloudApp.Data.DetectedOperation;
using AsbCloudApp.Data.SAUB;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
+using AsbCloudApp.Services;
using AsbCloudDb.Model;
-using AsbCloudInfrastructure.Services.DetectOperations.Detectors;
-using Microsoft.EntityFrameworkCore;
+using AsbCloudInfrastructure.Services.DetectOperations;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
@@ -19,7 +20,6 @@ namespace AsbCloudInfrastructure.Background.PeriodicWorks
///
internal class WorkDataSaubStat : Work
{
- private int MechanicalDrillingCategoryId = 4001;
private int Gap = 60;
public WorkDataSaubStat() : base("Generate DataSaubStat entries and save them into Db")
@@ -29,7 +29,6 @@ namespace AsbCloudInfrastructure.Background.PeriodicWorks
protected override async Task Action(string id, IServiceProvider services, Action onProgressCallback, CancellationToken token)
{
- using var db = services.GetRequiredService();
var telemetryDataCache = services.GetRequiredService>();
@@ -43,96 +42,102 @@ namespace AsbCloudInfrastructure.Background.PeriodicWorks
return;
var dataSaubStatRepo = services.GetRequiredService();
+ var dataSaubService = services.GetRequiredService();
+ var detectedOperationRepository = services.GetRequiredService();
var stats = await dataSaubStatRepo.GetLastsAsync(idTelemetries, token);
for( var i =0; i < idTelemetries.Length; i++)
{
var idTelemetry = idTelemetries[i];
- var lastDate = stats.FirstOrDefault(s => s.IdTelemetry == idTelemetry)?.DateEnd ?? DateTimeOffset.UnixEpoch;
- var statsCount = await CreateStatForTelemetryFromDate(db, idTelemetry, lastDate, dataSaubStatRepo, token);
+ var lastDate = stats.FirstOrDefault(s => s.IdTelemetry == idTelemetry)?.DateEnd.ToUniversalTime() ?? DateTimeOffset.UnixEpoch;
+ var statsCount = await CreateStatForTelemetryFromDate(idTelemetry, lastDate, dataSaubService, dataSaubStatRepo, detectedOperationRepository, token);
onProgressCallback($"Calculate stat for telemetry: {idTelemetry}; from {lastDate}; results count: {statsCount};", 100*i / idTelemetries.Length);
}
}
- private async Task CreateStatForTelemetryFromDate(IAsbCloudDbContext db, int idTelemetry, DateTimeOffset begin, IDataSaubStatRepository dataSaubStatRepo, CancellationToken token)
+ private static async Task CreateStatForTelemetryFromDate(
+ int idTelemetry,
+ DateTimeOffset begin,
+ ITelemetryDataSaubService dataSaubService,
+ IDataSaubStatRepository dataSaubStatRepo,
+ IDetectedOperationRepository detectedOperationRepository,
+ CancellationToken token)
{
- var detectedOperations = await db.Set()
- .Where(o => o.IdTelemetry == idTelemetry)
- .Where(o => o.DateStart > begin)
- .Where(o => o.OperationCategory.IdParent == MechanicalDrillingCategoryId)
- .OrderBy(o => o.DateStart)
- .Take(250)
- .ToArrayAsync(token);
+ var detectedOperationRequest = new DetectedOperationByTelemetryRequest {
+ GeDateStart = begin,
+ IdTelemetry = idTelemetry,
+ IdsCategories = WellOperationCategory.MechanicalDrillingSubIds,
+ SortFields = new[] {nameof(DetectedOperation.DateStart) },
+ Take = 250,
+ };
+
+ var detectedOperations = await detectedOperationRepository.Get(detectedOperationRequest, token);
if (!detectedOperations.Any())
return 0;
- var minDate = detectedOperations.First().DateStart;
- var maxDate = detectedOperations.OrderByDescending(d => d.DateEnd).First().DateEnd;
+ var geDate = detectedOperations.First().DateStart;
+ var leDate = detectedOperations.OrderByDescending(d => d.DateEnd).First().DateEnd;
- var telemetryDataSaub = await db.Set()
- .Where(t => t.IdTelemetry == idTelemetry)
- .Where(t => t.DateTime >= minDate)
- .Where(t => t.DateTime <= maxDate)
- .Where(t => Math.Abs(t.BitDepth - t.WellDepth) < 0.0001)
- .OrderBy(t => t.DateTime)
- .Take(100_000)
- .ToArrayAsync(token);
+ var dataSaub = await dataSaubService.Get(idTelemetry, true, geDate, leDate, 100_000, token);
- if (!telemetryDataSaub.Any())
+ if (!dataSaub.Any())
return 0;
- var dataSaubStats = CreateDataSaubStat(detectedOperations, telemetryDataSaub);
+ if(dataSaub is not TelemetryDataSaubDto[] dataSaubArray)
+ dataSaubArray = dataSaub.ToArray();
+
+ var dataSaubStats = CreateDataSaubStat(detectedOperations, dataSaubArray);
return await dataSaubStatRepo.InsertRangeAsync(dataSaubStats, token);
}
- private static IEnumerable CreateDataSaubStat(IEnumerable detectedOperations, TelemetryDataSaub[] telemetryDataSaub)
+ private static IEnumerable CreateDataSaubStat(IEnumerable detectedOperations, TelemetryDataSaubDto[] dataSaub)
{
var indexStart = 0;
var indexEnd = 0;
var result = new List();
- if (!telemetryDataSaub.Any())
+ if (!dataSaub.Any())
return result;
foreach (var operation in detectedOperations)
{
- indexStart = Array.FindIndex(telemetryDataSaub, indexEnd, t => t.DateTime >= operation.DateStart);
+ indexStart = Array.FindIndex(dataSaub, indexEnd, t => t.DateTime >= operation.DateStart);
if (indexStart < 0)
break;
- indexEnd = Array.FindIndex(telemetryDataSaub, indexStart, t => t.DateTime > operation.DateEnd);
+ indexEnd = Array.FindIndex(dataSaub, indexStart, t => t.DateTime > operation.DateEnd);
if (indexEnd < 0)
- indexEnd = telemetryDataSaub.Length - 1;
+ indexEnd = dataSaub.Length - 1;
if (indexEnd == indexStart)
continue;
var length = indexEnd - indexStart;
- var subset = telemetryDataSaub.AsSpan(indexStart, length);
+ var subset = dataSaub.AsSpan(indexStart, length);
var stats = CalcStats(operation, subset);
result.AddRange(stats);
}
return result;
}
- private static IEnumerable CalcStats(DetectedOperation operation, Span telemetryDataSaub)
+ private static IEnumerable CalcStats(DetectedOperationDto operation, Span dataSaub)
{
var result = new List();
var indexStart = 0;
- for (var i = 1; i < telemetryDataSaub.Length; i++)
+ for (var i = 1; i < dataSaub.Length; i++)
{
- var previous = telemetryDataSaub[i - 1];
- var current = telemetryDataSaub[i];
+ var previous = dataSaub[i - 1];
+ var current = dataSaub[i];
- if (IsNewCacheItem(previous, current) || i == telemetryDataSaub.Length - 1)
+ if (IsNewCacheItem(previous, current) || i == dataSaub.Length - 1)
{
var length = i - indexStart;
- var span = telemetryDataSaub.Slice(indexStart, length);
+ var span = dataSaub.Slice(indexStart, length);
indexStart = i;
if (length <= 2 || (span[^1].WellDepth - span[0].WellDepth) < 0.001)
continue; // мелкие выборки не учитываем.
@@ -144,10 +149,9 @@ namespace AsbCloudInfrastructure.Background.PeriodicWorks
return result;
}
- private static DataSaubStatDto CalcStat(DetectedOperation operation, Span span)
+ private static DataSaubStatDto CalcStat(DetectedOperationDto operation, Span span)
{
- var hasOscillation = operation.ExtraData.TryGetValue(DetectorDrilling.ExtraDataKeyHasOscillation, out object? hasOscillationObject)
- && hasOscillationObject is true;
+ var hasOscillation = EnabledSubsystemsFlags.AutoOscillation.HasEnabledSubsystems(operation.EnabledSubsystems);
var aggregatedValues = CalcAggregate(span);
var processMapDrillingCacheItem = new DataSaubStatDto
@@ -184,7 +188,7 @@ namespace AsbCloudInfrastructure.Background.PeriodicWorks
double RotorTorque,
double RotorSpeed,
double Flow
- ) CalcAggregate(Span span)
+ ) CalcAggregate(Span span)
{
var sumPressure = 0.0;
var sumAxialLoad = 0.0;
@@ -210,7 +214,7 @@ namespace AsbCloudInfrastructure.Background.PeriodicWorks
);
}
- private static bool IsNewCacheItem(TelemetryDataSaub previous, TelemetryDataSaub current)
+ private static bool IsNewCacheItem(TelemetryDataSaubDto previous, TelemetryDataSaubDto current)
{
return !(current.Mode == previous.Mode)
|| !(current.WellDepth >= previous.WellDepth)
diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs
index 363ec8ee..92d305c5 100644
--- a/AsbCloudInfrastructure/DependencyInjection.cs
+++ b/AsbCloudInfrastructure/DependencyInjection.cs
@@ -327,6 +327,8 @@ namespace AsbCloudInfrastructure
services.AddTransient, ProcessMapPlanService>();
services.AddTransient();
+ services.AddTransient();
+ services.AddTransient();
services.AddSingleton();
diff --git a/AsbCloudInfrastructure/Repository/ChangeLogRepositoryAbstract.cs b/AsbCloudInfrastructure/Repository/ChangeLogRepositoryAbstract.cs
index 64208a8e..f9265ba8 100644
--- a/AsbCloudInfrastructure/Repository/ChangeLogRepositoryAbstract.cs
+++ b/AsbCloudInfrastructure/Repository/ChangeLogRepositoryAbstract.cs
@@ -18,11 +18,11 @@ public abstract class ChangeLogRepositoryAbstract : ICh
where TEntity : ChangeLogAbstract
where TRequest : ChangeLogBaseRequest
{
- protected readonly IAsbCloudDbContext context;
+ protected readonly IAsbCloudDbContext db;
- public ChangeLogRepositoryAbstract(IAsbCloudDbContext context)
+ public ChangeLogRepositoryAbstract(IAsbCloudDbContext db)
{
- this.context = context;
+ this.db = db;
}
public async Task InsertRange(int idUser, IEnumerable dtos, CancellationToken token)
@@ -30,9 +30,10 @@ public abstract class ChangeLogRepositoryAbstract : ICh
var result = 0;
if (dtos.Any())
{
+ using var transaction = await db.Database.BeginTransactionAsync(token);
var entities = dtos.Select(Convert);
var creation = DateTimeOffset.UtcNow;
- var dbSet = context.Set();
+ var dbSet = db.Set();
foreach (var entity in entities)
{
entity.Id = default;
@@ -46,6 +47,7 @@ public abstract class ChangeLogRepositoryAbstract : ICh
}
result += await SaveChangesWithExceptionHandling(token);
+ await transaction.CommitAsync(token);
}
return result;
}
@@ -62,9 +64,8 @@ public abstract class ChangeLogRepositoryAbstract : ICh
var ids = dtos.Select(d => d.Id);
- using var transaction = context.Database.BeginTransaction();
var result = 0;
- var dbSet = context
+ var dbSet = db
.Set();
var entitiesToDelete = await dbSet
@@ -80,13 +81,14 @@ public abstract class ChangeLogRepositoryAbstract : ICh
throw new ArgumentInvalidException(nameof(dtos), $"записи с id:[{stringnotFoundIds}] не найдены, или не актуальны.");
}
+ using var transaction = db.Database.BeginTransaction();
foreach (var entity in entitiesToDelete)
{
entity.IdState = ChangeLogAbstract.IdStateReplaced;
entity.Obsolete = updateTime;
entity.IdEditor = idUser;
}
- result += await context.SaveChangesAsync(token);
+ result += await db.SaveChangesAsync(token);
var entitiesNew = dtos.Select(Convert);
foreach (var entity in entitiesNew)
@@ -143,7 +145,7 @@ public abstract class ChangeLogRepositoryAbstract : ICh
public async Task ClearAndInsertRange(int idUser, TRequest request, IEnumerable dtos, CancellationToken token)
{
var result = 0;
- var transaction = await context.Database.BeginTransactionAsync(token);
+ using var transaction = await db.Database.BeginTransactionAsync(token);
result += await Clear(idUser, request, token);
result += await InsertRange(idUser, dtos, token);
await transaction.CommitAsync(token);
@@ -153,7 +155,7 @@ public abstract class ChangeLogRepositoryAbstract : ICh
public async Task DeleteRange(int idUser, IEnumerable ids, CancellationToken token)
{
var updateTime = DateTimeOffset.UtcNow;
- var query = context.Set()
+ var query = db.Set()
.Where(e => ids.Contains(e.Id))
.Where(e => e.Obsolete == null);
@@ -269,7 +271,7 @@ public abstract class ChangeLogRepositoryAbstract : ICh
{
try
{
- var result = await context.SaveChangesAsync(token);
+ var result = await db.SaveChangesAsync(token);
return result;
}
catch (DbUpdateException ex)
diff --git a/AsbCloudInfrastructure/Repository/DetectedOperationRepository.cs b/AsbCloudInfrastructure/Repository/DetectedOperationRepository.cs
new file mode 100644
index 00000000..50ec6c9e
--- /dev/null
+++ b/AsbCloudInfrastructure/Repository/DetectedOperationRepository.cs
@@ -0,0 +1,191 @@
+using AsbCloudApp.Data.DetectedOperation;
+using AsbCloudApp.Exceptions;
+using AsbCloudApp.Repositories;
+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.Repository;
+
+public class DetectedOperationRepository : IDetectedOperationRepository
+{
+ private readonly IAsbCloudDbContext db;
+ private readonly ITelemetryService telemetryService;
+
+ public DetectedOperationRepository(
+ IAsbCloudDbContext db,
+ ITelemetryService telemetryService)
+ {
+ this.db = db;
+ this.telemetryService = telemetryService;
+ }
+
+ public async Task Delete(int idUser, DetectedOperationByTelemetryRequest request, CancellationToken token)
+ {
+ var query = BuildQuery(request);
+ db.Set().RemoveRange(query);
+ return await db.SaveChangesAsync(token);
+ }
+
+ public async Task DeleteRange(int idUser, IEnumerable ids, CancellationToken token)
+ {
+ var query = db.Set()
+ .Where(e => ids.Contains( e.Id));
+
+ db.Set()
+ .RemoveRange(query);
+
+ return await db.SaveChangesAsync(token);
+ }
+
+ public async Task> Get(DetectedOperationByTelemetryRequest request, CancellationToken token)
+ {
+ var query = BuildQuery(request)
+ .Include(o => o.OperationCategory);
+ var entities = await query.ToArrayAsync(token);
+ var offset = telemetryService.GetTimezone(request.IdTelemetry).Offset;
+ var dtos = entities.Select(o => Convert(o, offset));
+
+ return dtos;
+ }
+
+ public async Task Insert(int? idUser, IEnumerable dtos, CancellationToken token)
+ {
+ if(!dtos.Any())
+ return 0;
+
+ var entities = dtos.Select(Convert);
+ var dbset = db.Set();
+ foreach(var entity in entities)
+ {
+ entity.Id = default;
+ dbset.Add(entity);
+ }
+
+ return await db.SaveChangesWithExceptionHandling(token);
+ }
+
+ public async Task Update(int idUser, IEnumerable dtos, CancellationToken token)
+ {
+ if (!dtos.Any())
+ return 0;
+
+ var ids = dtos
+ .Select(o => o.Id)
+ .Distinct()
+ .ToArray();
+
+ if (ids.Any(id => id == default))
+ throw new ArgumentInvalidException(nameof(dtos), "Все записи должны иметь Id");
+
+ if (ids.Length != dtos.Count())
+ throw new ArgumentInvalidException(nameof(dtos), "Все записи должны иметь уникальные Id");
+
+ var dbSet = db.Set();
+
+ var existingEntitiesCount = await dbSet
+ .Where(o => ids.Contains(o.Id))
+ .CountAsync(token);
+
+ if (ids.Length != existingEntitiesCount)
+ throw new ArgumentInvalidException(nameof(dtos), "Все записи должны существовать в БД");
+
+ var entities = dtos
+ .Select(Convert)
+ .ToArray();
+
+ var entries = new Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry[entities.Length];
+ for(var i = 0; i < entities.Length; i++)
+ entries[i] = dbSet.Update(entities[i]);
+
+ var result = await db.SaveChangesWithExceptionHandling(token);
+
+ for (var i = 0; i < entries.Length; i++)
+ entries[i].State = EntityState.Detached;
+
+ return result;
+ }
+
+ public async Task UpdateOrInsert(int idUser, IEnumerable dtos, CancellationToken token)
+ {
+ var result = 0;
+
+ var itemsToInsert = dtos.Where(e => e.Id == 0);
+ if (itemsToInsert.Any())
+ result += await Insert(idUser, itemsToInsert, token);
+
+ var itemsToUpdate = dtos.Where(e => e.Id != 0);
+ if (itemsToUpdate.Any())
+ result += await Update(idUser, itemsToUpdate, token);
+
+ return result;
+ }
+
+ private IQueryable BuildQuery(DetectedOperationByTelemetryRequest request)
+ {
+ var query = db.Set()
+ .Where(o => o.IdTelemetry == request.IdTelemetry);
+
+ if (request.IdsCategories.Any())
+ query = query.Where(o => request.IdsCategories.Contains(o.IdCategory));
+
+ if (request.GeDepthStart is not null)
+ query = query.Where(o => o.DepthStart >= request.GeDepthStart);
+
+ if (request.LeDepthEnd is not null)
+ query = query.Where(o => o.DepthEnd <= request.LeDepthEnd);
+
+ if (request.GeDateStart is not null)
+ {
+ var geDate = request.GeDateStart.Value.ToUniversalTime();
+ query = query.Where(o => o.DateStart >= geDate);
+ }
+
+ if (request.LeDateEnd is not null)
+ {
+ var leDate = request.LeDateEnd.Value.ToUniversalTime();
+ query = query.Where(o => o.DateEnd <= leDate);
+ }
+
+ if (request.SortFields?.Any() == true)
+ {
+ query = query.SortBy(request.SortFields);
+ }
+ else
+ query = query
+ .OrderBy(o => o.DateStart)
+ .ThenBy(o => o.DepthStart);
+
+ if (request.Skip.HasValue)
+ query = query.Skip((int)request.Skip);
+
+ if (request.Take.HasValue)
+ query = query.Take((int)request.Take);
+
+ return query;
+ }
+
+ private static DetectedOperationDto Convert(DetectedOperation entity, TimeSpan offset)
+ {
+ var dto = entity.Adapt();
+ dto.DateStart = entity.DateStart.ToOffset(offset);
+ dto.DateEnd = entity.DateEnd.ToOffset(offset);
+ return dto;
+ }
+
+ private static DetectedOperation Convert(DetectedOperationDto dto)
+ {
+ var entity = dto.Adapt();
+ entity.DateStart = dto.DateStart.ToUniversalTime();
+ entity.DateEnd = dto.DateEnd.ToUniversalTime();
+ return entity;
+ }
+}
diff --git a/AsbCloudInfrastructure/Repository/ProcessMapPlanBaseRepository.cs b/AsbCloudInfrastructure/Repository/ProcessMapPlanBaseRepository.cs
index 0133b527..7da062ac 100644
--- a/AsbCloudInfrastructure/Repository/ProcessMapPlanBaseRepository.cs
+++ b/AsbCloudInfrastructure/Repository/ProcessMapPlanBaseRepository.cs
@@ -23,7 +23,7 @@ public class ProcessMapPlanBaseRepository : ChangeLogRepositoryAb
protected override IQueryable BuildQuery(ProcessMapPlanBaseRequestWithWell request)
{
- var query = context
+ var query = db
.Set()
.Include(e => e.Author)
.Include(e => e.Editor)
diff --git a/AsbCloudInfrastructure/Repository/WellOperationCategoryRepository.cs b/AsbCloudInfrastructure/Repository/WellOperationCategoryRepository.cs
new file mode 100644
index 00000000..84da5f3d
--- /dev/null
+++ b/AsbCloudInfrastructure/Repository/WellOperationCategoryRepository.cs
@@ -0,0 +1,44 @@
+using AsbCloudApp.Data;
+using AsbCloudApp.Repositories;
+using AsbCloudDb.Model;
+using Mapster;
+using Microsoft.Extensions.Caching.Memory;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace AsbCloudInfrastructure.Repository;
+
+public class WellOperationCategoryRepository : IWellOperationCategoryRepository
+{
+ private readonly IAsbCloudDbContext db;
+ private readonly IMemoryCache memoryCache;
+
+ public WellOperationCategoryRepository(IAsbCloudDbContext db, IMemoryCache memoryCache)
+ {
+ this.db = db;
+ this.memoryCache = memoryCache;
+ }
+
+ public IEnumerable Get(bool includeParents)
+ {
+ var categories = memoryCache
+ .GetOrCreateBasic(db.Set());
+
+ if (!includeParents)
+ {
+ var parentIds = categories
+ .Select(o => o.IdParent)
+ .Distinct();
+
+ categories = categories
+ .Where(o => !parentIds.Contains(o.Id));
+ }
+
+ var result = categories
+ .OrderBy(o => o.Name)
+ .Adapt>();
+
+ return result;
+ }
+}
diff --git a/AsbCloudInfrastructure/Repository/WellOperationRepository.cs b/AsbCloudInfrastructure/Repository/WellOperationRepository.cs
index c9d3c7fc..1330a282 100644
--- a/AsbCloudInfrastructure/Repository/WellOperationRepository.cs
+++ b/AsbCloudInfrastructure/Repository/WellOperationRepository.cs
@@ -28,35 +28,20 @@ public class WellOperationRepository : IWellOperationRepository
private readonly IAsbCloudDbContext db;
private readonly IMemoryCache memoryCache;
private readonly IWellService wellService;
+ private readonly IWellOperationCategoryRepository wellOperationCategoryRepository;
- public WellOperationRepository(IAsbCloudDbContext db, IMemoryCache memoryCache, IWellService wellService)
+ public WellOperationRepository(IAsbCloudDbContext db, IMemoryCache memoryCache, IWellService wellService, IWellOperationCategoryRepository wellOperationCategoryRepository)
{
this.db = db;
this.memoryCache = memoryCache;
this.wellService = wellService;
+ this.wellOperationCategoryRepository = wellOperationCategoryRepository;
}
///
public IEnumerable GetCategories(bool includeParents)
{
- var categories = memoryCache
- .GetOrCreateBasic(db.Set());
-
- if (!includeParents)
- {
- var parentIds = categories
- .Select(o => o.IdParent)
- .Distinct();
-
- categories = categories
- .Where(o => !parentIds.Contains(o.Id));
- }
-
- var result = categories
- .OrderBy(o => o.Name)
- .Adapt>();
-
- return result;
+ return wellOperationCategoryRepository.Get(includeParents);
}
///
diff --git a/AsbCloudInfrastructure/Services/DailyReport/DailyReportService.cs b/AsbCloudInfrastructure/Services/DailyReport/DailyReportService.cs
index e4271c8f..87108fd2 100644
--- a/AsbCloudInfrastructure/Services/DailyReport/DailyReportService.cs
+++ b/AsbCloudInfrastructure/Services/DailyReport/DailyReportService.cs
@@ -282,7 +282,7 @@ public class DailyReportService : IDailyReportService
var leDateEnd = dailyReport.Date.AddDays(1).ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified);
dailyReport.TimeBalanceBlock.WellOperationSlipsTimeCount = (await detectedOperationService.GetAsync(
- new DetectedOperationRequest
+ new DetectedOperationByWellRequest
{
IdsCategories = new[] { idWellOperationSlipsTime },
IdWell = dailyReport.IdWell,
diff --git a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationExportService.cs b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationExportService.cs
index ef781a1e..987f274f 100644
--- a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationExportService.cs
+++ b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationExportService.cs
@@ -1,6 +1,5 @@
using AsbCloudDb.Model;
using ClosedXML.Excel;
-using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.IO;
@@ -9,22 +8,21 @@ using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data.DetectedOperation;
-using AsbCloudInfrastructure.Services.DetectOperations.Detectors;
using AsbCloudApp.Repositories;
using Microsoft.AspNetCore.Http.Extensions;
using AsbCloudApp.Exceptions;
+using AsbCloudApp.Services;
+using AsbCloudApp.Data;
+using AsbCloudInfrastructure.Services.DetectOperations.Detectors;
namespace AsbCloudInfrastructure.Services.DetectOperations;
public class DetectedOperationExportService
{
- private readonly DetectorAbstract[] detectors =
- {
- new DetectorDrilling(),
- new DetectorSlipsTime()
- };
-
- private const int headerRowsCount = 1;
+ private readonly IAsbCloudDbContext db;
+ private readonly IWellService wellService;
+ private readonly IWellOperationCategoryRepository wellOperationCategoryRepository;
+ private const int headerRowsCount = 1;
private const string cellDepositName = "B1";
private const string cellClusterName = "B2";
@@ -42,14 +40,14 @@ public class DetectedOperationExportService
private const int columnIdReasonOfEnd = 9;
private const int columnComment = 10;
- //TODO: удалить неиспользуемую зависимость
- private readonly IAsbCloudDbContext dbContext;
- private readonly IWellOperationRepository wellOperationRepository;
-
- public DetectedOperationExportService(IAsbCloudDbContext dbContext, IWellOperationRepository wellOperationRepository)
+ public DetectedOperationExportService(
+ IAsbCloudDbContext db,
+ IWellService wellService,
+ IWellOperationCategoryRepository wellOperationCategoryRepository)
{
- this.dbContext = dbContext;
- this.wellOperationRepository = wellOperationRepository;
+ this.db = db;
+ this.wellService = wellService;
+ this.wellOperationCategoryRepository = wellOperationCategoryRepository;
}
///
@@ -57,15 +55,12 @@ public class DetectedOperationExportService
///
/// ключ скважины
/// хост
- ///
+ ///
///
///
- public async Task ExportAsync(int idWell, string host, CancellationToken cancellationToken)
+ public async Task ExportAsync(int idWell, string host, CancellationToken token)
{
- var well = await dbContext.Set()
- .Include(w => w.Cluster)
- .ThenInclude(c => c.Deposit)
- .FirstOrDefaultAsync(w => w.Id == idWell, cancellationToken);
+ var well = await wellService.GetOrDefaultAsync(idWell, token);
if (well is null)
throw new ArgumentInvalidException(nameof(idWell), $"Well {idWell} does not exist");
@@ -73,19 +68,19 @@ public class DetectedOperationExportService
if (!well.IdTelemetry.HasValue)
throw new ArgumentInvalidException(nameof(idWell), $"Well {idWell} has no telemetry");
- var operations = await DetectOperationsAsync(well.IdTelemetry.Value, DateTime.UnixEpoch, cancellationToken);
+ var operations = await WorkOperationDetection.DetectOperationsAsync(well.IdTelemetry.Value, DateTime.UnixEpoch, db, token);
- return await GenerateExcelFileStreamAsync(well, host, operations, cancellationToken);
+ return await GenerateExcelFileStreamAsync(well, host, operations, token);
}
- private async Task GenerateExcelFileStreamAsync(Well well, string host, IEnumerable operationDetectorResults,
+ private async Task GenerateExcelFileStreamAsync(WellDto well, string host, IEnumerable operationDetectorResults,
CancellationToken cancellationToken)
{
using var excelTemplateStream = await GetExcelTemplateStreamAsync(cancellationToken);
using var workbook = new XLWorkbook(excelTemplateStream);
- await AddToWorkbookAsync(workbook, well, host, operationDetectorResults, cancellationToken);
+ AddToWorkbook(workbook, well, host, operationDetectorResults);
MemoryStream memoryStream = new MemoryStream();
workbook.SaveAs(memoryStream, new SaveOptions { });
@@ -93,36 +88,34 @@ public class DetectedOperationExportService
return memoryStream;
}
- private async Task AddToWorkbookAsync(XLWorkbook workbook, Well well, string host, IEnumerable operationDetectorResults,
- CancellationToken cancellationToken)
+ private void AddToWorkbook(XLWorkbook workbook, WellDto well, string host, IEnumerable operations)
{
const string sheetName = "Операции";
- if (!operationDetectorResults.Any())
+ if (!operations.Any())
return;
var sheet = workbook.GetWorksheet(sheetName);
- await AddToSheetAsync(sheet, well, host, operationDetectorResults
- .OrderBy(x => x.Operation.DateStart).ThenBy(x => x.Operation.DepthStart).ToArray(),
- cancellationToken);
+ var orderedOperations = operations
+ .OrderBy(x => x.DateStart)
+ .ThenBy(x => x.DepthStart).ToArray();
+
+ AddToSheet(sheet, well, host, orderedOperations);
}
- private async Task AddToSheetAsync(IXLWorksheet sheet, Well well, string host, IList operationDetectorResults,
- CancellationToken cancellationToken)
+ private void AddToSheet(IXLWorksheet sheet, WellDto well, string host, IList operations)
{
- var wellOperationCategories = await dbContext.WellOperationCategories.ToListAsync(cancellationToken);
+ var wellOperationCategories = wellOperationCategoryRepository.Get(true);
- sheet.Cell(cellDepositName).SetCellValue(well.Cluster.Deposit.Caption);
- sheet.Cell(cellClusterName).SetCellValue(well.Cluster.Caption);
+ sheet.Cell(cellDepositName).SetCellValue(well.Deposit);
+ sheet.Cell(cellClusterName).SetCellValue(well.Cluster);
sheet.Cell(cellWellName).SetCellValue(well.Caption);
- sheet.Cell(cellDeltaDate).SetCellValue(operationDetectorResults.Max(o => o.Operation.DateEnd) - operationDetectorResults.Min(o => o.Operation.DateStart));
+ sheet.Cell(cellDeltaDate).SetCellValue((TimeSpan)(Enumerable.Max(operations, (Func)(o => o.DateEnd)) - Enumerable.Min(operations, (Func)(o => o.DateStart))));
- var detectedOperations = operationDetectorResults.Select(o => o.Operation).ToArray();
-
- for (int i = 0; i < operationDetectorResults.Count; i++)
+ for (int i = 0; i < operations.Count; i++)
{
- var current = detectedOperations[i];
+ var current = operations[i];
var dateStart = current.DateStart.ToRemoteDateTime(well.Timezone.Hours);
var dateEnd = current.DateEnd.ToRemoteDateTime(well.Timezone.Hours);
@@ -153,17 +146,18 @@ public class DetectedOperationExportService
var link = $"{host}/well/{well.Id}/telemetry/monitoring{query}";
row.Cell(columnDateStart).SetHyperlink(link);
- var deltaDepth = i > 0 && i + 1 < detectedOperations.Length
- ? current.DepthStart - detectedOperations[i - 1].DepthEnd
+ var deltaDepth = i > 0 && i + 1 < operations.Count
+ ? current.DepthStart - operations[i - 1].DepthEnd
: 0;
+
row.Cell(columnDeltaDepth).SetCellValue(deltaDepth);
- var comment = CreateComment(operationDetectorResults[i]);
+ var comment = CreateComment(operations[i]);
row.Cell(columnComment).SetCellValue(comment);
}
}
- private static string GetCategoryName(IEnumerable wellOperationCategories, DetectedOperation current)
+ private static string GetCategoryName(IEnumerable wellOperationCategories, DetectedOperation current)
{
var idCategory = current.IdCategory;
if (idCategory == WellOperationCategory.IdSlide &&
@@ -204,9 +198,8 @@ public class DetectedOperationExportService
return memoryStream;
}
- private static string CreateComment(OperationDetectorResult operationDetectorResult)
+ private static string CreateComment(DetectedOperation operation)
{
- var operation = operationDetectorResult.Operation;
switch (operation.IdCategory)
{
case WellOperationCategory.IdRotor:
@@ -224,69 +217,4 @@ public class DetectedOperationExportService
return string.Empty;
}
}
-
- private async Task> DetectOperationsAsync(int idTelemetry, DateTimeOffset begin,
- CancellationToken token)
- {
- var query = dbContext.TelemetryDataSaub
- .AsNoTracking()
- .Where(d => d.IdTelemetry == idTelemetry)
- .Where(d => d.BlockPosition >= 0)
- .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 startDate = begin;
- var detectedOperationResults = new List(8);
- DetectedOperation? lastDetectedOperation = null;
- const int minOperationLength = 5;
- const int maxDetectorsInterpolationFrameLength = 30;
- const int gap = maxDetectorsInterpolationFrameLength + minOperationLength;
-
- while (true)
- {
- var data = await query
- .Where(d => d.DateTime > startDate)
- .ToArrayAsync(token);
-
- if (data.Length < gap)
- break;
-
- var isDetected = false;
- var positionBegin = 0;
- var positionEnd = data.Length - gap;
- while (positionEnd > positionBegin)
- {
- foreach (var detector in detectors)
- {
- if (!detector.TryDetect(idTelemetry, data, positionBegin, positionEnd, lastDetectedOperation, out var result))
- continue;
-
- detectedOperationResults.Add(result!);
- lastDetectedOperation = result!.Operation;
- isDetected = true;
- positionBegin = result.TelemetryEnd;
- break;
- }
-
- positionBegin += 1;
- }
-
- if (isDetected)
- startDate = lastDetectedOperation!.DateEnd;
- else
- startDate = data[positionEnd].DateTime;
- }
-
- return detectedOperationResults;
- }
}
\ No newline at end of file
diff --git a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs
index 23a5d20b..bd8fba77 100644
--- a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs
+++ b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs
@@ -1,5 +1,6 @@
using AsbCloudApp.Data;
using AsbCloudApp.Data.DetectedOperation;
+using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudDb;
@@ -11,274 +12,200 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-namespace AsbCloudInfrastructure.Services.DetectOperations
+namespace AsbCloudInfrastructure.Services.DetectOperations;
+
+public class DetectedOperationService : IDetectedOperationService
{
+ private readonly IDetectedOperationRepository operationRepository;
+ private readonly IWellOperationCategoryRepository wellOperationCategoryRepository;
+ private readonly IWellService wellService;
+ private readonly IRepositoryWellRelated operationValueService;
+ private readonly IScheduleRepository scheduleService;
- public class DetectedOperationService : IDetectedOperationService
+ public DetectedOperationService(
+ IDetectedOperationRepository operationRepository,
+ IWellOperationCategoryRepository wellOperationCategoryRepository,
+ IWellService wellService,
+ IRepositoryWellRelated operationValueRepository,
+ IScheduleRepository scheduleRepository)
{
- private readonly IAsbCloudDbContext db;
- private readonly IWellService wellService;
- private readonly IRepositoryWellRelated operationValueService;
- private readonly IScheduleRepository scheduleService;
+ this.operationRepository = operationRepository;
+ this.wellOperationCategoryRepository = wellOperationCategoryRepository;
+ this.wellService = wellService;
+ this.operationValueService = operationValueRepository;
+ this.scheduleService = scheduleRepository;
+ }
- public DetectedOperationService(IAsbCloudDbContext db, IWellService wellService,
- IRepositoryWellRelated operationValueService, IScheduleRepository scheduleService)
+ public async Task GetAsync(DetectedOperationByWellRequest request, CancellationToken token)
+ {
+ var dtos = await GetOperationsAsync(request, token);
+ if (dtos?.Any() != true)
+ return new DetectedOperationListDto();
+
+ var stats = GetOperationsDrillersStat(dtos);
+ var result = new DetectedOperationListDto
{
- this.db = db;
- this.wellService = wellService;
- this.operationValueService = operationValueService;
- this.scheduleService = scheduleService;
+ Operations = dtos,
+ Stats = stats
+ };
+ return result;
+ }
+
+ public async Task> GetOperationsAsync(DetectedOperationByWellRequest request, CancellationToken token)
+ {
+ var well = await wellService.GetOrDefaultAsync(request.IdWell, token);
+ if (well?.IdTelemetry is null)
+ return Enumerable.Empty();
+
+ var requestByTelemetry = new DetectedOperationByTelemetryRequest(well.IdTelemetry.Value, request);
+ var data = await operationRepository.Get(requestByTelemetry, token);
+
+ var operationValues = await operationValueService.GetByIdWellAsync(request.IdWell, token);
+ var schedules = await scheduleService.GetByIdWellAsync(request.IdWell, token);
+ var dtos = data.Select(o => Convert(o, operationValues, schedules));
+ return dtos;
+ }
+
+ public async Task> GetCategoriesAsync(int? idWell, CancellationToken token)
+ {
+ if(idWell is null)
+ {
+ return wellOperationCategoryRepository.Get(false);
}
-
- public async Task GetAsync(DetectedOperationRequest request, CancellationToken token)
+ else
{
- var dtos = await GetOperationsAsync(request, token);
- if (dtos?.Any() != true)
- return null;
-
- var stats = GetOperationsDrillersStat(dtos);
- var result = new DetectedOperationListDto
- {
- Operations = dtos,
- Stats = stats
- };
- return result;
- }
-
- public async Task> GetOperationsAsync(DetectedOperationRequest request, CancellationToken token)
- {
- var well = await wellService.GetOrDefaultAsync(request.IdWell, token);
+ var well = await wellService.GetOrDefaultAsync((int )idWell, token);
if (well?.IdTelemetry is null)
- return Enumerable.Empty();
+ return Enumerable.Empty();
- var query = BuildQuery(well, request).AsNoTracking();
-
- var data = await query.ToListAsync(token);
-
- var operationValues = await operationValueService.GetByIdWellAsync(request.IdWell, token);
- var schedules = await scheduleService.GetByIdWellAsync(request.IdWell, token);
- var dtos = data.Select(o => Convert(o, well, operationValues, schedules));
- return dtos;
- }
-
- private static IEnumerable GetOperationsDrillersStat(IEnumerable operations)
- {
- var groups = operations.GroupBy(o => o.Driller);
-
- var stats = new List(groups.Count());
- foreach (var group in groups)
- {
- var itemsWithTarget = group.Where(i => i.OperationValue is not null);
- var stat = new DetectedOperationDrillersStatDto
- {
- Driller = group.Key,
- AverageValue = group.Sum(e => e.Value) / group.Count(),
- Count = group.Count(),
- };
- if (itemsWithTarget.Any())
- {
- var itemsOutOfTarget = itemsWithTarget.Where(o => !IsTargetOk(o));
- stat.AverageTargetValue = itemsWithTarget.Average(e => e.OperationValue?.TargetValue);
- stat.Efficiency = 100d * itemsOutOfTarget.Count() / itemsWithTarget.Count();
- stat.Loss = itemsOutOfTarget.Sum(DeltaToTarget);
- }
-
- stats.Add(stat);
- }
- return stats;
- }
-
- public async Task?> GetOperationsStatAsync(DetectedOperationRequest request, CancellationToken token)
- {
- var well = await wellService.GetOrDefaultAsync(request.IdWell, token);
- if (well?.IdTelemetry is null || well.Timezone is null)
- return null;
-
- var query = BuildQuery(well, request)
- ?.AsNoTracking();
-
- if (query is null)
- return null;
-
- var entities = await query
- .Select(o => new {
- o.IdCategory,
- DurationMinutes = (o.DateEnd - o.DateStart).TotalMinutes,
- o.Value,
- })
- .ToListAsync(token);
-
- if (!entities.Any())
- return null;
-
- var operationValues = await operationValueService.GetByIdWellAsync(request.IdWell, token);
- var categories = await query
- .Select(o => new {o.IdCategory, o.OperationCategory.Name })
- .Distinct()
- .ToDictionaryAsync(c=>c.IdCategory, c=>c.Name, token);
-
- var dtos = entities
- .GroupBy(o => o.IdCategory)
- .OrderBy(g => g.Key)
- .Select(g => new DetectedOperationStatDto{
- IdCategory = g.Key,
- Category = categories[g.Key],
- Count = g.Count(),
- MinutesAverage = g.Average(o => o.DurationMinutes),
- MinutesMin = g.Min(o => o.DurationMinutes),
- MinutesMax = g.Max(o => o.DurationMinutes),
- MinutesTotal = g.Sum(o => o.DurationMinutes),
- ValueAverage = g.Average(o => o.Value),
- ValueMax = g.Max(o => o.Value),
- ValueMin = g.Min(o => o.Value),
- });
-
- return dtos;
- }
-
- public async Task DeleteAsync(DetectedOperationRequest request, CancellationToken token)
- {
- var well = await wellService.GetOrDefaultAsync(request.IdWell, token);
- if (well?.IdTelemetry is null || well.Timezone is null)
- return 0;
-
- var query = BuildQueryBase(well, request);
-
- if (query is null)
- return 0;
-
- db.DetectedOperations.RemoveRange(query);
- return await db.SaveChangesAsync(token);
- }
-
- private static bool IsTargetOk(DetectedOperationDto op)
- {
- return (op.IdCategory) switch
- {
- WellOperationCategory.IdRotor => op.Value > op.OperationValue?.TargetValue,
- WellOperationCategory.IdSlide => op.Value > op.OperationValue?.TargetValue,
- WellOperationCategory.IdSlipsTime => op.Value > op.OperationValue?.TargetValue,
- _ => op.Value > op.OperationValue?.TargetValue,
+ var request = new DetectedOperationByTelemetryRequest()
+ {
+ IdTelemetry = well.IdTelemetry.Value
};
- }
- private static double DeltaToTarget(DetectedOperationDto op)
- {
- return (op.IdCategory) switch
- {
- WellOperationCategory.IdRotor => 0,
- WellOperationCategory.IdSlide => 0,
- WellOperationCategory.IdSlipsTime => op.Value - op.OperationValue?.TargetValue??0,
- _ => 0,
- };
- }
-
- private IQueryable BuildQueryBase(WellDto well, DetectedOperationRequest request)
- {
- var query = db.Set()
- .Where(o => o.IdTelemetry == well.IdTelemetry);
-
- if (request.IdsTelemetries.Any())
- {
- query = query
- .Union(db.Set().Where(o => request.IdsTelemetries.Contains(o.IdTelemetry)));
- }
-
- if (request.IdsCategories.Any())
- query = query.Where(o => request.IdsCategories.Contains(o.IdCategory));
-
- if (request.GeDateStart is not null)
- query = query.Where(o => o.DateStart >= request.GeDateStart.Value.Date.ToUtcDateTimeOffset(well.Timezone.Hours));
-
- if (request.LeDateEnd is not null)
- query = query.Where(o => o.DateEnd <= request.LeDateEnd.Value.Date.ToUtcDateTimeOffset(well.Timezone.Hours));
-
- if (request.GeDepth is not null)
- query = query.Where(o => o.DepthStart >= request.GeDepth);
-
- if (request.LeDepth is not null)
- query = query.Where(o => o.DepthEnd <= request.LeDepth);
-
- if (request.IdTelemetryUser is not null)
- query = query.Where(o => o.IdUsersAtStart == request.IdTelemetryUser);
-
- return query;
- }
-
- private IQueryable BuildQuery(WellDto well, DetectedOperationRequest request)
- {
- IQueryable query = BuildQueryBase(well, request)
- .Include(o => o.OperationCategory);
-
- if (request.SortFields?.Any() == true)
- {
- query = query.SortBy(request.SortFields);
- }
- else
- query = query
- .OrderBy(o => o.DateStart)
- .ThenBy(o => o.DepthStart);
-
- if (request.Skip.HasValue)
- query = query.Skip((int)request.Skip);
-
- if (request.Take.HasValue)
- query = query.Take((int)request.Take);
-
- return query;
- }
-
- private static DetectedOperationDto Convert(DetectedOperation operation, WellDto well, IEnumerable operationValues, IEnumerable schedules)
- {
- var dto = operation.Adapt();
- dto.IdWell = well.Id;
- var dateStart = operation.DateStart.ToRemoteDateTime(well.Timezone.Hours);
- dto.DateStart = dateStart;
- dto.DateEnd = operation.DateEnd.ToRemoteDateTime(well.Timezone.Hours);
- dto.OperationValue = operationValues.FirstOrDefault(v => v.IdOperationCategory == dto.IdCategory
- && v.DepthStart <= dto.DepthStart
- && v.DepthEnd > dto.DepthStart);
-
- var timeStart = new TimeDto(dateStart);
- var driller = schedules.FirstOrDefault(s =>
- s.DrillStart <= dateStart &&
- s.DrillEnd > dateStart && (
- s.ShiftStart > s.ShiftEnd
- ) ^ (s.ShiftStart <= timeStart &&
- s.ShiftEnd > timeStart
- ))
- ?.Driller;
- dto.Driller = driller;
- return dto;
- }
-
- public async Task?> GetCategoriesAsync(int? idWell, CancellationToken token)
- {
- IQueryable query;
- if(idWell is null)
- {
- query = db.WellOperationCategories;
- }
- else
- {
- var well = await wellService.GetOrDefaultAsync((int )idWell, token);
- if (well?.IdTelemetry is null)
- return null;
- var idTelemetry = (int)well.IdTelemetry;
- query = db.DetectedOperations
- .Include(o => o.OperationCategory)
- .Where(o => o.IdTelemetry == idTelemetry)
- .Select(o => o.OperationCategory)
- .Distinct();
- }
-
- var result = await query
- .Where(c => c.Id < 1000)
- .AsNoTracking()
- .Select(c => c.Adapt())
- .ToArrayAsync(token);
- return result;
+ var operations = await operationRepository.Get(request, token);
+ var categories = operations
+ .Select(o => o.OperationCategory)
+ .Distinct();
+ return categories;
}
}
+ public async Task> GetOperationsStatAsync(DetectedOperationByWellRequest request, CancellationToken token)
+ {
+ var well = await wellService.GetOrDefaultAsync(request.IdWell, token);
+ if (well?.IdTelemetry is null || well.Timezone is null)
+ return Enumerable.Empty();
+
+ var requestByTelemetry = new DetectedOperationByTelemetryRequest(well.IdTelemetry.Value, request);
+
+ var operations = await operationRepository.Get(requestByTelemetry, token);
+
+ if (!operations.Any())
+ return Enumerable.Empty();
+
+ var operationValues = await operationValueService.GetByIdWellAsync(request.IdWell, token);
+
+ var dtos = operations
+ .GroupBy(o => (o.IdCategory, o.OperationCategory.Name))
+ .OrderBy(g => g.Key)
+ .Select(g => new DetectedOperationStatDto
+ {
+ IdCategory = g.Key.IdCategory,
+ Category = g.Key.Name,
+ Count = g.Count(),
+ MinutesAverage = g.Average(o => o.DurationMinutes),
+ MinutesMin = g.Min(o => o.DurationMinutes),
+ MinutesMax = g.Max(o => o.DurationMinutes),
+ MinutesTotal = g.Sum(o => o.DurationMinutes),
+ ValueAverage = g.Average(o => o.Value),
+ ValueMax = g.Max(o => o.Value),
+ ValueMin = g.Min(o => o.Value),
+ });
+
+ return dtos;
+ }
+
+ public async Task DeleteAsync(DetectedOperationByWellRequest request, CancellationToken token)
+ {
+ var well = await wellService.GetOrDefaultAsync(request.IdWell, token);
+ if (well?.IdTelemetry is null || well.Timezone is null)
+ return 0;
+
+ var requestByTelemetry = new DetectedOperationByTelemetryRequest(well.IdTelemetry.Value, request);
+ var result = await operationRepository.Delete(-1, requestByTelemetry, token);
+ return result;
+ }
+
+ private static IEnumerable GetOperationsDrillersStat(IEnumerable operations)
+ {
+ var groups = operations.GroupBy(o => o.Driller);
+
+ var stats = new List(groups.Count());
+ foreach (var group in groups)
+ {
+ var itemsWithTarget = group.Where(i => i.OperationValue is not null);
+ var stat = new DetectedOperationDrillersStatDto
+ {
+ Driller = group.Key,
+ AverageValue = group.Sum(e => e.Value) / group.Count(),
+ Count = group.Count(),
+ };
+ if (itemsWithTarget.Any())
+ {
+ var itemsOutOfTarget = itemsWithTarget.Where(o => !IsTargetOk(o));
+ stat.AverageTargetValue = itemsWithTarget.Average(e => e.OperationValue?.TargetValue);
+ stat.Efficiency = 100d * itemsOutOfTarget.Count() / itemsWithTarget.Count();
+ stat.Loss = itemsOutOfTarget.Sum(DeltaToTarget);
+ }
+
+ stats.Add(stat);
+ }
+ return stats;
+ }
+
+ private static bool IsTargetOk(DetectedOperationWithDrillerDto op)
+ {
+ return (op.IdCategory) switch
+ {
+ WellOperationCategory.IdRotor => op.Value > op.OperationValue?.TargetValue,
+ WellOperationCategory.IdSlide => op.Value > op.OperationValue?.TargetValue,
+ WellOperationCategory.IdSlipsTime => op.Value > op.OperationValue?.TargetValue,
+ _ => op.Value > op.OperationValue?.TargetValue,
+ };
+ }
+
+ private static double DeltaToTarget(DetectedOperationWithDrillerDto op)
+ {
+ return (op.IdCategory) switch
+ {
+ WellOperationCategory.IdRotor => 0,
+ WellOperationCategory.IdSlide => 0,
+ WellOperationCategory.IdSlipsTime => op.Value - op.OperationValue?.TargetValue??0,
+ _ => 0,
+ };
+ }
+
+
+ private static DetectedOperationWithDrillerDto Convert(DetectedOperationDto operation, IEnumerable operationValues, IEnumerable schedules)
+ {
+ var dto = operation.Adapt();
+ dto.OperationValue = operationValues.FirstOrDefault(v => v.IdOperationCategory == dto.IdCategory
+ && v.DepthStart <= dto.DepthStart
+ && v.DepthEnd > dto.DepthStart);
+
+ var dateStart = dto.DateStart;
+ var timeStart = new TimeDto(dateStart);
+ var driller = schedules.FirstOrDefault(s =>
+ s.DrillStart <= dateStart &&
+ s.DrillEnd > dateStart && (
+ s.ShiftStart > s.ShiftEnd
+ ) ^ (s.ShiftStart <= timeStart &&
+ s.ShiftEnd > timeStart
+ ))
+ ?.Driller;
+ dto.Driller = driller;
+
+ return dto;
+ }
}
diff --git a/AsbCloudInfrastructure/Services/DetectOperations/WorkOperationDetection.cs b/AsbCloudInfrastructure/Services/DetectOperations/WorkOperationDetection.cs
index 2dc09b02..aece226e 100644
--- a/AsbCloudInfrastructure/Services/DetectOperations/WorkOperationDetection.cs
+++ b/AsbCloudInfrastructure/Services/DetectOperations/WorkOperationDetection.cs
@@ -86,7 +86,8 @@ public class WorkOperationDetection: Work
}
}
- private static async Task> DetectOperationsAsync(int idTelemetry, DateTimeOffset begin, IAsbCloudDbContext db, CancellationToken token)
+ //todo: move this logic to DetectedOperationsService
+ internal static async Task> DetectOperationsAsync(int idTelemetry, DateTimeOffset begin, IAsbCloudDbContext db, CancellationToken token)
{
var query = db.TelemetryDataSaub
.AsNoTracking()
diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataSaubService.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataSaubService.cs
index d918f384..fdced02c 100644
--- a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataSaubService.cs
+++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataSaubService.cs
@@ -1,5 +1,4 @@
-using AsbCloudApp.Data;
-using AsbCloudApp.Data.SAUB;
+using AsbCloudApp.Data.SAUB;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
@@ -11,142 +10,163 @@ using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
-using System.Text;
using System.Text.Csv;
using System.Threading;
using System.Threading.Tasks;
-namespace AsbCloudInfrastructure.Services.SAUB
+namespace AsbCloudInfrastructure.Services.SAUB;
+
+public class TelemetryDataSaubService : TelemetryDataBaseService, ITelemetryDataSaubService
{
+ private readonly ITelemetryUserService telemetryUserService;
- public class TelemetryDataSaubService : TelemetryDataBaseService, ITelemetryDataSaubService
+ public TelemetryDataSaubService(
+ IAsbCloudDbContext db,
+ ITelemetryService telemetryService,
+ ITelemetryUserService telemetryUserService,
+ ITelemetryDataCache telemetryDataCache)
+ : base(db, telemetryService, telemetryDataCache)
{
- private readonly ITelemetryUserService telemetryUserService;
-
- public TelemetryDataSaubService(
- IAsbCloudDbContext db,
- ITelemetryService telemetryService,
- ITelemetryUserService telemetryUserService,
- ITelemetryDataCache telemetryDataCache)
- : base(db, telemetryService, telemetryDataCache)
- {
- this.telemetryUserService = telemetryUserService;
- }
-
- public async Task> GetTelemetryDataStatAsync(int idTelemetry, CancellationToken token)
- {
- var timezone = telemetryService.GetTimezone(idTelemetry);
- var timezoneOffset = TimeSpan.FromHours(timezone.Hours);
- int[] modes = new int[] { 0, 1, 3 };
-
- db.Database.SetCommandTimeout(TimeSpan.FromMinutes(1.5));
-
- var query = db.Set()
- .Where(t => t.IdTelemetry == idTelemetry)
- .Where(t => t.BlockPosition > 0.0001)
- .Where(t => t.WellDepth > 0.0001)
- .Where(t => modes.Contains(t.Mode))
- .Where(t => t.WellDepth - t.BitDepth < 0.01)
- .GroupBy(t => new {
- t.DateTime.Hour,
- WellDepthX10 = Math.Truncate(t.WellDepth * 10),
- t.Mode,
- t.IdFeedRegulator})
- .Select(g => new TelemetryDataSaubStatDto
- {
- Count = g.Count(),
- IdMode = g.Key.Mode,
- IdFeedRegulator = g.Key.IdFeedRegulator,
-
- DateMin = DateTime.SpecifyKind(g.Min(t => t.DateTime.UtcDateTime) + timezoneOffset, DateTimeKind.Unspecified),
- DateMax = DateTime.SpecifyKind(g.Max(t => t.DateTime.UtcDateTime) + timezoneOffset, DateTimeKind.Unspecified),
-
- WellDepthMin = g.Min(t => t.WellDepth),
- WellDepthMax = g.Max(t => t.WellDepth),
-
- Pressure = g.Average(t => t.Pressure),
- PressureSp = g.Average(t => t.PressureSp!.Value),
- PressureIdle = g.Average(t => t.PressureIdle!.Value),
- PressureDeltaLimitMax = g.Average(t => t.PressureDeltaLimitMax!.Value),
- PressureDelta = g.Average(t => t.Pressure - t.PressureIdle!.Value),
- PressureSpDelta = g.Average(t => t.PressureSp!.Value - t.PressureIdle!.Value),
-
- AxialLoad = g.Average(t => t.AxialLoad),
- AxialLoadSp = g.Average(t => t.AxialLoadSp!.Value),
- AxialLoadLimitMax = g.Average(t => t.AxialLoadLimitMax!.Value),
-
- RotorTorque = g.Average(t => t.RotorTorque),
- RotorTorqueSp = g.Average(t => t.RotorTorqueSp!.Value),
- RotorTorqueLimitMax = g.Average(t => t.RotorTorqueLimitMax!.Value),
-
- BlockSpeed = g.Average(t => t.BlockSpeed!.Value),
- BlockSpeedSp = g.Average(t => t.BlockSpeedSp!.Value),
- })
- .Where(s => s.WellDepthMin != s.WellDepthMax)
- .Where(s => s.Count > 3)
- .OrderBy(t => t.DateMin);
-
- return await query.ToArrayAsync(token);
- }
-
- public override TelemetryDataSaub Convert(TelemetryDataSaubDto src, double timezoneOffset)
- {
- var entity = src.Adapt();
- var telemetryUser = telemetryUserService
- .GetUsers(src.IdTelemetry, u => (u.Name == src.User || u.Surname == src.User))
- .FirstOrDefault();
- entity.IdUser = telemetryUser?.Id;
- entity.DateTime = src.DateTime.ToUtcDateTimeOffset(timezoneOffset);
- return entity;
- }
-
- public override TelemetryDataSaubDto Convert(TelemetryDataSaub src, double timezoneOffset)
- {
- var dto = src.Adapt();
- var telemetryUser = telemetryUserService.GetOrDefault(src.IdTelemetry, src.IdUser??int.MinValue);
- dto.User = telemetryUser?.MakeDisplayName();
- dto.DateTime = src.DateTime.ToRemoteDateTime(timezoneOffset);
- dto.BitDepth = src.BitDepth <= src.WellDepth
- ? src.BitDepth
- : src.WellDepth;
- return dto;
- }
-
- public async Task GetZippedCsv(int idWell, DateTime beginDate, DateTime endDate, CancellationToken token)
- {
- double intervalSec = (endDate - beginDate).TotalSeconds;
- if (intervalSec > 60*60*24*3)
- throw new ArgumentInvalidException(nameof(endDate), "Слишком большой диапазон");
-
- var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell)
- ?? throw new ArgumentInvalidException(nameof(idWell), $"Скважина id:{idWell} не содержит телеметрии");
-
- var approxPointsCount = intervalSec switch
- {
- < 2048 => 2048,
- < 8_192 => 4_096,
- < 131_072 => 16_384,
- _ => 32_768
- };
-
- var data = await GetAsync(idWell, beginDate, intervalSec, approxPointsCount, token );
-
- var fileName = $"DataSaub idWell{idWell}";
- if (telemetry.Info is not null)
- fileName += $" {telemetry.Info?.Cluster}, {telemetry.Info?.Well}";
- fileName += $" {beginDate:yyyy-MM-DDTHH-mm} - {endDate:yyyy-MM-DDTHH-mm}.csv";
-
- var outStream = new MemoryStream();
- using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
- {
- var entryFile = archive.CreateEntry(fileName, CompressionLevel.Optimal);
- using var entryStream = entryFile.Open();
- var serializer = new CsvSerializer();
- serializer.Serialize(data, entryStream);
- }
- outStream.Seek(0, SeekOrigin.Begin);
- return outStream;
- }
+ this.telemetryUserService = telemetryUserService;
}
+ public async Task> Get(int idTelemetry, bool isBitOnBottom, DateTimeOffset geDate, DateTimeOffset leDate, int take, CancellationToken token)
+ {
+ var offset = telemetryService.GetTimezone(idTelemetry).Offset;
+ var geDateUtc = geDate.ToUniversalTime();
+ var leDateUtc = leDate.ToUniversalTime();
+
+ var query = db.Set()
+ .Where(t => t.IdTelemetry == idTelemetry)
+ .Where(t => t.DateTime >= geDateUtc)
+ .Where(t => t.DateTime <= leDateUtc);
+
+ if (isBitOnBottom)
+ query = query.Where(t => Math.Abs(t.BitDepth - t.WellDepth) < 0.0001);
+
+ query = query
+ .OrderBy(t => t.DateTime)
+ .Take(take);
+
+ var entities = await query.ToArrayAsync(token);
+ var dtos = entities.Select(e => Convert(e, offset.TotalHours));
+ return dtos;
+ }
+
+ public async Task> GetTelemetryDataStatAsync(int idTelemetry, CancellationToken token)
+ {
+ var timezone = telemetryService.GetTimezone(idTelemetry);
+ var timezoneOffset = TimeSpan.FromHours(timezone.Hours);
+ int[] modes = new int[] { 0, 1, 3 };
+
+ db.Database.SetCommandTimeout(TimeSpan.FromMinutes(1.5));
+
+ var query = db.Set()
+ .Where(t => t.IdTelemetry == idTelemetry)
+ .Where(t => t.BlockPosition > 0.0001)
+ .Where(t => t.WellDepth > 0.0001)
+ .Where(t => modes.Contains(t.Mode))
+ .Where(t => t.WellDepth - t.BitDepth < 0.01)
+ .GroupBy(t => new
+ {
+ t.DateTime.Hour,
+ WellDepthX10 = Math.Truncate(t.WellDepth * 10),
+ t.Mode,
+ t.IdFeedRegulator
+ })
+ .Select(g => new TelemetryDataSaubStatDto
+ {
+ Count = g.Count(),
+ IdMode = g.Key.Mode,
+ IdFeedRegulator = g.Key.IdFeedRegulator,
+
+ DateMin = DateTime.SpecifyKind(g.Min(t => t.DateTime.UtcDateTime) + timezoneOffset, DateTimeKind.Unspecified),
+ DateMax = DateTime.SpecifyKind(g.Max(t => t.DateTime.UtcDateTime) + timezoneOffset, DateTimeKind.Unspecified),
+
+ WellDepthMin = g.Min(t => t.WellDepth),
+ WellDepthMax = g.Max(t => t.WellDepth),
+
+ Pressure = g.Average(t => t.Pressure),
+ PressureSp = g.Average(t => t.PressureSp!.Value),
+ PressureIdle = g.Average(t => t.PressureIdle!.Value),
+ PressureDeltaLimitMax = g.Average(t => t.PressureDeltaLimitMax!.Value),
+ PressureDelta = g.Average(t => t.Pressure - t.PressureIdle!.Value),
+ PressureSpDelta = g.Average(t => t.PressureSp!.Value - t.PressureIdle!.Value),
+
+ AxialLoad = g.Average(t => t.AxialLoad),
+ AxialLoadSp = g.Average(t => t.AxialLoadSp!.Value),
+ AxialLoadLimitMax = g.Average(t => t.AxialLoadLimitMax!.Value),
+
+ RotorTorque = g.Average(t => t.RotorTorque),
+ RotorTorqueSp = g.Average(t => t.RotorTorqueSp!.Value),
+ RotorTorqueLimitMax = g.Average(t => t.RotorTorqueLimitMax!.Value),
+
+ BlockSpeed = g.Average(t => t.BlockSpeed!.Value),
+ BlockSpeedSp = g.Average(t => t.BlockSpeedSp!.Value),
+ })
+ .Where(s => s.WellDepthMin != s.WellDepthMax)
+ .Where(s => s.Count > 3)
+ .OrderBy(t => t.DateMin);
+
+ return await query.ToArrayAsync(token);
+ }
+
+ public override TelemetryDataSaub Convert(TelemetryDataSaubDto src, double timezoneOffset)
+ {
+ var entity = src.Adapt();
+ var telemetryUser = telemetryUserService
+ .GetUsers(src.IdTelemetry, u => (u.Name == src.User || u.Surname == src.User))
+ .FirstOrDefault();
+ entity.IdUser = telemetryUser?.Id;
+ entity.DateTime = src.DateTime.ToUtcDateTimeOffset(timezoneOffset);
+ return entity;
+ }
+
+ public override TelemetryDataSaubDto Convert(TelemetryDataSaub src, double timezoneOffset)
+ {
+ var dto = src.Adapt();
+ var telemetryUser = telemetryUserService.GetOrDefault(src.IdTelemetry, src.IdUser ?? int.MinValue);
+ dto.User = telemetryUser?.MakeDisplayName();
+ dto.DateTime = src.DateTime.ToRemoteDateTime(timezoneOffset);
+ dto.BitDepth = src.BitDepth <= src.WellDepth
+ ? src.BitDepth
+ : src.WellDepth;
+ return dto;
+ }
+
+ public async Task GetZippedCsv(int idWell, DateTime beginDate, DateTime endDate, CancellationToken token)
+ {
+ double intervalSec = (endDate - beginDate).TotalSeconds;
+ if (intervalSec > 60 * 60 * 24 * 3)
+ throw new ArgumentInvalidException(nameof(endDate), "Слишком большой диапазон");
+
+ var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell)
+ ?? throw new ArgumentInvalidException(nameof(idWell), $"Скважина id:{idWell} не содержит телеметрии");
+
+ var approxPointsCount = intervalSec switch
+ {
+ < 2048 => 2048,
+ < 8_192 => 4_096,
+ < 131_072 => 16_384,
+ _ => 32_768
+ };
+
+ var data = await GetAsync(idWell, beginDate, intervalSec, approxPointsCount, token);
+
+ var fileName = $"DataSaub idWell{idWell}";
+ if (telemetry.Info is not null)
+ fileName += $" {telemetry.Info?.Cluster}, {telemetry.Info?.Well}";
+ fileName += $" {beginDate:yyyy-MM-DDTHH-mm} - {endDate:yyyy-MM-DDTHH-mm}.csv";
+
+ var outStream = new MemoryStream();
+ using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
+ {
+ var entryFile = archive.CreateEntry(fileName, CompressionLevel.Optimal);
+ using var entryStream = entryFile.Open();
+ var serializer = new CsvSerializer();
+ serializer.Serialize(data, entryStream);
+ }
+ outStream.Seek(0, SeekOrigin.Begin);
+ return outStream;
+ }
}
diff --git a/AsbCloudInfrastructure/Services/Subsystems/SubsystemService.cs b/AsbCloudInfrastructure/Services/Subsystems/SubsystemService.cs
index fd27efdf..93c5ab2f 100644
--- a/AsbCloudInfrastructure/Services/Subsystems/SubsystemService.cs
+++ b/AsbCloudInfrastructure/Services/Subsystems/SubsystemService.cs
@@ -45,17 +45,16 @@ internal class SubsystemService : ISubsystemService
if(!well.IdTelemetry.HasValue)
return Enumerable.Empty();
- var detectedOperationSummaryRequest = new DetectedOperationRequest
- {
+ var detectedOperationSummaryRequest = new DetectedOperationByWellRequest
+ {
IdWell = request.IdWell,
- IdsTelemetries = new[] { well.IdTelemetry.Value },
IdsCategories = WellOperationCategory.MechanicalDrillingSubIds,
GeDateStart = request.GeDate,
LeDateEnd = request.LeDate,
- GeDepth = request.GeDepth,
- LeDepth = request.LeDepth,
+ GeDepthStart = request.GeDepth,
+ LeDepthEnd = request.LeDepth,
};
var operations = await detectedOperationService.GetOperationsAsync(detectedOperationSummaryRequest,
@@ -78,7 +77,7 @@ internal class SubsystemService : ISubsystemService
return result;
}
- private async Task> CalcStatAsync(IEnumerable operations, CancellationToken token)
+ private async Task> CalcStatAsync(IEnumerable operations, CancellationToken token)
{
if (!subsystems.Any())
subsystems = (await subsystemRepository.GetAllAsync(token)).ToDictionary(s => s.Id, s => s);
@@ -92,7 +91,7 @@ internal class SubsystemService : ISubsystemService
return stat;
}
- private SubsystemStatDto CalcOscillationStat(IEnumerable operations)
+ private SubsystemStatDto CalcOscillationStat(IEnumerable operations)
{
operations = operations.Where(o => o.IdCategory == WellOperationCategory.IdSlide);
@@ -114,7 +113,7 @@ internal class SubsystemService : ISubsystemService
return oscillationStat;
}
- private IEnumerable CalcApdStat(IEnumerable operations)
+ private IEnumerable CalcApdStat(IEnumerable operations)
{
var apdRotorAndSlide = operations
.Where(o => WellOperationCategory.MechanicalDrillingSubIds.Contains(o.IdCategory))
@@ -169,7 +168,7 @@ internal class SubsystemService : ISubsystemService
}
private static (double SumDepthInterval, double UsedTimeHours, int Count) AggregateOperations(int idSubsystem,
- IEnumerable operations) =>
+ IEnumerable operations) =>
idSubsystem switch
{
IdSubsystemAPDRotor => CalcOperationsByEnableSubsystems(operations, EnabledSubsystemsFlags.AutoRotor),
@@ -180,7 +179,7 @@ internal class SubsystemService : ISubsystemService
};
private static (double SumDepthInterval, double UsedTimeHours, int OperationCount) CalcOperationsByEnableSubsystems(
- IEnumerable operations,
+ IEnumerable operations,
EnabledSubsystemsFlags enabledSubsystems)
{
var filtered = operations.Where(o => enabledSubsystems.HasEnabledSubsystems(o.EnabledSubsystems));
@@ -215,7 +214,7 @@ internal class SubsystemService : ISubsystemService
var leDateUtc = leDate?.ToUtcDateTimeOffset(hoursOffset);
- var request = new DetectedOperationRequest
+ var request = new DetectedOperationByWellRequest
{
IdWell = well.Id,
IdsCategories = WellOperationCategory.MechanicalDrillingSubIds,
diff --git a/AsbCloudInfrastructure/Services/WellService.cs b/AsbCloudInfrastructure/Services/WellService.cs
index befa8851..4d6ec10a 100644
--- a/AsbCloudInfrastructure/Services/WellService.cs
+++ b/AsbCloudInfrastructure/Services/WellService.cs
@@ -36,13 +36,18 @@ namespace AsbCloudInfrastructure.Services
.ThenInclude(r => r.Company)
.AsNoTracking();
- public WellService(IAsbCloudDbContext db, IMemoryCache memoryCache, ITelemetryService telemetryService, ITimezoneService timezoneService, WellInfoService wellInfoService)
+ public WellService(IAsbCloudDbContext db,
+ IMemoryCache memoryCache,
+ ITelemetryService telemetryService,
+ ITimezoneService timezoneService,
+ WellInfoService wellInfoService,
+ IWellOperationCategoryRepository wellOperationCategoryRepository)
: base(db, memoryCache, MakeQueryWell)
{
this.telemetryService = telemetryService;
this.timezoneService = timezoneService;
this.wellInfoService = wellInfoService;
- this.wellOperationRepository = new WellOperationRepository(db, memoryCache, this);
+ this.wellOperationRepository = new WellOperationRepository(db, memoryCache, this, wellOperationCategoryRepository);
companyTypesService = new CrudCacheRepositoryBase(dbContext, memoryCache);
}
diff --git a/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs b/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs
index 02f2d4cb..c6bd6dda 100644
--- a/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs
+++ b/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs
@@ -5,7 +5,6 @@ using AsbCloudApp.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudInfrastructure.Services.DetectOperations;
@@ -57,7 +56,7 @@ namespace AsbCloudWebApi.Controllers.SAUB
[HttpGet]
[ProducesResponseType(typeof(DetectedOperationListDto), (int)System.Net.HttpStatusCode.OK)]
public async Task GetAsync(
- [FromQuery] DetectedOperationRequest request,
+ [FromQuery] DetectedOperationByWellRequest request,
CancellationToken token)
{
if (!await UserHasAccessToWellAsync(request.IdWell, token))
@@ -76,7 +75,7 @@ namespace AsbCloudWebApi.Controllers.SAUB
[HttpGet("stat")]
[ProducesResponseType(typeof(IEnumerable), (int)System.Net.HttpStatusCode.OK)]
public async Task GetStatAsync(
- [FromQuery] DetectedOperationRequest request,
+ [FromQuery] DetectedOperationByWellRequest request,
CancellationToken token)
{
if (!await UserHasAccessToWellAsync(request.IdWell, token))
@@ -98,7 +97,7 @@ namespace AsbCloudWebApi.Controllers.SAUB
[Permission]
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
public async Task DeleteAsync(
- [FromQuery] DetectedOperationRequest request,
+ [FromQuery] DetectedOperationByWellRequest request,
CancellationToken token)
{
if (!await UserHasAccessToWellAsync(request.IdWell, token))