Merge branch 'dev' into feature/integration_tests

This commit is contained in:
Степанов Дмитрий 2023-12-28 10:42:40 +05:00
commit c0b15e1839
89 changed files with 37994 additions and 2021 deletions

View File

@ -64,7 +64,7 @@ public class DailyReportDto : IId,
/// <summary> /// <summary>
/// Дата формирования отчёта /// Дата формирования отчёта
/// </summary> /// </summary>
public DateTime Date { get; set; } public DateOnly Date { get; set; }
/// <summary> /// <summary>
/// Дата последнего обновления /// Дата последнего обновления

View File

@ -13,6 +13,11 @@ namespace AsbCloudApp.Data.DetectedOperation
/// <inheritdoc/> /// <inheritdoc/>
public int IdWell { get; set; } public int IdWell { get; set; }
/// <summary>
/// Id телеметрии
/// </summary>
public int IdTelemetry { get; set; }
/// <summary> /// <summary>
/// Id названия/описания операции /// Id названия/описания операции
/// </summary> /// </summary>
@ -72,5 +77,10 @@ namespace AsbCloudApp.Data.DetectedOperation
/// Ключевой параметр операции /// Ключевой параметр операции
/// </summary> /// </summary>
public double Value { get; set; } public double Value { get; set; }
/// <summary>
/// Флаг включенной подсистемы
/// </summary>
public int EnabledSubsystems { get; set; }
} }
} }

View File

@ -0,0 +1,50 @@
using System;
namespace AsbCloudApp.Data.DetectedOperation;
/// <summary>
/// Флаги включенных подсистем
/// </summary>
[Flags]
public enum EnabledSubsystemsFlags
{
/// <summary>
/// Автоподача долота
/// </summary>
AutoRotor = 1 << 0,
/// <summary>
/// БУРЕНИЕ В СЛАЙДЕ
/// </summary>
AutoSlide = 1 << 1,
/// <summary>
/// ПРОРАБОТКА
/// </summary>
AutoConditionig = 1 << 2,
/// <summary>
/// СПУСК СПО
/// </summary>
AutoSinking = 1 << 3,
/// <summary>
/// ПОДЪЕМ СПО
/// </summary>
AutoLifting = 1 << 4,
/// <summary>
/// ПОДЪЕМ С ПРОРАБОТКОЙ
/// </summary>
AutoLiftingWithConditionig = 1 << 5,
/// <summary>
/// блокировка
/// </summary>
AutoBlocknig = 1 << 6,
/// <summary>
/// осцилляция
/// </summary>
AutoOscillation = 1 << 7,
}

View File

@ -1,32 +0,0 @@
namespace AsbCloudApp.Data.DetectedOperation;
/// <summary>
/// Статистика по операциям
/// </summary>
public class OperationsSummaryDto
{
/// <summary>
/// Id телеметрии
/// </summary>
public int IdTelemetry { get; set; }
/// <summary>
/// Id названия/описания операции
/// </summary>
public int IdCategory { get; set; }
/// <summary>
/// Количество операций
/// </summary>
public int Count { get; set; }
/// <summary>
/// Cумма проходок операций
/// </summary>
public double SumDepthIntervals { get; set; }
/// <summary>
/// Cумма продолжительностей операций
/// </summary>
public double SumDurationHours { get; set; }
}

View File

@ -0,0 +1,18 @@
namespace AsbCloudApp.Data.Progress;
/// <summary>
/// DTO прогресса
/// </summary>
public class ProgressDto
{
/// <summary>
/// прогресс 0 - 100%
/// </summary>
public float Progress { get; set; }
/// <summary>
/// название текущей операции генерации
/// </summary>
public string? Operation { get; set; }
}

View File

@ -0,0 +1,29 @@
using System;
namespace AsbCloudApp.Data.Progress;
/// <summary>
/// DTO прогресса с ошибкой
/// </summary>
public class ProgressExceptionDto
{
/// <summary>
/// прогресс 0 - 100%
/// </summary>
public float Progress { get; set; }
/// <summary>
/// название текущей операции генерации
/// </summary>
public string? Operation { get; set; }
/// <summary>
/// Отображаемый текст ошибки
/// </summary>
public string Message { get; set; } = null!;
/// <summary>
/// Инфо об исключении
/// </summary>
public Exception Exception { get; set; } = null!;
}

View File

@ -0,0 +1,12 @@
namespace AsbCloudApp.Data.Progress;
/// <summary>
/// DTO завершенного прогресса генерации рапорта-диаграммы
/// </summary>
public class ReportProgressFinalDto : ReportProgressDto
{
/// <summary>
/// файл
/// </summary>
public FileInfoDto file { get; set; }
}

View File

@ -0,0 +1,17 @@
namespace AsbCloudApp.Data.Progress;
/// <summary>
/// DTO прогресса генерации рапорта-диаграммы
/// </summary>
public class ReportProgressDto : ProgressDto
{
/// <summary>
/// номер текущей страницы
/// </summary>
public int CurrentPage { get; set; }
/// <summary>
/// предполагаемое суммарное количество страниц
/// </summary>
public int TotalPages { get; set; }
}

View File

@ -1,28 +0,0 @@
namespace AsbCloudApp.Data
{
/// <summary>
/// DTO прогресса генерации рапорта-диаграммы
/// </summary>
public class ReportProgressDto
{
/// <summary>
/// прогресс 0 - 100%
/// </summary>
public float Progress { get; set; }
/// <summary>
/// название текущей операции генерации
/// </summary>
public string? Operation { get; set; }
/// <summary>
/// номер текущей страницы
/// </summary>
public int CurrentPage { get; set; }
/// <summary>
/// предполагаемое суммарное количество страниц
/// </summary>
public int TotalPages { get; set; }
}
}

View File

@ -1,6 +1,5 @@
using System.Collections.Generic; namespace AsbCloudApp.Data.Subsystems;
namespace AsbCloudApp.Data.Subsystems
{
/// <summary> /// <summary>
/// Статистика наработки подсистем по активным скважинам /// Статистика наработки подсистем по активным скважинам
/// </summary> /// </summary>
@ -11,20 +10,15 @@ namespace AsbCloudApp.Data.Subsystems
/// </summary> /// </summary>
public WellInfoDto Well { get; set; } = null!; public WellInfoDto Well { get; set; } = null!;
/// <summary> /// <summary>
/// Наработки подсистемы АКБ /// Наработки подсистемы АПД
/// </summary> /// </summary>
public SubsystemStatDto? SubsystemAKB { get; set; } public SubsystemStatDto? SubsystemAPD { get; set; }
/// <summary> /// <summary>
/// Наработки подсистемы МСЕ /// Наработки подсистемы с осцилляцией
/// </summary> /// </summary>
public SubsystemStatDto? SubsystemMSE { get; set; } public SubsystemStatDto? SubsystemOscillation { get; set; }
/// <summary>
/// Наработки подсистемы СПИН
/// </summary>
public SubsystemStatDto? SubsystemSpinMaster { get; set; }
/// <summary> /// <summary>
/// Наработки подсистемы ТОРК /// Наработки подсистемы ТОРК
/// </summary> /// </summary>
public SubsystemStatDto? SubsystemTorqueMaster { get; set; } public SubsystemStatDto? SubsystemTorqueMaster { get; set; }
} }
}

View File

@ -1,38 +0,0 @@
using System;
namespace AsbCloudApp.Data.Subsystems
{
/// <summary>
/// Модель информации о работе подсистемы
/// </summary>
public class SubsystemOperationTimeDto:IId
{
/// <summary>
/// Идентификатор
/// </summary>
public int Id { get; set; }
/// <summary>
/// идентификатор подсистемы
/// </summary>
public int IdSubsystem { get; set; }
/// <summary>
/// Название подсистемы
/// </summary>
public string SubsystemName { get; set; } = null!;
/// <summary>
/// дата/время включения подсистемы
/// </summary>
public DateTime DateStart { get; set; }
/// <summary>
/// дата/время выключения подсистемы
/// </summary>
public DateTime DateEnd { get; set; }
/// <summary>
/// глубина забоя на момент включения подсистемы
/// </summary>
public double DepthStart { get; set; }
/// <summary>
/// глубина забоя на момент выключения подсистемы
/// </summary>
public double DepthEnd { get; set; }
}
}

View File

@ -1,5 +1,7 @@
using AsbCloudApp.Data.SAUB; using AsbCloudApp.Data.SAUB;
using System; using System;
using System.Collections.Generic;
using System.Linq;
namespace AsbCloudApp.Data namespace AsbCloudApp.Data
{ {
@ -37,6 +39,11 @@ namespace AsbCloudApp.Data
/// Дата полседнего получения данных от станции контроля параметров цементирования (СКЦ) /// Дата полседнего получения данных от станции контроля параметров цементирования (СКЦ)
/// </summary> /// </summary>
public DateTime? LastDataCpmsDate { get; set; } public DateTime? LastDataCpmsDate { get; set; }
/// <summary>
/// Компании
/// </summary>
public IEnumerable<CompanyDto> Companies { get; set; } = Enumerable.Empty<CompanyDto>();
} }
/// <summary> /// <summary>

View File

@ -29,5 +29,5 @@ public interface IDailyReportRepository : ICrudRepository<DailyReportDto>
/// <param name="date"></param> /// <param name="date"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
Task<DailyReportDto?> GetOrDefaultAsync(int idWell, DateTime date, CancellationToken cancellationToken); Task<DailyReportDto?> GetOrDefaultAsync(int idWell, DateOnly date, CancellationToken cancellationToken);
} }

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace AsbCloudApp.Requests namespace AsbCloudApp.Requests
{ {
@ -15,35 +16,41 @@ namespace AsbCloudApp.Requests
[Required] [Required]
public int IdWell { get; set; } public int IdWell { get; set; }
/// <summary>
/// Список id телеметрий
/// пустой список - нет фильтрации
/// </summary>
public IEnumerable<int> IdsTelemetries { get; set; } = Array.Empty<int>();
/// <summary> /// <summary>
/// категории операций /// категории операций
/// </summary> /// </summary>
public IEnumerable<int>? IdsCategories { get; set; } public IEnumerable<int> IdsCategories { get; set; } = Array.Empty<int>();
/// <summary> /// <summary>
/// Больше или равно дате /// Больше или равно дате
/// </summary> /// </summary>
public DateTime? GtDate { get; set; } public DateTimeOffset? GeDateStart { get; set; }
/// <summary> /// <summary>
/// Меньше или равно дате /// Меньше или равно дате
/// </summary> /// </summary>
public DateTime? LtDate { get; set; } public DateTimeOffset? LeDateEnd { get; set; }
/// <summary> /// <summary>
/// Больше или равно глубины забоя /// Больше или равно глубины забоя
/// </summary> /// </summary>
public double? GtDepth { get; set; } public double? GeDepth { get; set; }
/// <summary> /// <summary>
/// Меньше или равно глубины забоя /// Меньше или равно глубины забоя
/// </summary> /// </summary>
public double? LtDepth { get; set; } public double? LeDepth { get; set; }
/// <summary> /// <summary>
/// Фильтр по пользователю панели /// Фильтр по пользователю панели
/// </summary> /// </summary>
public int? EqIdTelemetryUser { get; set; } public int? IdTelemetryUser { get; set; }
} }
} }

View File

@ -1,53 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace AsbCloudApp.Requests;
/// <summary>
/// Запрос на получение обобщенных данных по операцим
/// </summary>
public class DetectedOperationSummaryRequest
{
/// <summary>
/// Список id телеметрий
/// пустой список - нет фильтрации
/// </summary>
public IEnumerable<int> IdsTelemetries { get;set;} = Enumerable.Empty<int>();
/// <summary>
/// Список id категорий операций
/// пустой список - нет фильтрации
/// </summary>
public IEnumerable<int> IdsOperationCategories { get; set; } = Enumerable.Empty<int>();
/// <summary>
/// Больше или равно даты начала операции
/// </summary>
public DateTimeOffset? GeDateStart {get;set;}
/// <summary>
/// Меньше или равно даты начала операции
/// </summary>
public DateTimeOffset? LeDateStart { get; set; }
/// <summary>
/// Меньше или равно даты окончания операции
/// </summary>
public DateTimeOffset? LeDateEnd { get; set; }
/// <summary>
/// Больше или равно глубины начала операции
/// </summary>
public double? GeDepthStart { get; set; }
/// <summary>
/// Меньше или равно глубины начала операции
/// </summary>
public double? LeDepthStart { get; set; }
/// <summary>
/// Меньше или равно глубины окончания операции
/// </summary>
public double? LeDepthEnd { get; set; }
}

View File

@ -1,94 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace AsbCloudApp.Requests
{
/// <summary>
/// класс с фильтрами для запроса
/// </summary>
public class SubsystemOperationTimeRequest: RequestBase, IValidatableObject
{
private static readonly DateTime validationMinDate = new DateTime(2020,01,01,0,0,0,DateTimeKind.Utc);
/// <summary>
/// идентификатор скважины
/// </summary>
[Required]
public int IdWell { get; set; }
/// <summary>
/// идентификатор подсистемы
/// </summary>
public IEnumerable<int> IdsSubsystems { get; set; } = Enumerable.Empty<int>();
/// <summary>
/// Больше или равно дате
/// </summary>
public DateTime? GtDate { get; set; }//TODO: its Ge*
/// <summary>
/// Меньше или равно дате
/// </summary>
public DateTime? LtDate { get; set; }
/// <summary>
/// Больше или равно глубины забоя
/// </summary>
public double? GtDepth { get; set; }
/// <summary>
/// Меньше или равно глубины забоя
/// </summary>
public double? LtDepth { get; set; }
//TODO: Replace modes by DateTimeOffset LeDateStart, LeDateEnd
/// <summary>
/// информация попадает в выборку, если интервал выборки частично или полностью пересекается с запрашиваемым интервалом
/// </summary>
public const int SelectModeOuter = 0;
/// <summary>
/// информация попадает в выборку, если интервал выборки строго полностью пересекается с запрашиваемым интервалом.
/// </summary>
public const int SelectModeInner = 1;
/// <summary>
/// аналогично outer, но интервалы в частично пересекающиеся укорачиваются по границам интервала выборки.
/// </summary>
public const int SelectModeTrim = 2;
/// <summary>
/// Режим выборки элементов
/// </summary>
public int SelectMode { get; set; } = SelectModeOuter;
/// <inheritdoc/>
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (GtDate.HasValue && GtDate < validationMinDate)
yield return new ValidationResult(
$"Должно быть больше {validationMinDate:O})",
new[] { nameof(GtDate) });
if (LtDate.HasValue && GtDate.HasValue)
{
if (LtDate < GtDate)
yield return new ValidationResult(
$"{nameof(LtDate)} должно быть больше {nameof(GtDate)}. ({LtDate:O} < {GtDate:O})",
new[] { nameof(LtDate), nameof(GtDate) });
}
if (LtDepth.HasValue && GtDepth.HasValue)
{
if (LtDepth < GtDepth)
yield return new ValidationResult(
$"{nameof(LtDepth)} должно быть больше {nameof(GtDepth)}. ({LtDepth} < {GtDepth})",
new[] { nameof(LtDepth), nameof(GtDepth) });
}
yield break;
}
}
}

View File

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace AsbCloudApp.Requests
{
/// <summary>
/// класс с фильтрами для запроса
/// </summary>
public class SubsystemRequest: RequestBase, IValidatableObject
{
private static readonly DateTime validationMinDate = new DateTime(2020,01,01,0,0,0,DateTimeKind.Utc);
/// <summary>
/// идентификатор скважины
/// </summary>
[Required]
public int IdWell { get; set; }
/// <summary>
/// Идентификатор бурильщика
/// </summary>
public int? IdDriller { get; set; }
/// <summary>
/// Больше или равно дате
/// </summary>
public DateTimeOffset? GeDate { get; set; }
/// <summary>
/// Меньше или равно дате
/// </summary>
public DateTimeOffset? LeDate { get; set; }
/// <summary>
/// Больше или равно глубины забоя
/// </summary>
public double? GeDepth { get; set; }
/// <summary>
/// Меньше или равно глубины забоя
/// </summary>
public double? LeDepth { get; set; }
/// <inheritdoc/>
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (GeDate.HasValue && GeDate < validationMinDate)
yield return new ValidationResult(
$"Должно быть больше {validationMinDate:O})",
new[] { nameof(GeDate) });
if (LeDate.HasValue && GeDate.HasValue)
{
if (LeDate < GeDate)
yield return new ValidationResult(
$"{nameof(LeDate)} должно быть больше {nameof(GeDate)}. ({LeDate:O} < {GeDate:O})",
new[] { nameof(LeDate), nameof(GeDate) });
}
if (LeDepth.HasValue && GeDepth.HasValue)
{
if (LeDepth < GeDepth)
yield return new ValidationResult(
$"{nameof(LeDepth)} должно быть больше {nameof(GeDepth)}. ({LeDepth} < {GeDepth})",
new[] { nameof(LeDepth), nameof(GeDepth) });
}
yield break;
}
}
}

View File

@ -14,8 +14,8 @@ public interface IDailyReportExportService
/// Экспортировать /// Экспортировать
/// </summary> /// </summary>
/// <param name="idWell"></param> /// <param name="idWell"></param>
/// <param name="dailyReportDateStart"></param> /// <param name="dailyReportDate"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
Task<(string FileName, Stream File)> ExportAsync(int idWell, DateTime dailyReportDateStart, CancellationToken cancellationToken); Task<(string FileName, Stream File)> ExportAsync(int idWell, DateOnly dailyReportDate, CancellationToken cancellationToken);
} }

View File

@ -21,7 +21,7 @@ public interface IDailyReportService
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <param name="idWell"></param> /// <param name="idWell"></param>
/// <returns></returns> /// <returns></returns>
Task<int> UpdateOrInsertAsync<TBlock>(int idWell, DateTime dateDailyReport, int idUser, TBlock editableBlock, Task<int> UpdateOrInsertAsync<TBlock>(int idWell, DateOnly dateDailyReport, int idUser, TBlock editableBlock,
CancellationToken cancellationToken) CancellationToken cancellationToken)
where TBlock : ItemInfoDto; where TBlock : ItemInfoDto;
@ -32,7 +32,7 @@ public interface IDailyReportService
/// <param name="dateDailyReport"></param> /// <param name="dateDailyReport"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
Task<DailyReportDto> GetAsync(int idWell, DateTime dateDailyReport, CancellationToken cancellationToken); Task<DailyReportDto> GetAsync(int idWell, DateOnly dateDailyReport, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Получить список суточных отчётов по скважине /// Получить список суточных отчётов по скважине

View File

@ -35,15 +35,7 @@ namespace AsbCloudApp.Services
/// <param name="request"></param> /// <param name="request"></param>
/// <param name="token"></param> /// <param name="token"></param>
/// <returns></returns> /// <returns></returns>
Task<IEnumerable<DetectedOperationDto>?> GetOperationsAsync(DetectedOperationRequest request, CancellationToken token); Task<IEnumerable<DetectedOperationDto>> GetOperationsAsync(DetectedOperationRequest request, CancellationToken token);
/// <summary>
/// Получить интервалы глубин по всем скважинам
/// </summary>
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns>кортеж - ид телеметрии, интервалы глубины забоя (ротор,слайд) </returns>
Task<IEnumerable<OperationsSummaryDto>> GetOperationSummaryAsync(DetectedOperationSummaryRequest request, CancellationToken token);
/// <summary> /// <summary>
/// Удалить операции /// Удалить операции

View File

@ -1,4 +1,6 @@
using AsbCloudApp.Data; using AsbCloudApp.Data;
using AsbCloudApp.Data.Progress;
using AsbCloudApp.Requests;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
@ -11,26 +13,30 @@ namespace AsbCloudApp.Services
/// </summary> /// </summary>
public interface IReportService public interface IReportService
{ {
/// <summary>
/// категория рапорта
/// </summary>
int ReportCategoryId { get; }
/// <summary> /// <summary>
/// Поставить рапорт в очередь на формирование /// Поставить рапорт в очередь на формирование
/// </summary> /// </summary>
/// <param name="idWell"></param> /// <param name="idWell"></param>
/// <param name="idUser"></param> /// <param name="idUser"></param>
/// <param name="stepSeconds"></param> /// <param name="request"></param>
/// <param name="format"></param>
/// <param name="begin"></param>
/// <param name="end"></param>
/// <param name="handleReportProgress"></param> /// <param name="handleReportProgress"></param>
/// <returns></returns> /// <returns></returns>
string EnqueueCreateReportWork(int idWell, int idUser, int stepSeconds, string EnqueueCreateReportWork(int idWell, int idUser, ReportParametersRequest request,
int format, DateTime begin, DateTime end,
Action<object, string> handleReportProgress); Action<object, string> handleReportProgress);
/// <summary>
/// Создание отчета
/// </summary>
/// <param name="workId"></param>
/// <param name="idWell"></param>
/// <param name="idUser"></param>
/// <param name="request"></param>
/// <param name="progressHandler"></param>
/// <param name="token"></param>
/// <returns></returns>
Task CreateReportAsync(string workId, int idWell, int idUser, ReportParametersRequest request, Action<ProgressDto, string> progressHandler, CancellationToken token);
/// <summary> /// <summary>
/// Получить предполагаемый список страниц рапорта /// Получить предполагаемый список страниц рапорта
/// </summary> /// </summary>

View File

@ -0,0 +1,40 @@
using AsbCloudApp.Data.Subsystems;
using AsbCloudApp.Requests;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudApp.Services;
/// <summary>
/// Получение инфо о наработке подсистем
/// </summary>
public interface ISubsystemService
{
/// <summary>
/// Статистика о наработке подсистем
/// </summary>
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<SubsystemStatDto>> GetStatAsync(SubsystemRequest request, CancellationToken token);
/// <summary>
/// Получение статистики по наработке подсистем по активным скважинам
/// </summary>
/// <param name="idCompany"></param>
/// <param name="gtDate"></param>
/// <param name="ltDate"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<SubsystemActiveWellStatDto>> GetStatByActiveWells(int idCompany, DateTime? gtDate, DateTime? ltDate, CancellationToken token);
/// <summary>
/// Получение статистики по наработке подсистем по активным скважинам
/// </summary>
/// <param name="wellIds"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<SubsystemActiveWellStatDto>> GetStatByActiveWells(IEnumerable<int> wellIds, CancellationToken token);
}

View File

@ -1,68 +0,0 @@
using AsbCloudApp.Data;
using AsbCloudApp.Data.Subsystems;
using AsbCloudApp.Requests;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudApp.Services.Subsystems
{
/// <summary>
/// Получение инфо о наработке подсистем
/// </summary>
public interface ISubsystemOperationTimeService
{
/// <summary>
/// Статистика о наработке подсистем
/// </summary>
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<SubsystemStatDto>> GetStatAsync(SubsystemOperationTimeRequest request, CancellationToken token);
/// <summary>
/// Удаление наработки по подсистемам.
/// Если удаляется конец, то фоновый сервис подсчета наработки восстановит эти данные.
/// Может потребоваться для запуска повторного расчета по новому алгоритму.
/// </summary>
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> DeleteAsync(SubsystemOperationTimeRequest request, CancellationToken token);
/// <summary>
/// Интервалы работы подсистем
/// </summary>
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<SubsystemOperationTimeDto>> GetOperationTimeAsync(SubsystemOperationTimeRequest request, CancellationToken token);
/// <summary>
/// Временной диапазон за который есть статистика работы подсистем
/// </summary>
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<DatesRangeDto?> GetDateRangeOperationTimeAsync(SubsystemOperationTimeRequest request, CancellationToken token);
/// <summary>
/// Получение статистики по наработке подсистем по активным скважинам
/// </summary>
/// <param name="idCompany"></param>
/// <param name="gtDate"></param>
/// <param name="ltDate"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<SubsystemActiveWellStatDto>> GetStatByActiveWells(int idCompany, DateTime? gtDate, DateTime? ltDate, CancellationToken token);
/// <summary>
/// Получение статистики по наработке подсистем по активным скважинам
/// </summary>
/// <param name="wellIds"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<SubsystemActiveWellStatDto>> GetStatByActiveWells(IEnumerable<int> wellIds, CancellationToken token);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,71 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class Update_Subsystems : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "t_subsystem_operation_time");
migrationBuilder.DeleteData(
table: "t_subsystem",
keyColumn: "id",
keyValue: 2);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "t_subsystem_operation_time",
columns: table => new
{
id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
id_subsystem = table.Column<int>(type: "integer", nullable: false),
id_telemetry = table.Column<int>(type: "integer", nullable: false, comment: "ИД телеметрии по которой выдается информация"),
date_end = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "дата/время выключения подсистемы"),
date_start = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "дата/время включения подсистемы"),
depth_end = table.Column<float>(type: "real", nullable: true, comment: "глубина забоя на момент выключения подсистемы"),
depth_start = table.Column<float>(type: "real", nullable: true, comment: "глубина забоя на момент включения подсистемы")
},
constraints: table =>
{
table.PrimaryKey("PK_t_subsystem_operation_time", x => x.id);
table.ForeignKey(
name: "FK_t_subsystem_operation_time_t_subsystem_id_subsystem",
column: x => x.id_subsystem,
principalTable: "t_subsystem",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_t_subsystem_operation_time_t_telemetry_id_telemetry",
column: x => x.id_telemetry,
principalTable: "t_telemetry",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
},
comment: "наработки подсистем");
migrationBuilder.InsertData(
table: "t_subsystem",
columns: new[] { "id", "description", "name" },
values: new object[] { 2, "Алгоритм поиска оптимальных параметров бурения САУБ", "MSE" });
migrationBuilder.CreateIndex(
name: "IX_t_subsystem_operation_time_id_subsystem",
table: "t_subsystem_operation_time",
column: "id_subsystem");
migrationBuilder.CreateIndex(
name: "IX_t_subsystem_operation_time_id_telemetry",
table: "t_subsystem_operation_time",
column: "id_telemetry");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,49 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class Update_DetectedOperations_And_Subsystems : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<int>(
name: "enabled_subsystems",
table: "t_detected_operation",
type: "integer",
nullable: false,
comment: "флаги включенных подсистем",
oldClrType: typeof(int),
oldType: "integer",
oldComment: "флаги аключенных подсистем");
migrationBuilder.UpdateData(
table: "t_subsystem",
keyColumn: "id",
keyValue: 1,
column: "name",
value: "АПД");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<int>(
name: "enabled_subsystems",
table: "t_detected_operation",
type: "integer",
nullable: false,
comment: "флаги аключенных подсистем",
oldClrType: typeof(int),
oldType: "integer",
oldComment: "флаги включенных подсистем");
migrationBuilder.UpdateData(
table: "t_subsystem",
keyColumn: "id",
keyValue: 1,
column: "name",
value: "АКБ");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,36 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class UpdateDateFormat_DailyReport : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<DateOnly>(
name: "date",
table: "t_daily_report",
type: "date",
nullable: false,
comment: "Дата формирования отчёта",
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldComment: "Дата формирования отчёта");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<DateTime>(
name: "date",
table: "t_daily_report",
type: "timestamp with time zone",
nullable: false,
comment: "Дата формирования отчёта",
oldClrType: typeof(DateOnly),
oldType: "date",
oldComment: "Дата формирования отчёта");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class Update_Table_WellOperation_set_Categories : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql
(@"UPDATE public.t_well_operation SET id_category=5010 WHERE id_category=4004; " +
@"UPDATE public.t_well_operation SET id_category=5019 WHERE id_category=4007; ");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}

View File

@ -1,12 +1,12 @@
// <auto-generated /> // <auto-generated />
using System; using System;
using System.Collections.Generic;
using System.Text.Json; using System.Text.Json;
using AsbCloudDb.Model; using AsbCloudDb.Model;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using System.Collections.Generic;
#nullable disable #nullable disable
@ -278,8 +278,8 @@ namespace AsbCloudDb.Migrations
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id")); NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTime>("Date") b.Property<DateOnly>("Date")
.HasColumnType("timestamp with time zone") .HasColumnType("date")
.HasColumnName("date") .HasColumnName("date")
.HasComment("Дата формирования отчёта"); .HasComment("Дата формирования отчёта");
@ -386,7 +386,7 @@ namespace AsbCloudDb.Migrations
b.Property<int>("EnabledSubsystems") b.Property<int>("EnabledSubsystems")
.HasColumnType("integer") .HasColumnType("integer")
.HasColumnName("enabled_subsystems") .HasColumnName("enabled_subsystems")
.HasComment("флаги аключенных подсистем"); .HasComment("флаги включенных подсистем");
b.Property<IDictionary<string, object>>("ExtraData") b.Property<IDictionary<string, object>>("ExtraData")
.IsRequired() .IsRequired()
@ -4543,7 +4543,7 @@ namespace AsbCloudDb.Migrations
b.HasComment("Запросы на изменение уставок панели оператора"); b.HasComment("Запросы на изменение уставок панели оператора");
}); });
modelBuilder.Entity("AsbCloudDb.Model.Subsystems.Subsystem", b => modelBuilder.Entity("AsbCloudDb.Model.Subsystem", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -4574,7 +4574,7 @@ namespace AsbCloudDb.Migrations
{ {
Id = 1, Id = 1,
Description = "Совместная работа режимов \"Бурение в роторе\" и \"Бурение в слайде\"", Description = "Совместная работа режимов \"Бурение в роторе\" и \"Бурение в слайде\"",
Name = "АКБ" Name = "АПД"
}, },
new new
{ {
@ -4589,12 +4589,6 @@ namespace AsbCloudDb.Migrations
Name = "АПД слайд" Name = "АПД слайд"
}, },
new new
{
Id = 2,
Description = "Алгоритм поиска оптимальных параметров бурения САУБ",
Name = "MSE"
},
new
{ {
Id = 65536, Id = 65536,
Description = "Осцилляция", Description = "Осцилляция",
@ -4608,55 +4602,6 @@ namespace AsbCloudDb.Migrations
}); });
}); });
modelBuilder.Entity("AsbCloudDb.Model.Subsystems.SubsystemOperationTime", 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<float?>("DepthEnd")
.HasColumnType("real")
.HasColumnName("depth_end")
.HasComment("глубина забоя на момент выключения подсистемы");
b.Property<float?>("DepthStart")
.HasColumnType("real")
.HasColumnName("depth_start")
.HasComment("глубина забоя на момент включения подсистемы");
b.Property<int>("IdSubsystem")
.HasColumnType("integer")
.HasColumnName("id_subsystem");
b.Property<int>("IdTelemetry")
.HasColumnType("integer")
.HasColumnName("id_telemetry")
.HasComment("ИД телеметрии по которой выдается информация");
b.HasKey("Id");
b.HasIndex("IdSubsystem");
b.HasIndex("IdTelemetry");
b.ToTable("t_subsystem_operation_time");
b.HasComment("наработки подсистем");
});
modelBuilder.Entity("AsbCloudDb.Model.Telemetry", b => modelBuilder.Entity("AsbCloudDb.Model.Telemetry", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
@ -8683,25 +8628,6 @@ namespace AsbCloudDb.Migrations
b.Navigation("Well"); b.Navigation("Well");
}); });
modelBuilder.Entity("AsbCloudDb.Model.Subsystems.SubsystemOperationTime", b =>
{
b.HasOne("AsbCloudDb.Model.Subsystems.Subsystem", "Subsystem")
.WithMany()
.HasForeignKey("IdSubsystem")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("AsbCloudDb.Model.Telemetry", "Telemetry")
.WithMany()
.HasForeignKey("IdTelemetry")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Subsystem");
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")

View File

@ -1,5 +1,4 @@
using AsbCloudDb.Model.GTR; using AsbCloudDb.Model.GTR;
using AsbCloudDb.Model.Subsystems;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -38,7 +37,6 @@ namespace AsbCloudDb.Model
public virtual DbSet<ReportProperty> ReportProperties => Set<ReportProperty>(); public virtual DbSet<ReportProperty> ReportProperties => Set<ReportProperty>();
public virtual DbSet<SetpointsRequest> SetpointsRequests => Set<SetpointsRequest>(); public virtual DbSet<SetpointsRequest> SetpointsRequests => Set<SetpointsRequest>();
public virtual DbSet<Subsystem> Subsystems => Set<Subsystem>(); public virtual DbSet<Subsystem> Subsystems => Set<Subsystem>();
public virtual DbSet<SubsystemOperationTime> SubsystemOperationTimes => Set<SubsystemOperationTime>();
public virtual DbSet<Telemetry> Telemetries => Set<Telemetry>(); public virtual DbSet<Telemetry> Telemetries => Set<Telemetry>();
public virtual DbSet<TelemetryDataSaub> TelemetryDataSaub => Set<TelemetryDataSaub>(); public virtual DbSet<TelemetryDataSaub> TelemetryDataSaub => Set<TelemetryDataSaub>();
public virtual DbSet<TelemetryDataSaubStat> TelemetryDataSaubStats => Set<TelemetryDataSaubStat>(); public virtual DbSet<TelemetryDataSaubStat> TelemetryDataSaubStats => Set<TelemetryDataSaubStat>();

View File

@ -20,8 +20,8 @@ public class DailyReport : IId
[Column("date_last_update", TypeName = "timestamp with time zone"), Comment("Дата последнего обновления")] [Column("date_last_update", TypeName = "timestamp with time zone"), Comment("Дата последнего обновления")]
public DateTime? DateLastUpdate { get; set; } public DateTime? DateLastUpdate { get; set; }
[Column("date", TypeName = "timestamp with time zone"), Comment("Дата формирования отчёта")] [Column("date", TypeName = "date"), Comment("Дата формирования отчёта")]
public DateTime Date { get; set; } public DateOnly Date { get; set; }
[Column("sign_block", TypeName = "jsonb"), Comment("Подпись")] [Column("sign_block", TypeName = "jsonb"), Comment("Подпись")]
public SignBlock? SignBlock { get; set; } public SignBlock? SignBlock { get; set; }

View File

@ -24,7 +24,7 @@ namespace AsbCloudDb.Model.DefaultData
{ typeof(WellType), new EntityFillerWellType()}, { typeof(WellType), new EntityFillerWellType()},
{ typeof(MeasureCategory), new EntityFillerMeasureCategory()}, { typeof(MeasureCategory), new EntityFillerMeasureCategory()},
{ typeof(CompanyType), new EntityFillerCompanyType()}, { typeof(CompanyType), new EntityFillerCompanyType()},
{ typeof(Subsystems.Subsystem), new EntityFillerSubsystem() }, { typeof(Subsystem), new EntityFillerSubsystem() },
{ typeof(NotificationCategory), new EntityNotificationCategory()}, { typeof(NotificationCategory), new EntityNotificationCategory()},
}; };
return fillers; return fillers;

View File

@ -1,14 +1,12 @@
using AsbCloudDb.Model.Subsystems; namespace AsbCloudDb.Model.DefaultData
namespace AsbCloudDb.Model.DefaultData
{ {
internal class EntityFillerSubsystem : EntityFiller<Subsystem> internal class EntityFillerSubsystem : EntityFiller<Subsystem>
{ {
public override Subsystem[] GetData() => new Subsystem[]{ public override Subsystem[] GetData() => new Subsystem[]{
// САУБ - ид подсистем с 1 до 65_535 // САУБ - ид подсистем с 1 до 65_535
new () {Id = 1, Name = "АКБ", Description = "Совместная работа режимов \"Бурение в роторе\" и \"Бурение в слайде\""}, new () {Id = 1, Name = "АПД", Description = "Совместная работа режимов \"Бурение в роторе\" и \"Бурение в слайде\""},
new () {Id = 11, Name = "АПД ротор", Description = "Режим работы \"Бурение в роторе\""}, new () {Id = 11, Name = "АПД ротор", Description = "Режим работы \"Бурение в роторе\""},
new () {Id = 12, Name = "АПД слайд", Description = "Режим работы \"Бурение в слайде\""}, new () {Id = 12, Name = "АПД слайд", Description = "Режим работы \"Бурение в слайде\""},
new () {Id = 2, Name = "MSE", Description = "Алгоритм поиска оптимальных параметров бурения САУБ"},
//Spin master - id подсистем с 65_536 до 131_071 //Spin master - id подсистем с 65_536 до 131_071
new () {Id = 65536, Name = "Осцилляция", Description = "Осцилляция"}, new () {Id = 65536, Name = "Осцилляция", Description = "Осцилляция"},
new () {Id = 65537, Name = "Демпфер", Description = "Демпфер"} new () {Id = 65537, Name = "Демпфер", Description = "Демпфер"}

View File

@ -4,6 +4,6 @@ public class EntityNotificationCategory : EntityFiller<NotificationCategory>
{ {
public override NotificationCategory[] GetData() => new NotificationCategory[] public override NotificationCategory[] GetData() => new NotificationCategory[]
{ {
new() { Id = 1, Name = "Системные уведомления" } new() { Id = NotificationCategory.IdSystemNotificationCategory, Name = "Системные уведомления" }
}; };
} }

View File

@ -41,7 +41,7 @@ namespace AsbCloudDb.Model
[Column("value"), Comment("Ключевой показатель операции")] [Column("value"), Comment("Ключевой показатель операции")]
public double Value { get; set; } public double Value { get; set; }
[Column("enabled_subsystems"), Comment("флаги аключенных подсистем")] [Column("enabled_subsystems"), Comment("флаги включенных подсистем")]
public int EnabledSubsystems { get; set; } public int EnabledSubsystems { get; set; }
[Column("extra_data", TypeName = "jsonb"), Comment("доп. инфо по операции")] [Column("extra_data", TypeName = "jsonb"), Comment("доп. инфо по операции")]
@ -57,57 +57,5 @@ namespace AsbCloudDb.Model
public override string ToString() public override string ToString()
=> $"{IdCategory}\t{DateStart:G}\t{DateEnd:G}\t{DurationMinutes:#0.#}\t{DepthStart:#0.#}\t{DepthEnd:#0.#}"; => $"{IdCategory}\t{DateStart:G}\t{DateEnd:G}\t{DurationMinutes:#0.#}\t{DepthStart:#0.#}\t{DepthEnd:#0.#}";
/// <summary>
/// Флаги аключенных подсистем
/// </summary>
[Flags]
public enum EnabledSubsystemsFlags
{
/// <summary>
/// Автоподача долота
/// </summary>
AutoRotor = 1 << 0,
/// <summary>
/// БУРЕНИЕ В СЛАЙДЕ
/// </summary>
AutoSlide = 1 << 1,
/// <summary>
/// ПРОРАБОТКА
/// </summary>
AutoConditionig = 1 << 2,
/// <summary>
/// СПУСК СПО
/// </summary>
AutoSinking = 1 << 3,
/// <summary>
/// ПОДЪЕМ СПО
/// </summary>
AutoLifting = 1 << 4,
/// <summary>
/// ПОДЪЕМ С ПРОРАБОТКОЙ
/// </summary>
AutoLiftingWithConditionig = 1 << 5,
/// <summary>
/// блокировка
/// </summary>
AutoBlocknig = 1 << 6,
}
/// <summary>
/// Есть ли флаг подсистемы у операции
/// </summary>
/// <param name="flag"></param>
/// <returns></returns>
public bool HasSubsystemFlag(EnabledSubsystemsFlags flag)
=> HasSubsystemFlag((int)flag);
/// <summary>
/// Есть ли флаг/флаги подсистемы у операции
/// </summary>
/// <param name="flags"></param>
/// <returns></returns>
public bool HasSubsystemFlag(int flags)
=> (EnabledSubsystems & flags) > 0;
} }
} }

View File

@ -1,5 +1,4 @@
using AsbCloudDb.Model.GTR; using AsbCloudDb.Model.GTR;
using AsbCloudDb.Model.Subsystems;
using AsbCloudDb.Model.WITS; using AsbCloudDb.Model.WITS;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.ChangeTracking;
@ -41,7 +40,6 @@ namespace AsbCloudDb.Model
DbSet<RelationContactWell> RelationContactsWells { get; } DbSet<RelationContactWell> RelationContactsWells { get; }
DbSet<ReportProperty> ReportProperties { get; } DbSet<ReportProperty> ReportProperties { get; }
DbSet<Subsystem> Subsystems { get; } DbSet<Subsystem> Subsystems { get; }
DbSet<SubsystemOperationTime> SubsystemOperationTimes { get; }
DbSet<Telemetry> Telemetries { get; } DbSet<Telemetry> Telemetries { get; }
DbSet<TelemetryDataSaub> TelemetryDataSaub { get; } DbSet<TelemetryDataSaub> TelemetryDataSaub { get; }
DbSet<TelemetryDataSaubStat> TelemetryDataSaubStats { get; } DbSet<TelemetryDataSaubStat> TelemetryDataSaubStats { get; }

View File

@ -8,6 +8,13 @@ namespace AsbCloudDb.Model;
[Table("t_notification_category"), Comment("Категории уведомлений")] [Table("t_notification_category"), Comment("Категории уведомлений")]
public class NotificationCategory : IId public class NotificationCategory : IId
{ {
#region constants category notifications ids
/// <summary>
/// СИСТЕМНЫЕ УВЕДОМЛЕНИЯ
/// </summary>
public const int IdSystemNotificationCategory = 1;
#endregion
[Key] [Key]
[Column("id")] [Column("id")]
public int Id { get; set; } public int Id { get; set; }

View File

@ -1,8 +1,8 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
namespace AsbCloudDb.Model.Subsystems namespace AsbCloudDb.Model
{ {
[Table("t_subsystem"), Comment("Описание подсистем")] [Table("t_subsystem"), Comment("Описание подсистем")]
public class Subsystem : IId public class Subsystem : IId

View File

@ -1,41 +0,0 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization;
namespace AsbCloudDb.Model.Subsystems
{
[Table("t_subsystem_operation_time"), Comment("наработки подсистем")]
public partial class SubsystemOperationTime : IId
{
[Column("id"), Key]
public int Id { get; set; }
[Column("id_telemetry"), Comment("ИД телеметрии по которой выдается информация")]
public int IdTelemetry { get; set; }
[Column("id_subsystem")]
public int IdSubsystem { get; set; }
[Column("date_start"), Comment("дата/время включения подсистемы")]
public DateTimeOffset DateStart { get; set; }
[Column("date_end"), Comment("дата/время выключения подсистемы")]
public DateTimeOffset DateEnd { get; set; }
[Column("depth_start"), Comment("глубина забоя на момент включения подсистемы")]
public float? DepthStart { get; set; }
[Column("depth_end"), Comment("глубина забоя на момент выключения подсистемы")]
public float? DepthEnd { get; set; }
[JsonIgnore]
[ForeignKey(nameof(IdSubsystem))]
public virtual Subsystem Subsystem { get; set; } = null!;
[JsonIgnore]
[ForeignKey(nameof(IdTelemetry))]
public virtual Telemetry Telemetry { get; set; } = null!;
}
}

View File

@ -1,127 +0,0 @@
using AsbCloudDb.Model;
using AsbCloudDb.Model.Subsystems;
using AsbCloudInfrastructure.Services.Subsystems;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Background.PeriodicWorks;
public class WorkSubsystemAbfOperationTimeCalc : WorkSubsystemOperationTimeCalcAbstract
{
public WorkSubsystemAbfOperationTimeCalc()
: base("Subsystem automated bit feeding operation time calc")
{
Timeout = TimeSpan.FromMinutes(30);
}
protected override async Task<IEnumerable<SubsystemOperationTime>> OperationTimeAsync(int idTelemetry, DateTimeOffset geDate, IAsbCloudDbContext db, CancellationToken token)
{
static bool isSubsytemAkbRotor(short? mode) => mode == 1;
static bool isSubsytemAkbSlide(short? mode) => mode == 3;
static bool IsSubsystemMse(short? state) => (state & 1) > 0;
var query =
$"select tt.date, tt.mode, tt.well_depth, tt.mse_state " +
$"from ( " +
$" select " +
$" date, " +
$" mode, " +
$" mse_state, " +
$" well_depth, " +
$" lag(mode,1) over (order by date) as mode_lag, " +
$" lead(mode,1) over (order by date) as mode_lead " +
$" from t_telemetry_data_saub " +
$" where id_telemetry = {idTelemetry} and well_depth is not null and well_depth > 0 " +
$" order by date ) as tt " +
$"where (tt.mode_lag is null or (tt.mode != tt.mode_lag and tt.mode_lead != tt.mode_lag)) and tt.date >= '{geDate:u}' " +
$"order by tt.date;";
using var result = await ExecuteReaderAsync(db, query, token);
var subsystemsOperationTimes = new List<SubsystemOperationTime>();
var detectorRotor = new SubsystemDetector(idTelemetry, idSubsystemAPDRotor, isSubsytemAkbRotor, IsValid);
var detectorSlide = new SubsystemDetector(idTelemetry, idSubsystemAPDSlide, isSubsytemAkbSlide, IsValid);
var detectorMse = new SubsystemDetector(idTelemetry, idSubsystemMse, IsSubsystemMse, IsValid);
while (result.Read())
{
var mode = result.GetFieldValue<short?>(1);
var state = result.GetFieldValue<short?>(3);
var isAkbRotorEnable = isSubsytemAkbRotor(mode);
var isAkbSlideEnable = isSubsytemAkbSlide(mode);
var isMseEnable = IsSubsystemMse(state);
var date = result.GetFieldValue<DateTimeOffset>(0);
var depth = result.GetFieldValue<float>(2);
if (detectorRotor.TryDetect(mode, date, depth, out var detectedRotor))
subsystemsOperationTimes.Add(detectedRotor!);
if (detectorSlide.TryDetect(mode, date, depth, out var detectedSlide))
subsystemsOperationTimes.Add(detectedSlide!);
if (detectorMse.TryDetect(mode, date, depth, out var detectedMse))
subsystemsOperationTimes.Add(detectedMse!);
}
return subsystemsOperationTimes;
}
private static async Task<DbDataReader> ExecuteReaderAsync(IAsbCloudDbContext db, string query, CancellationToken token)
{
var connection = db.Database.GetDbConnection();
if (
connection?.State is null ||
connection.State == ConnectionState.Broken ||
connection.State == ConnectionState.Closed)
{
await db.Database.OpenConnectionAsync(token);
connection = db.Database.GetDbConnection();
}
using var command = connection.CreateCommand();
command.CommandText = query;
var result = await command.ExecuteReaderAsync(token);
return result;
}
private static bool IsValid(SubsystemOperationTime item)
{
var validateCode = GetValidateErrorCode(item);
if (validateCode != 0)
{
var str = System.Text.Json.JsonSerializer.Serialize(item);
Trace.TraceWarning($"Wrong({validateCode}) SubsystemOperationTime: {str}");
}
return validateCode == 0;
}
private static int GetValidateErrorCode(SubsystemOperationTime item)
{
if (item.DateStart > item.DateEnd)
return -1;
if ((item.DateEnd - item.DateStart).TotalHours > 48)
return -2;
if (item.DepthEnd < item.DepthStart)
return -3;
if (item.DepthEnd - item.DepthStart > 2000d)
return -4;
if (item.DepthEnd < 0d)
return -5;
if (item.DepthStart < 0d)
return -6;
if (item.DepthEnd > 24_0000d)
return -7;
if (item.DepthStart > 24_0000d)
return -8;
return 0;
}
}

View File

@ -1,102 +0,0 @@
using AsbCloudApp.Data.SAUB;
using AsbCloudApp.Repositories;
using AsbCloudDb.Model;
using AsbCloudDb.Model.Subsystems;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Background.PeriodicWorks;
public abstract class WorkSubsystemOperationTimeCalcAbstract : Work
{
protected const int idSubsystemTorqueMaster = 65537;
protected const int idSubsystemSpinMaster = 65536;
protected const int idSubsystemAPDRotor = 11;
protected const int idSubsystemAPDSlide = 12;
protected const int idSubsystemMse = 2;
private static TimeSpan obsoleteTime = TimeSpan.FromDays(365 * 100);
public WorkSubsystemOperationTimeCalcAbstract(string workId)
: base(workId)
{
Timeout = TimeSpan.FromMinutes(30);
}
protected override async Task Action(string id, IServiceProvider services, Action<string, double?> onProgressCallback, CancellationToken token)
{
var db = services.GetRequiredService<IAsbCloudDbContext>();
db.Database.SetCommandTimeout(TimeSpan.FromMinutes(5));
var telemetryLastDetectedDates = await GetTelemetryLastDetectedDates(services, db, token);
var count = telemetryLastDetectedDates.Count();
var i = 0d;
foreach (var item in telemetryLastDetectedDates)
{
onProgressCallback($"Start handling telemetry: {item.IdTelemetry} from {item.DateDetectedLast}", i++ / count);
var newOperationsSaub = await OperationTimeAsync(item.IdTelemetry, item.DateDetectedLast, db, token);
if (newOperationsSaub.Any())
{
db.SubsystemOperationTimes.AddRange(newOperationsSaub);
await db.SaveChangesAsync(token);
}
}
obsoleteTime = TimeSpan.FromDays(3);
}
protected abstract Task<IEnumerable<SubsystemOperationTime>> OperationTimeAsync(int idTelemetry, DateTimeOffset geDate, IAsbCloudDbContext db, CancellationToken token);
private static async Task<IEnumerable<TelemetryDateLast>> GetTelemetryLastDetectedDates(IServiceProvider services, IAsbCloudDbContext db, CancellationToken token)
{
var telemetryDataCache = services.GetRequiredService<ITelemetryDataCache<TelemetryDataSaubDto>>();
var updatingTelemetries = telemetryDataCache.GetStat()
.Where(tstat => (DateTimeOffset.Now - tstat.DateLast) < obsoleteTime);
var telemetryIds = updatingTelemetries
.Select(t => t.IdTelemetry)
.ToArray();
IEnumerable<TelemetryDateLast> lastDetectedDates = await GetLastSubsystemOperationTimeAsync(db, token);
lastDetectedDates = lastDetectedDates
.Where(s => telemetryIds.Contains(s.IdTelemetry));
var result = updatingTelemetries.Select(tstat => new TelemetryDateLast
{
IdTelemetry = tstat.IdTelemetry,
DateDetectedLast = lastDetectedDates.FirstOrDefault(ldd => ldd.IdTelemetry == tstat.IdTelemetry)?.DateDetectedLast
?? DateTimeOffset.UnixEpoch,
DateTelemetryLast = tstat.DateLast
});
return result;
}
private static async Task<IEnumerable<TelemetryDateLast>> GetLastSubsystemOperationTimeAsync(IAsbCloudDbContext db, CancellationToken token)
{
var result = await db.SubsystemOperationTimes
.GroupBy(o => o.IdTelemetry)
.Select(g => new TelemetryDateLast
{
IdTelemetry = g.Key,
DateDetectedLast = g.Max(o => o.DateEnd)
})
.ToArrayAsync(token);
return result;
}
protected class TelemetryDateLast
{
public int IdTelemetry { get; set; }
public DateTimeOffset DateDetectedLast { get; set; }
public DateTimeOffset DateTelemetryLast { get; internal set; }
}
}

View File

@ -1,186 +0,0 @@
using AsbCloudDb.Model;
using AsbCloudDb.Model.Subsystems;
using AsbCloudInfrastructure.Background;
using AsbCloudInfrastructure.Services.Subsystems;
using AsbCloudInfrastructure.Services.Subsystems.Utils;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Background.PeriodicWorks;
public class WorkSubsystemOscillationOperationTimeCalc : WorkSubsystemOperationTimeCalcAbstract
{
public WorkSubsystemOscillationOperationTimeCalc()
: base("Subsystem operation time calc")
{
Timeout = TimeSpan.FromMinutes(30);
}
protected override async Task<IEnumerable<SubsystemOperationTime>> OperationTimeAsync(int idTelemetry, DateTimeOffset geDate, IAsbCloudDbContext db, CancellationToken token)
{
static int? GetSubsytemId(short? mode, int? state)
{
// При изменении следующего кода сообщи в Vladimir.Sobolev@nedra.digital
if (state == 7 && (mode & 2) > 0)
return idSubsystemTorqueMaster;// демпфер
if (state != 0 && state != 5 && state != 6 && state != 7)
return idSubsystemSpinMaster;// осцилляция
return null;
}
var querySpin =
$"select " +
$" tspin.date, " +
$" tspin.mode, " +
$" tspin.state " +
$"from ( " +
$" select " +
$" date, " +
$" mode, " +
$" lag(mode, 1) over (order by date) as mode_lag, " +
$" lead(mode, 1) over (order by date) as mode_lead, " +
$" state, " +
$" lag(state, 1) over (order by date) as state_lag " +
$" from t_telemetry_data_spin " +
$" where id_telemetry = {idTelemetry} and date >= '{geDate:u}'" +
$" order by date ) as tspin " +
$"where mode_lag is null or state_lag is null or (mode != mode_lag and mode_lead != mode_lag) or state != state_lag " +
$"order by date;";
var rows = new List<(int? IdSubsystem, DateTimeOffset Date)>(32);
using var resultSpin = await ExecuteReaderAsync(db, querySpin, token);
int? idSubsystemLast = null;
while (resultSpin.Read())
{
var mode = resultSpin.GetFieldValue<short?>(1);
var state = resultSpin.GetFieldValue<short?>(2);
var idSubsystem = GetSubsytemId(mode, state);
if (idSubsystemLast != idSubsystem)
{
idSubsystemLast = idSubsystem;
var date = resultSpin.GetFieldValue<DateTimeOffset>(0);
rows.Add((idSubsystem, date));
}
}
await resultSpin.DisposeAsync();
if (rows.Count < 2)
return Enumerable.Empty<SubsystemOperationTime>();
var minSpinDate = rows.Min(i => i.Date);
var maxSpinDate = rows.Max(i => i.Date);
var depthInterpolation = await GetInterpolation(db, idTelemetry, minSpinDate, maxSpinDate, token);
if (depthInterpolation is null)
return Enumerable.Empty<SubsystemOperationTime>();
var subsystemsOperationTimes = new List<SubsystemOperationTime>(32);
for (int i = 1; i < rows.Count; i++)
{
var r0 = rows[i - 1];
var r1 = rows[i];
if (r0.IdSubsystem is not null && r0.IdSubsystem != r1.IdSubsystem)
{
var subsystemOperationTime = new SubsystemOperationTime()
{
IdTelemetry = idTelemetry,
IdSubsystem = r0.IdSubsystem.Value,
DateStart = r0.Date,
DateEnd = r1.Date,
DepthStart = depthInterpolation.GetDepth(r0.Date),
DepthEnd = depthInterpolation.GetDepth(r1.Date),
};
if (IsValid(subsystemOperationTime))
subsystemsOperationTimes.Add(subsystemOperationTime);
}
}
return subsystemsOperationTimes;
}
private static async Task<DbDataReader> ExecuteReaderAsync(IAsbCloudDbContext db, string query, CancellationToken token)
{
var connection = db.Database.GetDbConnection();
if (
connection?.State is null ||
connection.State == ConnectionState.Broken ||
connection.State == ConnectionState.Closed)
{
await db.Database.OpenConnectionAsync(token);
connection = db.Database.GetDbConnection();
}
using var command = connection.CreateCommand();
command.CommandText = query;
var result = await command.ExecuteReaderAsync(token);
return result;
}
private static bool IsValid(SubsystemOperationTime item)
{
var validateCode = GetValidateErrorCode(item);
if (validateCode != 0)
{
var str = System.Text.Json.JsonSerializer.Serialize(item);
Trace.TraceWarning($"Wrong({validateCode}) SubsystemOperationTime: {str}");
}
return validateCode == 0;
}
private static int GetValidateErrorCode(SubsystemOperationTime item)
{
if (item.DateStart > item.DateEnd)
return -1;
if ((item.DateEnd - item.DateStart).TotalHours > 48)
return -2;
if (item.DepthEnd < item.DepthStart)
return -3;
if (item.DepthEnd - item.DepthStart > 2000d)
return -4;
if (item.DepthEnd < 0d)
return -5;
if (item.DepthStart < 0d)
return -6;
if (item.DepthEnd > 24_0000d)
return -7;
if (item.DepthStart > 24_0000d)
return -8;
return 0;
}
private static async Task<DepthInterpolation?> GetInterpolation(IAsbCloudDbContext db, int idTelemetry, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token)
{
var dataDepthFromSaub = await db.TelemetryDataSaub
.Where(d => d.IdTelemetry == idTelemetry)
.Where(d => d.DateTime >= dateBegin)
.Where(d => d.DateTime <= dateEnd)
.Where(d => d.WellDepth > 0)
.GroupBy(d => Math.Ceiling(d.WellDepth * 10))
.Select(g => new
{
DateMin = g.Min(d => d.DateTime),
DepthMin = g.Min(d => d.WellDepth),
})
.OrderBy(i => i.DateMin)
.ToArrayAsync(token);
if (!dataDepthFromSaub.Any())
return null;
var depthInterpolation = new DepthInterpolation(dataDepthFromSaub.Select(i => (i.DateMin, i.DepthMin)));
return depthInterpolation;
}
}

View File

@ -0,0 +1,41 @@
using AsbCloudApp.Data.Progress;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Background;
/// <summary>
/// Класс для создания отчета
/// </summary>
internal class WorkToCreateReport : Work
{
private readonly int idWell;
private readonly int idUser;
private readonly ReportParametersRequest request;
private readonly Action<object, string> progressHandler;
public WorkToCreateReport(int idWell, int idUser, ReportParametersRequest request, Action<object, string> progressHandler) : base("")
{
this.idWell = idWell;
this.idUser = idUser;
this.request = request;
this.progressHandler = progressHandler;
Id = $"create report by wellid:{idWell} for userid:{idUser} requested at {DateTime.Now}";
}
protected override async Task Action(string id, IServiceProvider services, Action<string, double?> onProgress, CancellationToken token)
{
var reportService = services.GetRequiredService<IReportService>();
void handler(ProgressDto state, string workId)
{
onProgress(state.Operation ?? string.Empty, state.Progress);
progressHandler(state, workId);
}
await reportService.CreateReportAsync(Id, idWell, idUser, request, handler, token);
}
}

View File

@ -0,0 +1,94 @@
using AsbCloudApp.Data;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Diagnostics;
using System.Net.Mail;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Background
{
/// <summary>
/// Класс для отправки email
/// </summary>
internal class WorkToSendEmail : Work
{
private NotificationDto notification;
private string sender;
private string smtpPassword;
private string smtpServer;
private bool IsConfigured;
public WorkToSendEmail(NotificationDto notification, IConfiguration configuration) : base(MakeWorkId(notification))
{
this.notification = notification;
sender = configuration.GetValue("email:sender", string.Empty);
smtpPassword = configuration.GetValue("email:password", string.Empty);
smtpServer = configuration.GetValue("email:smtpServer", string.Empty);
var configError = string.IsNullOrEmpty(sender) ||
string.IsNullOrEmpty(smtpPassword) ||
string.IsNullOrEmpty(smtpServer);
IsConfigured = !configError;
}
protected override async Task Action(string id, IServiceProvider services, Action<string, double?> onProgressCallback, CancellationToken token)
{
if (!IsConfigured)
{
Trace.TraceWarning("smtp is not configured");
return;
}
var notificationRepository = services.GetRequiredService<INotificationRepository>();
var userRepository = services.GetRequiredService<IUserRepository>();
var user = await userRepository.GetOrDefaultAsync(notification.IdUser, token)
?? throw new ArgumentInvalidException(nameof(notification.IdUser), "Пользователь не найден");
if (!MailAddress.TryCreate(user.Email, out var mailAddress))
{
Trace.TraceWarning($"Mail {user.Email} is not correct.");
throw new ArgumentInvalidException(nameof(user.Email), $"Mail {user.Email} is not null.");
}
var from = new MailAddress(sender);
var message = new MailMessage
{
From = from
};
message.To.Add(mailAddress.Address);
message.BodyEncoding = System.Text.Encoding.UTF8;
message.Body = notification.Message;
message.Subject = notification.Title;
message.IsBodyHtml = true;
using var client = new SmtpClient(smtpServer);
client.EnableSsl = true;
client.UseDefaultCredentials = false;
client.Credentials = new System.Net.NetworkCredential(sender, smtpPassword);
await client.SendMailAsync(message, token);
notification.SentDate = DateTime.UtcNow;
await notificationRepository.UpdateAsync(notification, token);
Trace.TraceInformation($"Send email to {user.Email} subj:{notification.Title} html body count {notification.Message.Length}");
}
private static string MakeWorkId(NotificationDto notification)
{
var hash = notification.IdUser.GetHashCode();
hash ^= notification.Title.GetHashCode();
hash ^= notification.Message.GetHashCode();
return hash.ToString("x");
}
}
}

View File

@ -12,12 +12,10 @@ using AsbCloudApp.Services;
using AsbCloudApp.Services.Notifications; using AsbCloudApp.Services.Notifications;
using AsbCloudApp.Services.ProcessMaps; using AsbCloudApp.Services.ProcessMaps;
using AsbCloudApp.Services.ProcessMaps.WellDrilling; using AsbCloudApp.Services.ProcessMaps.WellDrilling;
using AsbCloudApp.Services.Subsystems;
using AsbCloudApp.Services.WellOperationImport; using AsbCloudApp.Services.WellOperationImport;
using AsbCloudDb.Model; using AsbCloudDb.Model;
using AsbCloudDb.Model.Manuals; using AsbCloudDb.Model.Manuals;
using AsbCloudDb.Model.ProcessMaps; using AsbCloudDb.Model.ProcessMaps;
using AsbCloudDb.Model.Subsystems;
using AsbCloudDb.Model.Trajectory; using AsbCloudDb.Model.Trajectory;
using AsbCloudInfrastructure.Background; using AsbCloudInfrastructure.Background;
using AsbCloudInfrastructure.Repository; using AsbCloudInfrastructure.Repository;
@ -211,7 +209,7 @@ namespace AsbCloudInfrastructure
services.AddTransient<IWellOperationRepository, WellOperationRepository>(); services.AddTransient<IWellOperationRepository, WellOperationRepository>();
services.AddTransient<IDailyReportService, DailyReportService>(); services.AddTransient<IDailyReportService, DailyReportService>();
services.AddTransient<IDetectedOperationService, DetectedOperationService>(); services.AddTransient<IDetectedOperationService, DetectedOperationService>();
services.AddTransient<ISubsystemOperationTimeService, SubsystemOperationTimeService>(); services.AddTransient<ISubsystemService, SubsystemService>();
services.AddTransient<IScheduleRepository, ScheduleRepository>(); services.AddTransient<IScheduleRepository, ScheduleRepository>();
services.AddTransient<IRepositoryWellRelated<OperationValueDto>, CrudWellRelatedRepositoryBase<OperationValueDto, OperationValue>>(); services.AddTransient<IRepositoryWellRelated<OperationValueDto>, CrudWellRelatedRepositoryBase<OperationValueDto, OperationValue>>();
services.AddTransient<IUserSettingsRepository, UserSettingsRepository>(); services.AddTransient<IUserSettingsRepository, UserSettingsRepository>();
@ -244,7 +242,8 @@ namespace AsbCloudInfrastructure
new CrudCacheRepositoryBase<DepositDto, Deposit>( new CrudCacheRepositoryBase<DepositDto, Deposit>(
s.GetRequiredService<IAsbCloudDbContext>(), s.GetRequiredService<IAsbCloudDbContext>(),
s.GetRequiredService<IMemoryCache>(), s.GetRequiredService<IMemoryCache>(),
dbSet => dbSet.Include(d => d.Clusters))); dbSet => dbSet.Include(d => d.Clusters)
.ThenInclude(c => c.Wells)));
services.AddTransient<ICrudRepository<CompanyDto>, CrudCacheRepositoryBase<CompanyDto, Company>>(s => services.AddTransient<ICrudRepository<CompanyDto>, CrudCacheRepositoryBase<CompanyDto, Company>>(s =>
new CrudCacheRepositoryBase<CompanyDto, Company>( new CrudCacheRepositoryBase<CompanyDto, Company>(
s.GetRequiredService<IAsbCloudDbContext>(), s.GetRequiredService<IAsbCloudDbContext>(),

View File

@ -33,25 +33,14 @@ public class DailyReportRepository : CrudRepositoryBase<DailyReportDto, DailyRep
var query = GetQuery().Where(d => d.IdWell == idWell); var query = GetQuery().Where(d => d.IdWell == idWell);
if (request.GeDate.HasValue) if (request.GeDate.HasValue)
{ query = query.Where(d => d.Date >= request.GeDate.Value);
var geDate = request.GeDate.Value.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc);
query = query.Where(d => d.Date >= geDate);
}
if (request.LeDate.HasValue) if (request.LeDate.HasValue)
{ query = query.Where(d => d.Date <= request.LeDate.Value);
var leDate = request.LeDate.Value.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc);
query = query.Where(d => d.Date <= leDate);
}
if (request.SortFields?.Any() == true) query = request.SortFields?.Any() == true ?
{ query.SortBy(request.SortFields) :
query = query.SortBy(request.SortFields); query.OrderBy(d => d.Date);
}
else
{
query = query.OrderBy(d => d.Date);
}
var entities = await query var entities = await query
.Skip(skip) .Skip(skip)
@ -64,7 +53,7 @@ public class DailyReportRepository : CrudRepositoryBase<DailyReportDto, DailyRep
return dtos; return dtos;
} }
public async Task<DailyReportDto?> GetOrDefaultAsync(int idWell, DateTime date, CancellationToken cancellationToken) public async Task<DailyReportDto?> GetOrDefaultAsync(int idWell, DateOnly date, CancellationToken cancellationToken)
{ {
var entity = await GetQuery() var entity = await GetQuery()
.AsNoTracking() .AsNoTracking()

View File

@ -3,12 +3,14 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsbCloudApp.Data.ProcessMaps; using AsbCloudApp.Data.ProcessMaps;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories; using AsbCloudApp.Repositories;
using AsbCloudApp.Requests; using AsbCloudApp.Requests;
using AsbCloudApp.Services; using AsbCloudApp.Services;
using AsbCloudDb.Model; using AsbCloudDb.Model;
using AsbCloudDb.Model.ProcessMaps; using AsbCloudDb.Model.ProcessMaps;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Org.BouncyCastle.Asn1.Ocsp;
namespace AsbCloudInfrastructure.Repository; namespace AsbCloudInfrastructure.Repository;
@ -47,11 +49,30 @@ public class ProcessMapPlanRepository<TDto, TEntity> : CrudWellRelatedRepository
} }
private IQueryable<TEntity> BuildQuery(IEnumerable<ProcessMapPlanRequest> requests) private IQueryable<TEntity> BuildQuery(IEnumerable<ProcessMapPlanRequest> requests)
{
var queries = requests
.Select(request => BuildQuery(request))
.ToArray();
var query = queries.FirstOrDefault()
?? throw new ArgumentInvalidException(nameof(requests), "Пустые запросы недопустимы");
for ( var i = 1; i < queries.Length; i++)
query = query.Union(queries[i]);
query = query
.Distinct()
.OrderBy(e => e.DepthStart)
.ThenBy(e => e.Id)
.AsNoTracking();
return query;
}
private IQueryable<TEntity> BuildQuery(ProcessMapPlanRequest request)
{ {
var query = GetQuery(); var query = GetQuery();
foreach (var request in requests)
{
query = query.Where(p => p.IdWell == request.IdWell); query = query.Where(p => p.IdWell == request.IdWell);
if (request.IdWellSectionType is not null) if (request.IdWellSectionType is not null)
@ -64,10 +85,6 @@ public class ProcessMapPlanRepository<TDto, TEntity> : CrudWellRelatedRepository
query = query.Where(p => p.LastUpdate >= updateFromUtc); query = query.Where(p => p.LastUpdate >= updateFromUtc);
} }
} return query;
return query.OrderBy(e => e.DepthStart)
.ThenBy(e => e.Id)
.AsNoTracking();
} }
} }

View File

@ -9,20 +9,26 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsbCloudApp.Services;
namespace AsbCloudInfrastructure.Repository namespace AsbCloudInfrastructure.Repository
{ {
public class TrajectoryNnbRepository : ITrajectoryNnbRepository public class TrajectoryNnbRepository : ITrajectoryNnbRepository
{ {
private readonly IAsbCloudDbContext db; private readonly IAsbCloudDbContext db;
public TrajectoryNnbRepository(IAsbCloudDbContext db) private readonly IWellService wellService;
public TrajectoryNnbRepository(IAsbCloudDbContext db,
IWellService wellService)
{ {
this.db = db; this.db = db;
this.wellService = wellService;
} }
private IQueryable<Record7> BuildQuery(TrajectoryRequest request) private IQueryable<Record7> BuildQuery(TrajectoryRequest request)
{ {
var well = db.Wells.SingleOrDefault(w => w.Id == request.IdWell); var well = db.Wells.SingleOrDefault(w => w.Id == request.IdWell);
var timezone = wellService.GetTimezone(request.IdWell);
if (well is null) if (well is null)
throw new ArgumentInvalidException($"Скважина с Id: {request.IdWell} не найдена", nameof(request.IdWell)); throw new ArgumentInvalidException($"Скважина с Id: {request.IdWell} не найдена", nameof(request.IdWell));
@ -31,10 +37,16 @@ namespace AsbCloudInfrastructure.Repository
.Where(x => x.IdTelemetry == well.IdTelemetry); .Where(x => x.IdTelemetry == well.IdTelemetry);
if (request.GeDate.HasValue) if (request.GeDate.HasValue)
query = query.Where(r => r.DateTime >= request.GeDate.Value); {
var geDate = request.GeDate.Value.ToUtcDateTimeOffset(timezone.Hours);
query = query.Where(r => r.DateTime >= geDate);
}
if (request.LeDate.HasValue) if (request.LeDate.HasValue)
query = query.Where(r => r.DateTime <= request.LeDate.Value); {
var leDate = request.LeDate.Value.ToUtcDateTimeOffset(timezone.Hours);
query = query.Where(r => r.DateTime <= leDate);
}
return query.OrderBy(e => e.Deptsvym); return query.OrderBy(e => e.Deptsvym);
} }

View File

@ -186,15 +186,20 @@ public class WellOperationRepository : IWellOperationRepository
public async Task<DatesRangeDto?> GetDatesRangeAsync(int idWell, int idType, CancellationToken cancellationToken) public async Task<DatesRangeDto?> GetDatesRangeAsync(int idWell, int idType, CancellationToken cancellationToken)
{ {
var timezone = wellService.GetTimezone(idWell);
var query = db.WellOperations.Where(o => o.IdWell == idWell && o.IdType == idType); var query = db.WellOperations.Where(o => o.IdWell == idWell && o.IdType == idType);
if (!await query.AnyAsync(cancellationToken)) if (!await query.AnyAsync(cancellationToken))
return null; return null;
var minDate = await query.MinAsync(o => o.DateStart, cancellationToken);
var maxDate = await query.MaxAsync(o => o.DateStart, cancellationToken);
return new DatesRangeDto return new DatesRangeDto
{ {
From = (await query.MinAsync(o => o.DateStart, cancellationToken)).Date, From = minDate.ToRemoteDateTime(timezone.Hours),
To = (await query.MaxAsync(o => o.DateStart, cancellationToken)).Date To = maxDate.ToRemoteDateTime(timezone.Hours)
}; };
} }

View File

@ -89,9 +89,9 @@ public class DailyReportExportService : IDailyReportExportService
this.dailyReportService = dailyReportService; this.dailyReportService = dailyReportService;
} }
public async Task<(string FileName, Stream File)> ExportAsync(int idWell, DateTime dailyReportDateStart, CancellationToken cancellationToken) public async Task<(string FileName, Stream File)> ExportAsync(int idWell, DateOnly dailyReportDate, CancellationToken cancellationToken)
{ {
var dailyReport = await dailyReportService.GetAsync(idWell, dailyReportDateStart, cancellationToken); var dailyReport = await dailyReportService.GetAsync(idWell, dailyReportDate, cancellationToken);
var stream = await GenerateFileAsync(dailyReport, cancellationToken); var stream = await GenerateFileAsync(dailyReport, cancellationToken);
@ -236,12 +236,16 @@ public class DailyReportExportService : IDailyReportExportService
private static void AddFactWellOperationBlockToSheet(IXLWorksheet sheet, WellOperationBlockDto factWellOperationBlock) private static void AddFactWellOperationBlockToSheet(IXLWorksheet sheet, WellOperationBlockDto factWellOperationBlock)
{ {
var rowCurrent = rowStartFactWellOperationBlock;
sheet.Cell(cellSectionDrillingHours).Value = factWellOperationBlock.SectionDrillingHours; sheet.Cell(cellSectionDrillingHours).Value = factWellOperationBlock.SectionDrillingHours;
foreach (var factOperation in factWellOperationBlock.WellOperations.OrderBy(w => w.CategoryName)) foreach (var factOperation in factWellOperationBlock.WellOperations.OrderBy(w => w.CategoryName))
{ {
sheet.Cell(rowStartFactWellOperationBlock, columnWellOperationCategory).Value = factOperation.CategoryName; sheet.Cell(rowCurrent, columnWellOperationCategory).Value = factOperation.CategoryName;
sheet.Cell(rowStartFactWellOperationBlock, columnWellOperationDurationHours).Value = factOperation.DurationHours; sheet.Cell(rowCurrent, columnWellOperationDurationHours).Value = factOperation.DurationHours;
rowCurrent++;
} }
} }

View File

@ -16,7 +16,6 @@ using AsbCloudApp.Data.DailyReport.Blocks.WellOperation;
using AsbCloudApp.Requests; using AsbCloudApp.Requests;
using AsbCloudApp.Services.DailyReport; using AsbCloudApp.Services.DailyReport;
using AsbCloudApp.Services.ProcessMaps.WellDrilling; using AsbCloudApp.Services.ProcessMaps.WellDrilling;
using AsbCloudApp.Services.Subsystems;
using AsbCloudDb.Model; using AsbCloudDb.Model;
using Mapster; using Mapster;
@ -29,7 +28,7 @@ public class DailyReportService : IDailyReportService
private readonly IDailyReportRepository dailyReportRepository; private readonly IDailyReportRepository dailyReportRepository;
private readonly IScheduleRepository scheduleRepository; private readonly IScheduleRepository scheduleRepository;
private readonly IWellOperationRepository wellOperationRepository; private readonly IWellOperationRepository wellOperationRepository;
private readonly ISubsystemOperationTimeService subsystemOperationTimeService; private readonly ISubsystemService subsystemService;
private readonly IProcessMapReportWellDrillingService processMapReportWellDrillingService; private readonly IProcessMapReportWellDrillingService processMapReportWellDrillingService;
private readonly IDetectedOperationService detectedOperationService; private readonly IDetectedOperationService detectedOperationService;
@ -38,7 +37,7 @@ public class DailyReportService : IDailyReportService
IDailyReportRepository dailyReportRepository, IDailyReportRepository dailyReportRepository,
IScheduleRepository scheduleRepository, IScheduleRepository scheduleRepository,
IWellOperationRepository wellOperationRepository, IWellOperationRepository wellOperationRepository,
ISubsystemOperationTimeService subsystemOperationTimeService, ISubsystemService subsystemService,
IProcessMapReportWellDrillingService processMapReportWellDrillingService, IProcessMapReportWellDrillingService processMapReportWellDrillingService,
IDetectedOperationService detectedOperationService) IDetectedOperationService detectedOperationService)
{ {
@ -47,12 +46,12 @@ public class DailyReportService : IDailyReportService
this.dailyReportRepository = dailyReportRepository; this.dailyReportRepository = dailyReportRepository;
this.scheduleRepository = scheduleRepository; this.scheduleRepository = scheduleRepository;
this.wellOperationRepository = wellOperationRepository; this.wellOperationRepository = wellOperationRepository;
this.subsystemOperationTimeService = subsystemOperationTimeService; this.subsystemService = subsystemService;
this.processMapReportWellDrillingService = processMapReportWellDrillingService; this.processMapReportWellDrillingService = processMapReportWellDrillingService;
this.detectedOperationService = detectedOperationService; this.detectedOperationService = detectedOperationService;
} }
public async Task<int> UpdateOrInsertAsync<TBlock>(int idWell, DateTime dateDailyReport, int idUser, TBlock editableBlock, public async Task<int> UpdateOrInsertAsync<TBlock>(int idWell, DateOnly dateDailyReport, int idUser, TBlock editableBlock,
CancellationToken cancellationToken) CancellationToken cancellationToken)
where TBlock : ItemInfoDto where TBlock : ItemInfoDto
{ {
@ -92,7 +91,7 @@ public class DailyReportService : IDailyReportService
return await dailyReportRepository.UpdateAsync(dailyReport, cancellationToken); return await dailyReportRepository.UpdateAsync(dailyReport, cancellationToken);
} }
public async Task<DailyReportDto> GetAsync(int idWell, DateTime dateDailyReport, CancellationToken cancellationToken) public async Task<DailyReportDto> GetAsync(int idWell, DateOnly dateDailyReport, CancellationToken cancellationToken)
{ {
var well = await wellService.GetOrDefaultAsync(idWell, cancellationToken) var well = await wellService.GetOrDefaultAsync(idWell, cancellationToken)
?? throw new ArgumentNullException(nameof(idWell), $"Скважина с Id: {idWell} не найдена"); ?? throw new ArgumentNullException(nameof(idWell), $"Скважина с Id: {idWell} не найдена");
@ -103,16 +102,19 @@ public class DailyReportService : IDailyReportService
var dailyReport = await dailyReportRepository.GetOrDefaultAsync(idWell, dateDailyReport, cancellationToken) ?? new DailyReportDto var dailyReport = await dailyReportRepository.GetOrDefaultAsync(idWell, dateDailyReport, cancellationToken) ?? new DailyReportDto
{ {
Date = dateDailyReport.Date, Date = dateDailyReport,
IdWell = well.Id IdWell = well.Id
}; };
var geDate = dailyReport.Date.ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified);
var ltDate = dailyReport.Date.AddDays(1).ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified);
var factOperationRequest = new WellOperationRequest var factOperationRequest = new WellOperationRequest
{ {
IdWell = idWell, IdWell = idWell,
OperationType = WellOperation.IdOperationTypeFact, OperationType = WellOperation.IdOperationTypeFact,
GeDate = dateDailyReport, GeDate = geDate,
LtDate = dateDailyReport.AddHours(24) LtDate = ltDate
}; };
var factWellOperations = (await wellOperationRepository.GetAsync(factOperationRequest, cancellationToken)) var factWellOperations = (await wellOperationRepository.GetAsync(factOperationRequest, cancellationToken))
@ -198,7 +200,7 @@ public class DailyReportService : IDailyReportService
{ {
for (var day = result.Skip; day - result.Skip < result.Take && datesRange.To.AddDays(-day) >= datesRange.From; day++) for (var day = result.Skip; day - result.Skip < result.Take && datesRange.To.AddDays(-day) >= datesRange.From; day++)
{ {
var dateDailyReport = datesRange.To.AddDays(-day).Date; var dateDailyReport = DateOnly.FromDateTime(datesRange.To.AddDays(-day));
AddDailyReport(dateDailyReport); AddDailyReport(dateDailyReport);
} }
@ -207,7 +209,7 @@ public class DailyReportService : IDailyReportService
{ {
for (var day = result.Skip; day - result.Skip < result.Take && datesRange.From.AddDays(day) <= datesRange.To; day++) for (var day = result.Skip; day - result.Skip < result.Take && datesRange.From.AddDays(day) <= datesRange.To; day++)
{ {
var dateDailyReport = datesRange.From.AddDays(day).Date; var dateDailyReport = DateOnly.FromDateTime(datesRange.From.AddDays(day));
AddDailyReport(dateDailyReport); AddDailyReport(dateDailyReport);
} }
@ -217,7 +219,7 @@ public class DailyReportService : IDailyReportService
return result; return result;
void AddDailyReport(DateTime date) void AddDailyReport(DateOnly date)
{ {
var dailyReport = existingDailyReports.FirstOrDefault(d => d.IdWell == idWell && d.Date == date) var dailyReport = existingDailyReports.FirstOrDefault(d => d.IdWell == idWell && d.Date == date)
?? new DailyReportDto ?? new DailyReportDto
@ -226,7 +228,11 @@ public class DailyReportService : IDailyReportService
IdWell = idWell IdWell = idWell
}; };
var factWellOperationPerDay = factWellOperations.Where(o => o.DateStart.Date >= date && o.DateStart.Date <= date.AddDays(1)); var geDate = date.ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified);
var leDate = date.AddDays(1).ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified);
var factWellOperationPerDay = factWellOperations.Where(o => o.DateStart.Date >= geDate &&
o.DateStart.Date <= leDate);
AddFactWellOperationBlock(dailyReport, factWellOperationPerDay); AddFactWellOperationBlock(dailyReport, factWellOperationPerDay);
@ -236,16 +242,29 @@ public class DailyReportService : IDailyReportService
public async Task<DatesRangeDto?> GetDatesRangeAsync(int idWell, CancellationToken cancellationToken) public async Task<DatesRangeDto?> GetDatesRangeAsync(int idWell, CancellationToken cancellationToken)
{ {
var timezone = wellService.GetTimezone(idWell);
var currentDate = DateTimeOffset.UtcNow.ToRemoteDateTime(timezone.Hours);
var factOperationDatesRange = await wellOperationRepository.GetDatesRangeAsync(idWell, WellOperation.IdOperationTypeFact, var factOperationDatesRange = await wellOperationRepository.GetDatesRangeAsync(idWell, WellOperation.IdOperationTypeFact,
cancellationToken); cancellationToken);
if (factOperationDatesRange is null) if (factOperationDatesRange is null)
return null; return null;
var from = (factOperationDatesRange.From.AddDays(1) <= DateTime.UtcNow ?
factOperationDatesRange.From :
currentDate.AddDays(-1))
.Date;
var to = (factOperationDatesRange.To.AddDays(1) <= DateTime.UtcNow ?
factOperationDatesRange.To :
currentDate.AddDays(-1))
.Date;
return new DatesRangeDto return new DatesRangeDto
{ {
From = factOperationDatesRange.From.AddDays(1) <= DateTime.UtcNow ? factOperationDatesRange.From : DateTime.UtcNow.Date.AddDays(-1), From = from,
To = factOperationDatesRange.To.AddDays(1) <= DateTime.UtcNow ? factOperationDatesRange.To : DateTime.UtcNow.Date.AddDays(-1) To = to
}; };
} }
@ -259,13 +278,16 @@ public class DailyReportService : IDailyReportService
dailyReport.TimeBalanceBlock.SectionName = wellOperationRepository.GetSectionTypes() dailyReport.TimeBalanceBlock.SectionName = wellOperationRepository.GetSectionTypes()
.FirstOrDefault(s => s.Id == dailyReport.TimeBalanceBlock.IdSection)?.Caption; .FirstOrDefault(s => s.Id == dailyReport.TimeBalanceBlock.IdSection)?.Caption;
var geDateStart = dailyReport.Date.ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified);
var leDateEnd = dailyReport.Date.AddDays(1).ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified);
dailyReport.TimeBalanceBlock.WellOperationSlipsTimeCount = (await detectedOperationService.GetAsync( dailyReport.TimeBalanceBlock.WellOperationSlipsTimeCount = (await detectedOperationService.GetAsync(
new DetectedOperationRequest new DetectedOperationRequest
{ {
IdsCategories = new[] { idWellOperationSlipsTime }, IdsCategories = new[] { idWellOperationSlipsTime },
IdWell = dailyReport.IdWell, IdWell = dailyReport.IdWell,
GtDate = dailyReport.Date, GeDateStart = geDateStart,
LtDate = dailyReport.Date.AddHours(24) LeDateEnd = leDateEnd
}, cancellationToken))?.Stats.Sum(s => s.Count); }, cancellationToken))?.Stats.Sum(s => s.Count);
dailyReport.TimeBalanceBlock.WellDepth.Fact = factWellOperations dailyReport.TimeBalanceBlock.WellDepth.Fact = factWellOperations
@ -276,11 +298,14 @@ public class DailyReportService : IDailyReportService
private async Task AddTrajectoryBlockAsync(DailyReportDto dailyReport, CancellationToken cancellationToken) private async Task AddTrajectoryBlockAsync(DailyReportDto dailyReport, CancellationToken cancellationToken)
{ {
var geDate = dailyReport.Date.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc);
var leDate = dailyReport.Date.AddDays(1).ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc);
var trajectory = (await trajectoryFactNnbRepository.GetByRequestAsync(new TrajectoryRequest var trajectory = (await trajectoryFactNnbRepository.GetByRequestAsync(new TrajectoryRequest
{ {
IdWell = dailyReport.IdWell, IdWell = dailyReport.IdWell,
GeDate = dailyReport.Date, GeDate = geDate,
LeDate = dailyReport.Date.AddHours(24) LeDate = leDate
}, cancellationToken)).MaxBy(t => t.WellboreDepth); }, cancellationToken)).MaxBy(t => t.WellboreDepth);
dailyReport.TrajectoryBlock = new TrajectoryBlockDto dailyReport.TrajectoryBlock = new TrajectoryBlockDto
@ -292,8 +317,11 @@ public class DailyReportService : IDailyReportService
}; };
} }
private async Task AddScheduleBlockAsync(DailyReportDto dailyReport, CancellationToken cancellationToken) => private async Task AddScheduleBlockAsync(DailyReportDto dailyReport, CancellationToken cancellationToken)
dailyReport.ScheduleBlock = (await scheduleRepository.GetAsync(dailyReport.IdWell, dailyReport.Date, cancellationToken)) {
var workDate = dailyReport.Date.ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified);
dailyReport.ScheduleBlock = (await scheduleRepository.GetAsync(dailyReport.IdWell, workDate, cancellationToken))
.Select(s => new ScheduleRecordDto .Select(s => new ScheduleRecordDto
{ {
ShiftStart = s.ShiftStart, ShiftStart = s.ShiftStart,
@ -302,6 +330,7 @@ public class DailyReportService : IDailyReportService
Surname = s.Driller?.Surname, Surname = s.Driller?.Surname,
Patronymic = s.Driller?.Patronymic Patronymic = s.Driller?.Patronymic
}); });
}
private async Task UpdateSubsystemBlockAsync(DailyReportDto dailyReport, CancellationToken cancellationToken) private async Task UpdateSubsystemBlockAsync(DailyReportDto dailyReport, CancellationToken cancellationToken)
{ {
@ -311,24 +340,27 @@ public class DailyReportService : IDailyReportService
async Task<IEnumerable<SubsystemRecordDto>> GetSubsystemsAsync() async Task<IEnumerable<SubsystemRecordDto>> GetSubsystemsAsync()
{ {
var subsystemOperationTimesPerWell = await subsystemOperationTimeService.GetStatAsync(new SubsystemOperationTimeRequest var subsystemsStatPerWell = await subsystemService.GetStatAsync(new SubsystemRequest
{ {
IdWell = dailyReport.IdWell IdWell = dailyReport.IdWell
}, cancellationToken); }, cancellationToken);
var subsystemOperationTimesPerDay = await subsystemOperationTimeService.GetStatAsync(new SubsystemOperationTimeRequest var geDate = dailyReport.Date.ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified);
var leDate = dailyReport.Date.AddDays(1).ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified);
var subsystemsStatPerDay = await subsystemService.GetStatAsync(new SubsystemRequest
{ {
IdWell = dailyReport.IdWell, IdWell = dailyReport.IdWell,
GtDate = dailyReport.Date, GeDate = geDate,
LtDate = dailyReport.Date.AddHours(24) LeDate = leDate
}, cancellationToken); }, cancellationToken);
var subsystems = subsystemOperationTimesPerWell var subsystems = subsystemsStatPerWell
.Select(subsystemOperationTime => new SubsystemRecordDto .Select(subsystemStatPerWell => new SubsystemRecordDto
{ {
Name = subsystemOperationTime.SubsystemName, Name = subsystemStatPerWell.SubsystemName,
UsagePerDay = subsystemOperationTimesPerDay.FirstOrDefault(s => s.IdSubsystem == subsystemOperationTime.IdSubsystem)?.Adapt<SubsystemParametersDto>(), UsagePerDay = subsystemsStatPerDay.FirstOrDefault(s => s.IdSubsystem == subsystemStatPerWell.IdSubsystem)?.Adapt<SubsystemParametersDto>(),
UsagePerWell = subsystemOperationTime.Adapt<SubsystemParametersDto>() UsagePerWell = subsystemStatPerWell.Adapt<SubsystemParametersDto>()
}).ToList(); }).ToList();
if (dailyReport.SubsystemBlock?.Subsystems != null && dailyReport.SubsystemBlock.Subsystems.Any()) if (dailyReport.SubsystemBlock?.Subsystems != null && dailyReport.SubsystemBlock.Subsystems.Any())
@ -340,9 +372,11 @@ public class DailyReportService : IDailyReportService
private async Task AddProcessMapWellDrillingBlockAsync(DailyReportDto dailyReport, CancellationToken cancellationToken) private async Task AddProcessMapWellDrillingBlockAsync(DailyReportDto dailyReport, CancellationToken cancellationToken)
{ {
var geDate = dailyReport.Date.ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified);
var leDate = dailyReport.Date.AddDays(1).ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified);
dailyReport.ProcessMapWellDrillingBlock = (await processMapReportWellDrillingService.GetAsync(dailyReport.IdWell, dailyReport.ProcessMapWellDrillingBlock = (await processMapReportWellDrillingService.GetAsync(dailyReport.IdWell,
cancellationToken)).Where(p => p.DateStart >= dailyReport.Date && cancellationToken)).Where(p => p.DateStart >= geDate && p.DateStart <= leDate)
p.DateStart <= dailyReport.Date.AddHours(24))
.GroupBy(p => p.DrillingMode) .GroupBy(p => p.DrillingMode)
.Select(g => new ProcessMapWellDrillingRecordDto .Select(g => new ProcessMapWellDrillingRecordDto
{ {
@ -363,7 +397,7 @@ public class DailyReportService : IDailyReportService
dailyReport.FactWellOperationBlock = new WellOperationBlockDto dailyReport.FactWellOperationBlock = new WellOperationBlockDto
{ {
WellOperations = factWellOperations.GroupBy(o => o.IdParentCategory) WellOperations = factWellOperations.GroupBy(o => o.IdCategory)
.Select(g => new WellOperationRecordDto .Select(g => new WellOperationRecordDto
{ {
CategoryName = g.First().CategoryName, CategoryName = g.First().CategoryName,
@ -376,10 +410,16 @@ public class DailyReportService : IDailyReportService
}; };
} }
private async Task<bool> IsDateDailyReportInRangeAsync(int idWell, DateTime dateDailyReport, CancellationToken cancellationToken) private async Task<bool> IsDateDailyReportInRangeAsync(int idWell, DateOnly dateDailyReport, CancellationToken cancellationToken)
{ {
var datesRange = await GetDatesRangeAsync(idWell, cancellationToken); var datesRange = await GetDatesRangeAsync(idWell, cancellationToken);
return dateDailyReport >= datesRange?.From && dateDailyReport <= datesRange.To; if (datesRange is null)
return false;
var from = DateOnly.FromDateTime(datesRange.From);
var to = DateOnly.FromDateTime(datesRange.To);
return dateDailyReport >= from && dateDailyReport <= to;
} }
} }

View File

@ -8,9 +8,11 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsbCloudApp.Data.DetectedOperation;
using AsbCloudInfrastructure.Services.DetectOperations.Detectors; using AsbCloudInfrastructure.Services.DetectOperations.Detectors;
using AsbCloudApp.Repositories; using AsbCloudApp.Repositories;
using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Http.Extensions;
using AsbCloudApp.Exceptions;
namespace AsbCloudInfrastructure.Services.DetectOperations; namespace AsbCloudInfrastructure.Services.DetectOperations;
@ -56,19 +58,19 @@ public class DetectedOperationExportService
/// <param name="host">хост</param> /// <param name="host">хост</param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
/// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentInvalidException"></exception>
public async Task<Stream> ExportAsync(int idWell, string host, CancellationToken cancellationToken) public async Task<Stream> ExportAsync(int idWell, string host, CancellationToken cancellationToken)
{ {
var well = await dbContext.Wells var well = await dbContext.Set<Well>()
.Include(w => w.Cluster) .Include(w => w.Cluster)
.ThenInclude(c => c.Deposit) .ThenInclude(c => c.Deposit)
.SingleOrDefaultAsync(w => w.Id == idWell, cancellationToken); .FirstOrDefaultAsync(w => w.Id == idWell, cancellationToken);
if (well is null) if (well is null)
throw new ArgumentNullException(nameof(well)); throw new ArgumentInvalidException(nameof(idWell), $"Well {idWell} does not exist");
if (!well.IdTelemetry.HasValue) if (!well.IdTelemetry.HasValue)
throw new ArgumentNullException(nameof(well)); throw new ArgumentInvalidException(nameof(idWell), $"Well {idWell} has no telemetry");
var operations = await DetectOperationsAsync(well.IdTelemetry.Value, DateTime.UnixEpoch, cancellationToken); var operations = await DetectOperationsAsync(well.IdTelemetry.Value, DateTime.UnixEpoch, cancellationToken);
@ -157,9 +159,8 @@ public class DetectedOperationExportService
private static string GetCategoryName(IEnumerable<WellOperationCategory> wellOperationCategories, DetectedOperation current) private static string GetCategoryName(IEnumerable<WellOperationCategory> wellOperationCategories, DetectedOperation current)
{ {
var idCategory = current.IdCategory; var idCategory = current.IdCategory;
if (idCategory == WellOperationCategory.IdSlide if (idCategory == WellOperationCategory.IdSlide &&
&& current.ExtraData[DetectorDrilling.ExtraDataKeyHasOscillation] is bool hasOscillation EnabledSubsystemsFlags.AutoOscillation.HasEnabledSubsystems(current.EnabledSubsystems))
&& hasOscillation)
return "Бурение в слайде с осцилляцией"; return "Бурение в слайде с осцилляцией";
var category = wellOperationCategories.FirstOrDefault(o => o.Id == current.IdCategory); var category = wellOperationCategories.FirstOrDefault(o => o.Id == current.IdCategory);

View File

@ -45,17 +45,13 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
return result; return result;
} }
public async Task<IEnumerable<DetectedOperationDto>?> GetOperationsAsync(DetectedOperationRequest request, CancellationToken token) public async Task<IEnumerable<DetectedOperationDto>> GetOperationsAsync(DetectedOperationRequest request, CancellationToken token)
{ {
var well = await wellService.GetOrDefaultAsync(request.IdWell, token); var well = await wellService.GetOrDefaultAsync(request.IdWell, token);
if (well?.IdTelemetry is null || well.Timezone is null) if (well?.IdTelemetry is null)
return null; return Enumerable.Empty<DetectedOperationDto>();
var query = BuildQuery(well, request) var query = BuildQuery(well, request).AsNoTracking();
?.AsNoTracking();
if (query is null)
return null;
var data = await query.ToListAsync(token); var data = await query.ToListAsync(token);
@ -65,61 +61,6 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
return dtos; return dtos;
} }
public async Task<IEnumerable<OperationsSummaryDto>> GetOperationSummaryAsync(DetectedOperationSummaryRequest request, CancellationToken token)
{
var query = db.Set<DetectedOperation>()
.AsNoTracking();
if (request.IdsTelemetries.Any())
query = query.Where(operation => request.IdsTelemetries.Contains(operation.IdTelemetry));
if (request.IdsOperationCategories.Any())
query = query.Where(operation => request.IdsOperationCategories.Contains(operation.IdCategory));
if (request.GeDateStart.HasValue)
{
var geDateStart = request.GeDateStart.Value.ToUniversalTime();
query = query.Where(operation => operation.DateStart >= geDateStart);
}
if (request.LeDateStart.HasValue)
{
var leDateStart = request.LeDateStart.Value.ToUniversalTime();
query = query.Where(operation => operation.DateStart <= leDateStart);
}
if (request.LeDateEnd.HasValue)
{
var leDateEnd = request.LeDateEnd.Value.ToUniversalTime();
query = query.Where(operation => operation.DateEnd <= leDateEnd);
}
if (request.GeDepthStart.HasValue)
query = query.Where(operation => operation.DepthStart >= request.GeDepthStart.Value);
if (request.LeDepthStart.HasValue)
query = query.Where(operation => operation.DepthStart <= request.LeDepthStart.Value);
if (request.LeDepthEnd.HasValue)
query = query.Where(operation => operation.DepthEnd <= request.LeDepthEnd.Value);
var queryGroup = query
.GroupBy(operation => new { operation.IdTelemetry, operation.IdCategory })
.Select(group => new OperationsSummaryDto
{
IdTelemetry = group.Key.IdTelemetry,
IdCategory = group.Key.IdCategory,
Count = group.Count(),
SumDepthIntervals = group.Sum(operation => operation.DepthEnd - operation.DepthStart),
SumDurationHours = group.Sum(operation => (operation.DateEnd - operation.DateStart).TotalHours)
})
.OrderBy(summ => summ.IdTelemetry)
.ThenBy(summ => summ.IdCategory);
var result = await queryGroup.ToArrayAsync(token);
return result;
}
private static IEnumerable<DetectedOperationDrillersStatDto> GetOperationsDrillersStat(IEnumerable<DetectedOperationDto> operations) private static IEnumerable<DetectedOperationDrillersStatDto> GetOperationsDrillersStat(IEnumerable<DetectedOperationDto> operations)
{ {
var groups = operations.GroupBy(o => o.Driller); var groups = operations.GroupBy(o => o.Driller);
@ -240,23 +181,26 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
if (request is not null) if (request is not null)
{ {
if (request.IdsCategories?.Any() == true) if (request.IdsTelemetries.Any())
query = query.Where(o => request.IdsTelemetries.Contains(o.IdTelemetry));
if (request.IdsCategories.Any())
query = query.Where(o => request.IdsCategories.Contains(o.IdCategory)); query = query.Where(o => request.IdsCategories.Contains(o.IdCategory));
if (request.GtDate is not null) if (request.GeDateStart is not null)
query = query.Where(o => o.DateStart >= request.GtDate.Value.ToUtcDateTimeOffset(well.Timezone.Hours)); query = query.Where(o => o.DateStart >= request.GeDateStart.Value.Date.ToUtcDateTimeOffset(well.Timezone.Hours));
if (request.LtDate is not null) if (request.LeDateEnd is not null)
query = query.Where(o => o.DateEnd <= request.LtDate.Value.ToUtcDateTimeOffset(well.Timezone.Hours)); query = query.Where(o => o.DateEnd <= request.LeDateEnd.Value.Date.ToUtcDateTimeOffset(well.Timezone.Hours));
if (request.GtDepth is not null) if (request.GeDepth is not null)
query = query.Where(o => o.DepthStart >= request.GtDepth); query = query.Where(o => o.DepthStart >= request.GeDepth);
if (request.LtDepth is not null) if (request.LeDepth is not null)
query = query.Where(o => o.DepthEnd <= request.LtDepth); query = query.Where(o => o.DepthEnd <= request.LeDepth);
if (request.EqIdTelemetryUser is not null) if (request.IdTelemetryUser is not null)
query = query.Where(o => o.IdUsersAtStart == request.EqIdTelemetryUser); query = query.Where(o => o.IdUsersAtStart == request.IdTelemetryUser);
} }
return query; return query;

View File

@ -1,7 +1,7 @@
using AsbCloudDb.Model; using AsbCloudDb.Model;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using AsbCloudApp.Data.DetectedOperation;
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
{ {
@ -126,7 +126,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
DepthEnd = (double)pEnd.WellDepth, DepthEnd = (double)pEnd.WellDepth,
ExtraData = ExtraData, ExtraData = ExtraData,
Value = CalcValue(telemetry, begin, end), Value = CalcValue(telemetry, begin, end),
EnabledSubsystems = DetectEnabledSubsystems(telemetry, begin, end) EnabledSubsystems = DetectEnabledSubsystems(telemetry, begin, end, ExtraData)
}; };
return operation; return operation;
@ -155,35 +155,40 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
/// <param name="telemetry"></param> /// <param name="telemetry"></param>
/// <param name="begin"></param> /// <param name="begin"></param>
/// <param name="end"></param> /// <param name="end"></param>
/// <param name="extraData"></param>
/// <returns></returns> /// <returns></returns>
private static int DetectEnabledSubsystems(DetectableTelemetry[] telemetry, int begin, int end) private static int DetectEnabledSubsystems(DetectableTelemetry[] telemetry, int begin, int end, IDictionary<string, object> extraData)
{ {
var enabledSubsystems = 0; var enabledSubsystems = 0;
if (extraData.TryGetValue(DetectorDrilling.ExtraDataKeyHasOscillation, out var hasOscillation)
&& hasOscillation is true)
enabledSubsystems |= (int)EnabledSubsystemsFlags.AutoOscillation;
for (var i = begin; i < end; i += 2) for (var i = begin; i < end; i += 2)
{ {
var mode = telemetry[i].Mode; var mode = telemetry[i].Mode;
if(mode == 1) if(mode == 1)
enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoRotor; enabledSubsystems |= (int)EnabledSubsystemsFlags.AutoRotor;
if (mode == 3) if (mode == 3)
enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoSlide; enabledSubsystems |= (int)EnabledSubsystemsFlags.AutoSlide;
if (mode == 2) if (mode == 2)
enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoConditionig; enabledSubsystems |= (int)EnabledSubsystemsFlags.AutoConditionig;
if (mode == 4) if (mode == 4)
enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoSinking; enabledSubsystems |= (int)EnabledSubsystemsFlags.AutoSinking;
if (mode == 5) if (mode == 5)
enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoLifting; enabledSubsystems |= (int)EnabledSubsystemsFlags.AutoLifting;
if (mode == 6) if (mode == 6)
enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoLiftingWithConditionig; enabledSubsystems |= (int)EnabledSubsystemsFlags.AutoLiftingWithConditionig;
if (mode == 10) if (mode == 10)
enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoBlocknig; enabledSubsystems |= (int)EnabledSubsystemsFlags.AutoBlocknig;
} }
return enabledSubsystems; return enabledSubsystems;

View File

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing;
using System.Linq; using System.Linq;
using AsbCloudDb.Model; using AsbCloudDb.Model;
@ -65,7 +64,7 @@ public class DetectorDrilling : DetectorAbstract
{ {
[ExtraDataKeyAvgRotorSpeed] = avgRotorSpeed, [ExtraDataKeyAvgRotorSpeed] = avgRotorSpeed,
[ExtraDataKeyDispersionOfNormalizedRotorSpeed] = dispersionOfNormalizedRotorSpeed, [ExtraDataKeyDispersionOfNormalizedRotorSpeed] = dispersionOfNormalizedRotorSpeed,
[ExtraDataKeyHasOscillation] = dispersionOfNormalizedRotorSpeed > dispersionOfNormalizedRotorSpeedThreshold [ExtraDataKeyHasOscillation] = avgRotorSpeed > 1 && dispersionOfNormalizedRotorSpeed > dispersionOfNormalizedRotorSpeedThreshold
}; };
return (idCategory, extraData); return (idCategory, extraData);
} }
@ -87,7 +86,7 @@ public class DetectorDrilling : DetectorAbstract
if(dispersionOfNormalizedRotorSpeed < dispersionOfNormalizedRotorSpeedThreshold) if(dispersionOfNormalizedRotorSpeed < dispersionOfNormalizedRotorSpeedThreshold)
return WellOperationCategory.IdRotor; return WellOperationCategory.IdRotor;
else
return idSlideWithOscillation; return idSlideWithOscillation;
} }

View File

@ -0,0 +1,15 @@
using AsbCloudApp.Data.DetectedOperation;
namespace AsbCloudInfrastructure.Services.DetectOperations;
public static class EnabledSubsystemsFlagsExtensions
{
/// <summary>
/// Есть ли флаг подсистемы у операции
/// </summary>
/// <param name="flags"></param>
/// <param name="enabledSubsystems"></param>
/// <returns></returns>
public static bool HasEnabledSubsystems(this EnabledSubsystemsFlags flags, int enabledSubsystems)
=> (enabledSubsystems & (int)flags) > 0;
}

View File

@ -24,4 +24,4 @@
## Метод определения бурения в роторе, слайде с осцилляцией ## Метод определения бурения в роторе, слайде с осцилляцией
Необходимо рассчитать десперсию нормированных оборотов ротора по(по среднему значению) Необходимо рассчитать десперсию нормированных оборотов ротора по(по среднему значению)
1. Если полученное значение больше константы(0,2), то мы подтвердили что бурение в роторе. 1. Если полученное значение больше константы(0,2), то мы подтвердили что бурение в роторе.
2. Если полученное значение меньше константы, то это бурение в слайде с осцилляцией. 2. Если полученное значение меньше константы, и средние обороты ротора больше 1, то это бурение ~~~~в слайде с осцилляцией.

View File

@ -35,7 +35,6 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
private readonly BackgroundWorker backgroundWorker; private readonly BackgroundWorker backgroundWorker;
private readonly NotificationService notificationService; private readonly NotificationService notificationService;
private const int idNotificationCategory = 20000;
private const int idTransportType = 1; private const int idTransportType = 1;
private const int idFileCategoryDrillingProgram = 1000; private const int idFileCategoryDrillingProgram = 1000;
@ -377,7 +376,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
await notificationService.NotifyAsync(new NotifyRequest await notificationService.NotifyAsync(new NotifyRequest
{ {
IdUser = user.Id, IdUser = user.Id,
IdNotificationCategory = idNotificationCategory, IdNotificationCategory = NotificationCategory.IdSystemNotificationCategory,
Title = subject, Title = subject,
Message = body, Message = body,
IdTransportType = idTransportType IdTransportType = idTransportType
@ -398,7 +397,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
await notificationService.NotifyAsync(new NotifyRequest await notificationService.NotifyAsync(new NotifyRequest
{ {
IdUser = user.Id, IdUser = user.Id,
IdNotificationCategory = idNotificationCategory, IdNotificationCategory = NotificationCategory.IdSystemNotificationCategory,
Title = subject, Title = subject,
Message = body, Message = body,
IdTransportType = idTransportType IdTransportType = idTransportType
@ -422,7 +421,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
await notificationService.NotifyAsync(new NotifyRequest await notificationService.NotifyAsync(new NotifyRequest
{ {
IdUser = user.Id, IdUser = user.Id,
IdNotificationCategory = idNotificationCategory, IdNotificationCategory = NotificationCategory.IdSystemNotificationCategory,
Title = subject, Title = subject,
Message = body, Message = body,
IdTransportType = idTransportType IdTransportType = idTransportType
@ -442,7 +441,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
await notificationService.NotifyAsync(new NotifyRequest await notificationService.NotifyAsync(new NotifyRequest
{ {
IdUser = user.Id, IdUser = user.Id,
IdNotificationCategory = idNotificationCategory, IdNotificationCategory = NotificationCategory.IdSystemNotificationCategory,
Title = subject, Title = subject,
Message = body, Message = body,
IdTransportType = idTransportType IdTransportType = idTransportType

View File

@ -1,43 +1,24 @@
using System; using AsbCloudApp.Data;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Mail;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services.Notifications; using AsbCloudApp.Services.Notifications;
using AsbCloudInfrastructure.Background; using AsbCloudInfrastructure.Background;
using DocumentFormat.OpenXml.Presentation;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services.Email namespace AsbCloudInfrastructure.Services.Email
{ {
public class EmailNotificationTransportService : INotificationTransportService public class EmailNotificationTransportService : INotificationTransportService
{ {
private readonly IConfiguration configuration;
private readonly BackgroundWorker backgroundWorker; private readonly BackgroundWorker backgroundWorker;
private readonly bool IsConfigured;
private readonly string sender;
private readonly string smtpServer;
private readonly string smtpPassword;
public EmailNotificationTransportService(BackgroundWorker backgroundWorker, public EmailNotificationTransportService(BackgroundWorker backgroundWorker,
IConfiguration configuration) IConfiguration configuration)
{ {
sender = configuration.GetValue("email:sender", string.Empty); this.configuration = configuration;
smtpPassword = configuration.GetValue("email:password", string.Empty);
smtpServer = configuration.GetValue("email:smtpServer", string.Empty);
var configError = string.IsNullOrEmpty(sender) ||
string.IsNullOrEmpty(smtpPassword) ||
string.IsNullOrEmpty(smtpServer);
IsConfigured = !configError;
this.backgroundWorker = backgroundWorker; this.backgroundWorker = backgroundWorker;
} }
@ -45,18 +26,9 @@ namespace AsbCloudInfrastructure.Services.Email
public Task SendAsync(NotificationDto notification, CancellationToken cancellationToken) public Task SendAsync(NotificationDto notification, CancellationToken cancellationToken)
{ {
if (!IsConfigured) var work = new WorkToSendEmail(notification, configuration);
if (!backgroundWorker.Works.Any(w => w.Id == work.Id))
{ {
Trace.TraceWarning("smtp is not configured");
return Task.CompletedTask;
}
var workId = MakeWorkId(notification.IdUser, notification.Title, notification.Message);
if (!backgroundWorker.Works.Any(w=>w.Id==workId))
{
var workAction = MakeEmailSendWorkAction(notification);
var work = Work.CreateByDelegate(workId, workAction);
backgroundWorker.Enqueue(work); backgroundWorker.Enqueue(work);
} }
@ -71,55 +43,6 @@ namespace AsbCloudInfrastructure.Services.Email
return Task.WhenAll(tasks); return Task.WhenAll(tasks);
} }
private Func<string, IServiceProvider, Action<string, double?>, CancellationToken, Task> MakeEmailSendWorkAction(NotificationDto notification)
{
return async (_, serviceProvider, onProgress, token) =>
{
var notificationRepository = serviceProvider.GetRequiredService<INotificationRepository>();
var userRepository = serviceProvider.GetRequiredService<IUserRepository>();
var user = await userRepository.GetOrDefaultAsync(notification.IdUser, token)
?? throw new ArgumentInvalidException(nameof(notification.IdUser), "Пользователь не найден");
if(!MailAddress.TryCreate(user.Email, out var mailAddress))
{
Trace.TraceWarning($"Mail {user.Email} is not correct.");
throw new ArgumentInvalidException(nameof(user.Email), $"Mail {user.Email} is not null.");
}
var from = new MailAddress(sender);
var message = new MailMessage
{
From = from
};
message.To.Add(mailAddress.Address);
message.BodyEncoding = System.Text.Encoding.UTF8;
message.Body = notification.Message;
message.Subject = notification.Title;
message.IsBodyHtml = true;
using var client = new SmtpClient(smtpServer);
client.EnableSsl = true;
client.UseDefaultCredentials = false;
client.Credentials = new System.Net.NetworkCredential(sender, smtpPassword);
await client.SendMailAsync(message, token);
notification.SentDate = DateTime.UtcNow;
await notificationRepository.UpdateAsync(notification, token);
Trace.TraceInformation($"Send email to {user.Email} subj:{notification.Title} html body count {notification.Message.Length}");
};
}
private static string MakeWorkId(int idUser, string subject, string content)
{
var hash = idUser.GetHashCode();
hash ^= subject.GetHashCode();
hash ^= content.GetHashCode();
return hash.ToString("x");
}
} }
} }

View File

@ -40,7 +40,7 @@ public class ProcessMapReportWellDrillingService : IProcessMapReportWellDrilling
?? throw new ArgumentInvalidException(nameof(idWell), $"Скважина с Id: {idWell} не найдена"); ?? throw new ArgumentInvalidException(nameof(idWell), $"Скважина с Id: {idWell} не найдена");
if (!well.IdTelemetry.HasValue) if (!well.IdTelemetry.HasValue)
throw new ArgumentInvalidException(nameof(idWell), $"Скважина с Id: {idWell} не имеет телеметрии"); return Enumerable.Empty<ProcessMapReportWellDrillingDto>();
var processMapPlanWellDrillings = await processMapPlanWellDrillingRepository.GetByIdWellAsync(idWell, token); var processMapPlanWellDrillings = await processMapPlanWellDrillingRepository.GetByIdWellAsync(idWell, token);

View File

@ -1,11 +1,12 @@
using AsbCloudApp.Data; using AsbCloudApp.Data;
using AsbCloudApp.Data.Progress;
using AsbCloudApp.Requests;
using AsbCloudApp.Services; using AsbCloudApp.Services;
using AsbCloudDb.Model; using AsbCloudDb.Model;
using AsbCloudInfrastructure.Background; using AsbCloudInfrastructure.Background;
using AsbSaubReport; using AsbSaubReport;
using Mapster; using Mapster;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@ -23,8 +24,6 @@ public class ReportService : IReportService
private readonly IWellService wellService; private readonly IWellService wellService;
private readonly BackgroundWorker backgroundWorkerService; private readonly BackgroundWorker backgroundWorkerService;
public int ReportCategoryId { get; private set; }
public ReportService(IAsbCloudDbContext db, public ReportService(IAsbCloudDbContext db,
ITelemetryService telemetryService, ITelemetryService telemetryService,
IWellService wellService, IWellService wellService,
@ -36,68 +35,14 @@ public class ReportService : IReportService
this.backgroundWorkerService = backgroundWorkerService; this.backgroundWorkerService = backgroundWorkerService;
this.telemetryService = telemetryService; this.telemetryService = telemetryService;
this.fileService = fileService; this.fileService = fileService;
ReportCategoryId = db.FileCategories
.AsNoTracking()
.First(c => c.Name.Equals("Рапорт"))
.Id;
} }
public string EnqueueCreateReportWork(int idWell, int idUser, int stepSeconds, int format, DateTime begin, public string EnqueueCreateReportWork(int idWell, int idUser, ReportParametersRequest request, Action<object, string> progressHandler)
DateTime end, Action<object, string> progressHandler)
{ {
var timezoneOffset = wellService.GetTimezone(idWell).Hours; var work = new WorkToCreateReport(idWell, idUser, request, progressHandler);
var beginUtc = begin.ToUtcDateTimeOffset(timezoneOffset);
var endUtc = end.ToUtcDateTimeOffset(timezoneOffset);
var beginRemote = begin.ToTimeZoneOffsetHours(timezoneOffset);
var endRemote = end.ToTimeZoneOffsetHours(timezoneOffset);
var workId = $"create report by wellid:{idWell} for userid:{idUser} requested at {DateTime.Now}"; work.OnErrorAsync = (message, exception, token) => Task.Run(() => {
var state = new ProgressExceptionDto
var workAction = async (string id, IServiceProvider serviceProvider, Action<string, double?> onProgress, CancellationToken token) =>
{
using var context = serviceProvider.GetRequiredService<IAsbCloudDbContext>();
var fileService = serviceProvider.GetRequiredService<FileService>();
var tempDir = Path.Combine(Path.GetTempPath(), "report");
var generator = GetReportGenerator(idWell, beginRemote, endRemote, stepSeconds, format, context);
var reportFileName = Path.Combine(tempDir, generator.GetReportDefaultFileName());
var totalPages = generator.GetPagesCount();
generator.OnProgress += (s, e) =>
{
var arg = e.Adapt<ReportProgressDto>();
onProgress(arg.Operation?? string.Empty, arg.Progress);
progressHandler.Invoke(arg, id);
};
generator.Make(reportFileName);
var fileInfo = (await fileService.MoveAsync(idWell, idUser, ReportCategoryId, reportFileName, reportFileName, token))!;
progressHandler.Invoke(new
{
Operation = "done",
Progress = 100f,
TotalPages = totalPages,
CurrentPage = totalPages,
file = fileInfo,
}, id);
var newReportProperties = new ReportProperty
{
IdWell = idWell,
IdFile = fileInfo.Id,
Begin = beginUtc,
End = endUtc,
Step = stepSeconds,
Format = format
};
context.ReportProperties.Add(newReportProperties);
context.SaveChanges();
};
var work = Work.CreateByDelegate(workId, workAction);
work.OnErrorAsync = (message, exception, token) => Task.Run(() => progressHandler.Invoke(new
{ {
Operation = "error", Operation = "error",
Progress = 100f, Progress = 100f,
@ -105,8 +50,9 @@ public class ReportService : IReportService
? exception.Message ? exception.Message
: message, : message,
Exception = exception, Exception = exception,
}, workId) };
, token); progressHandler.Invoke(state, work.Id);
}, token);
backgroundWorkerService.Enqueue(work); backgroundWorkerService.Enqueue(work);
@ -114,8 +60,8 @@ public class ReportService : IReportService
{ {
Operation = "Ожидает начала в очереди.", Operation = "Ожидает начала в очереди.",
Progress = 0f, Progress = 0f,
}, workId); }, work.Id);
return workId; return work.Id;
} }
public int GetReportPagesCount(int idWell, DateTime begin, DateTime end, int stepSeconds, int format) public int GetReportPagesCount(int idWell, DateTime begin, DateTime end, int stepSeconds, int format)
@ -172,6 +118,62 @@ public class ReportService : IReportService
return dtos; return dtos;
} }
public async Task CreateReportAsync(
string workId,
int idWell,
int idUser,
ReportParametersRequest request,
Action<ProgressDto, string> progressHandler,
CancellationToken token)
{
var timezoneOffset = wellService.GetTimezone(idWell).Hours;
var beginRemote = request.Begin.ToTimeZoneOffsetHours(timezoneOffset);
var endRemote = request.End.ToTimeZoneOffsetHours(timezoneOffset);
var beginUtc = request.Begin.ToUtcDateTimeOffset(timezoneOffset);
var endUtc = request.End.ToUtcDateTimeOffset(timezoneOffset);
var tempDir = Path.Combine(Path.GetTempPath(), "report");
var generator = GetReportGenerator(idWell, beginRemote, endRemote, request.StepSeconds, request.Format, db);
var reportFileName = Path.Combine(tempDir, generator.GetReportDefaultFileName());
var totalPages = generator.GetPagesCount();
generator.OnProgress += (s, e) =>
{
var arg = e.Adapt<ReportProgressDto>();
progressHandler(arg, workId);
};
generator.Make(reportFileName);
var ReportCategoryId = db.FileCategories
.AsNoTracking()
.First(c => c.Name.Equals("Рапорт"))
.Id;
var fileInfo = (await fileService.MoveAsync(idWell, idUser, ReportCategoryId, reportFileName, reportFileName, token))!;
progressHandler(new ReportProgressFinalDto()
{
Operation = "done",
Progress = 100f,
TotalPages = totalPages,
CurrentPage = totalPages,
file = fileInfo,
}, workId);
var newReportProperties = new ReportProperty
{
IdWell = idWell,
IdFile = fileInfo.Id,
Begin = beginUtc,
End = endUtc,
Step = request.StepSeconds,
Format = request.Format
};
db.ReportProperties.Add(newReportProperties);
db.SaveChanges();
}
private static IReportGenerator GetReportGenerator(int idWell, DateTime begin, private static IReportGenerator GetReportGenerator(int idWell, DateTime begin,
DateTime end, int stepSeconds, int format, IAsbCloudDbContext context) DateTime end, int stepSeconds, int format, IAsbCloudDbContext context)
{ {

View File

@ -39,6 +39,8 @@ namespace AsbCloudInfrastructure.Services.SAUB
var timezoneOffset = TimeSpan.FromHours(timezone.Hours); var timezoneOffset = TimeSpan.FromHours(timezone.Hours);
int[] modes = new int[] { 0, 1, 3 }; int[] modes = new int[] { 0, 1, 3 };
db.Database.SetCommandTimeout(TimeSpan.FromMinutes(1.5));
var query = db.Set<TelemetryDataSaub>() var query = db.Set<TelemetryDataSaub>()
.Where(t => t.IdTelemetry == idTelemetry) .Where(t => t.IdTelemetry == idTelemetry)
.Where(t => t.BlockPosition > 0.0001) .Where(t => t.BlockPosition > 0.0001)

View File

@ -1,395 +0,0 @@
using AsbCloudApp.Data;
using AsbCloudApp.Data.DetectedOperation;
using AsbCloudApp.Data.Subsystems;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudApp.Services.Subsystems;
using AsbCloudDb;
using AsbCloudDb.Model;
using AsbCloudDb.Model.Subsystems;
using Mapster;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services.Subsystems;
/// Todo: Выделить репозиторий
internal class SubsystemOperationTimeService : ISubsystemOperationTimeService
{
private readonly IAsbCloudDbContext db;
private readonly IWellService wellService;
private readonly ICrudRepository<SubsystemDto> subsystemService;
private readonly IDetectedOperationService detectedOperationService;
public const int IdSubsystemAKB = 1;
public const int IdSubsystemAKBRotor = 11;
public const int IdSubsystemAKBSlide = 12;
public const int IdSubsystemMSE = 2;
public const int IdSubsystemSpin = 65536;
public const int IdSubsystemTorque = 65537;
public SubsystemOperationTimeService(IAsbCloudDbContext db, IWellService wellService, ICrudRepository<SubsystemDto> subsystemService, IDetectedOperationService detectedOperationService)
{
this.db = db;
this.wellService = wellService;
this.subsystemService = subsystemService;
this.detectedOperationService = detectedOperationService;
}
/// <inheritdoc/>
public async Task<int> DeleteAsync(SubsystemOperationTimeRequest request, CancellationToken token)
{
var well = await wellService.GetOrDefaultAsync(request.IdWell, token)
?? throw new ArgumentInvalidException(nameof(request.IdWell), $"Well Id: {request.IdWell} does not exist");
var query = BuildQuery(request, well);
db.SubsystemOperationTimes.RemoveRange(query);
return await db.SaveChangesAsync(token);
}
/// <inheritdoc/>
public async Task<IEnumerable<SubsystemOperationTimeDto>> GetOperationTimeAsync(SubsystemOperationTimeRequest request, CancellationToken token)
{
var well = await wellService.GetOrDefaultAsync(request.IdWell, token)
?? throw new ArgumentInvalidException(nameof(request.IdWell), $"Well Id: {request.IdWell} does not exist");
var dtos = await GetOperationTimeAsync(request, well, token);
return dtos;
}
private async Task<IEnumerable<SubsystemOperationTimeDto>> GetOperationTimeAsync(SubsystemOperationTimeRequest request, WellDto well, CancellationToken token)
{
var query = BuildQuery(request, well);
IEnumerable<SubsystemOperationTime> data = await query.ToListAsync(token);
if (request.SelectMode == SubsystemOperationTimeRequest.SelectModeInner)
{
if (request.GtDate is not null)
data = data.Where(o => o.DateStart >= request.GtDate.Value);
if (request.LtDate is not null)
data = data.Where(o => o.DateEnd <= request.LtDate.Value);
}
else if (request.SelectMode == SubsystemOperationTimeRequest.SelectModeTrim)
{
var begin = request.GtDate?.ToUtcDateTimeOffset(well.Timezone.Hours);
var end = request.LtDate?.ToUtcDateTimeOffset(well.Timezone.Hours);
data = TrimOperation(data, begin, end);
}
var dtos = data.Select(o => Convert(o, well.Timezone.Hours));
return dtos;
}
/// <inheritdoc/>
public async Task<IEnumerable<SubsystemStatDto>> GetStatAsync(SubsystemOperationTimeRequest request, CancellationToken token)
{
var well = await wellService.GetOrDefaultAsync(request.IdWell, token)
?? throw new ArgumentInvalidException(nameof(request.IdWell), $"Well Id: {request.IdWell} does not exist");
request.SelectMode = SubsystemOperationTimeRequest.SelectModeTrim;
var subsystemsTimes = await GetOperationTimeAsync(request, well, token);
if (subsystemsTimes is null)
return Enumerable.Empty<SubsystemStatDto>();
var detectedOperationSummaryRequest = new DetectedOperationSummaryRequest()
{
IdsTelemetries = new[] {well.IdTelemetry!.Value},
IdsOperationCategories = WellOperationCategory.MechanicalDrillingSubIds,
GeDateStart = request.GtDate,
LeDateStart = request.LtDate,
GeDepthStart = request.GtDepth,
LeDepthStart = request.LtDepth,
};
var operationsSummaries = await detectedOperationService.GetOperationSummaryAsync(detectedOperationSummaryRequest, token);
if(!operationsSummaries.Any())
return Enumerable.Empty<SubsystemStatDto>();
var statList = CalcStat(subsystemsTimes, operationsSummaries);
return statList;
}
private static IEnumerable<SubsystemOperationTime> TrimOperation(IEnumerable<SubsystemOperationTime> data, DateTimeOffset? gtDate, DateTimeOffset? ltDate)
{
if (!ltDate.HasValue && !gtDate.HasValue)
return data.Select(d => d.Adapt<SubsystemOperationTime>());
var items = data.Select((item) =>
{
var operationTime = item.Adapt<SubsystemOperationTime>();
if (!(item.DepthStart.HasValue && item.DepthEnd.HasValue))
return operationTime;
var dateDiff = (item.DateEnd - item.DateStart).TotalSeconds;
var depthDiff = item.DepthEnd.Value - item.DepthStart.Value;
var a = depthDiff / dateDiff;
var b = item.DepthStart.Value;
if (gtDate.HasValue && item.DateStart < gtDate.Value)
{
operationTime.DateStart = gtDate.Value;
var x = (gtDate.Value - item.DateStart).TotalSeconds;
operationTime.DepthStart = (float)(a * x + b);
}
if (ltDate.HasValue && item.DateEnd > ltDate.Value)
{
operationTime.DateEnd = ltDate.Value;
var x = (ltDate.Value - item.DateStart).TotalSeconds;
operationTime.DepthEnd = (float)(a * x + b);
}
return operationTime;
});
return items;
}
private IEnumerable<SubsystemStatDto> CalcStat(
IEnumerable<SubsystemOperationTimeDto> subsystemsTimes,
IEnumerable<OperationsSummaryDto> operationsSummaries)
{
var groupedSubsystemsTimes = subsystemsTimes
.OrderBy(o => o.Id)
.GroupBy(o => o.IdSubsystem);
var periodGroupTotal = subsystemsTimes.Sum(o => (o.DateEnd - o.DateStart).TotalHours);
var result = groupedSubsystemsTimes.Select(g =>
{
var periodGroup = g.Sum(o => (o.DateEnd - o.DateStart).TotalHours);
var periodGroupDepth = g.Sum(o => o.DepthEnd - o.DepthStart);
var (sumOprationsDepth, sumOprationsDurationHours) = AggregateOperationsSummaries(g.Key, operationsSummaries);
var subsystemStat = new SubsystemStatDto()
{
IdSubsystem = g.Key,
SubsystemName = subsystemService.GetOrDefault(g.Key)?.Name ?? "unknown",
UsedTimeHours = periodGroup,
SumOperationDepthInterval = sumOprationsDepth,
SumOperationDurationHours = sumOprationsDurationHours,
SumDepthInterval = periodGroupDepth,
KUsage = periodGroupDepth / sumOprationsDepth,
OperationCount = g.Count(),
};
if (subsystemStat.KUsage > 1)
subsystemStat.KUsage = 1;
return subsystemStat;
});
var apdParts = result.Where(x => x.IdSubsystem == 11 || x.IdSubsystem == 12);
if (apdParts.Any())
{
var apdSum = new SubsystemStatDto()
{
IdSubsystem = IdSubsystemAKB,
SubsystemName = "АПД",
UsedTimeHours = apdParts.Sum(part => part.UsedTimeHours),
SumOperationDepthInterval = apdParts.Sum(part => part.SumOperationDepthInterval),
SumOperationDurationHours = apdParts.Sum(part => part.SumOperationDurationHours),
SumDepthInterval = apdParts.Sum(part => part.SumDepthInterval),
OperationCount = apdParts.Sum(part => part.OperationCount),
};
apdSum.KUsage = apdSum.SumDepthInterval / apdSum.SumOperationDepthInterval;
if (apdSum.KUsage > 1)
apdSum.KUsage = 1;
result = result.Append(apdSum).OrderBy(m => m.IdSubsystem);
}
return result;
}
private static (double SumDepth, double SumDurationHours) AggregateOperationsSummaries(int idSubsystem, IEnumerable<OperationsSummaryDto> operationsSummaries)
=> idSubsystem switch
{
IdSubsystemAKBRotor or IdSubsystemTorque => CalcOperationSummariesByCategories(operationsSummaries, WellOperationCategory.IdRotor),
IdSubsystemAKBSlide or IdSubsystemSpin => CalcOperationSummariesByCategories(operationsSummaries, WellOperationCategory.IdSlide),
IdSubsystemAKB or IdSubsystemMSE => CalcOperationSummariesByCategories(operationsSummaries, WellOperationCategory.IdRotor, WellOperationCategory.IdSlide),
_ => throw new ArgumentException($"idSubsystem: {idSubsystem} does not supported in this method", nameof(idSubsystem)),
};
private static (double SumDepth, double SumDurationHours) CalcOperationSummariesByCategories(
IEnumerable<OperationsSummaryDto> operationsSummaries,
params int[] idsOperationCategories)
{
var filtered = operationsSummaries.Where(sum => idsOperationCategories.Contains(sum.IdCategory));
var sumDepth = filtered.Sum(summ => summ.SumDepthIntervals);
var sumDurationHours = filtered.Sum(summ => summ.SumDurationHours);
return (sumDepth, sumDurationHours);
}
/// <inheritdoc/>
public async Task<IEnumerable<SubsystemActiveWellStatDto>> GetStatByActiveWells(int idCompany, DateTime? gtDate, DateTime? ltDate, CancellationToken token)
{
var activeWells = await wellService.GetAsync(new() { IdCompany = idCompany, IdState = 1 }, token);
var result = await GetStatAsync(activeWells, gtDate, ltDate, token);
return result;
}
/// <inheritdoc/>
public async Task<IEnumerable<SubsystemActiveWellStatDto>> GetStatByActiveWells(IEnumerable<int> wellIds, CancellationToken token)
{
var activeWells = await wellService.GetAsync(new() { Ids = wellIds, IdState = 1 }, token);
var result = await GetStatAsync(activeWells, null, null, token);
return result;
}
private async Task<IEnumerable<SubsystemActiveWellStatDto>> GetStatAsync(IEnumerable<WellDto> wells, DateTime? gtDate, DateTime? ltDate, CancellationToken token)
{
if (!wells.Any())
return Enumerable.Empty<SubsystemActiveWellStatDto>();
var hoursOffset = wells
.FirstOrDefault(well => well.Timezone is not null)
?.Timezone.Hours
?? 5d;
var beginUTC = gtDate.HasValue
? gtDate.Value.ToUtcDateTimeOffset(hoursOffset)
: db.SubsystemOperationTimes.Min(s => s.DateStart)
.DateTime
.ToUtcDateTimeOffset(hoursOffset);
var endUTC = ltDate.HasValue
? ltDate.Value.ToUtcDateTimeOffset(hoursOffset)
: db.SubsystemOperationTimes.Max(s => s.DateEnd)
.DateTime
.ToUtcDateTimeOffset(hoursOffset);
IEnumerable<int> idsTelemetries = wells
.Where(w => w.IdTelemetry is not null)
.Select(w => w.IdTelemetry!.Value)
.Distinct();
var query = db.SubsystemOperationTimes
.Where(o => idsTelemetries.Contains(o.IdTelemetry) &&
o.DateStart >= beginUTC &&
o.DateEnd <= endUTC)
.AsNoTracking();
var subsystemsOperationTime = await query.ToArrayAsync(token);
var operationSummaries = await detectedOperationService
.GetOperationSummaryAsync(new ()
{
IdsTelemetries = idsTelemetries,
IdsOperationCategories = WellOperationCategory.MechanicalDrillingSubIds,
GeDateStart = beginUTC,
LeDateEnd = endUTC,
}, token);
var result = wells
.Select(well => {
var dtos = subsystemsOperationTime
.Where(s => s.IdTelemetry == well.IdTelemetry)
.Select(s => Convert(s, well.Timezone.Hours));
var wellStat = new SubsystemActiveWellStatDto{ Well = well };
var telemetryOperationSummaries = operationSummaries.Where(summ => summ.IdTelemetry == well.IdTelemetry);
if (telemetryOperationSummaries.Any())
{
var subsystemStat = CalcStat(dtos, telemetryOperationSummaries);
if (subsystemStat.Any())
{
wellStat.SubsystemAKB = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemAKB);
wellStat.SubsystemMSE = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemMSE);
wellStat.SubsystemSpinMaster = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemSpin);
wellStat.SubsystemTorqueMaster = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemTorque);
}
}
return wellStat;
});
return result;
}
/// <inheritdoc/>
public async Task<DatesRangeDto?> GetDateRangeOperationTimeAsync(SubsystemOperationTimeRequest request, CancellationToken token)
{
var well = await wellService.GetOrDefaultAsync(request.IdWell, token)
?? throw new ArgumentInvalidException(nameof(request.IdWell), $"Well Id: {request.IdWell} does not exist");
var query = BuildQuery(request, well);
if (query is null)
{
return null;
}
var result = await query
.GroupBy(o => o.IdTelemetry)
.Select(g => new DatesRangeDto
{
From = g.Min(o => o.DateStart).DateTime,
To = g.Max(o => o.DateEnd).DateTime
})
.FirstOrDefaultAsync(token);
return result;
}
private IQueryable<SubsystemOperationTime> BuildQuery(SubsystemOperationTimeRequest request, WellDto well)
{
var idTelemetry = well.IdTelemetry
?? throw new ArgumentInvalidException(nameof(request.IdWell), $"Well Id: {request.IdWell} has no telemetry");
var query = db.SubsystemOperationTimes
.Include(o => o.Subsystem)
.Where(o => o.IdTelemetry == idTelemetry)
.AsNoTracking();
if (request.IdsSubsystems.Any())
query = query.Where(o => request.IdsSubsystems.Contains(o.IdSubsystem));
// # Dates range condition
// [GtDate LtDate]
// [DateStart DateEnd] [DateStart DateEnd]
if (request.GtDate.HasValue)
{
DateTimeOffset gtDate = request.GtDate.Value.ToUtcDateTimeOffset(well.Timezone.Hours);
query = query.Where(o => o.DateEnd >= gtDate);
}
if (request.LtDate.HasValue)
{
DateTimeOffset ltDate = request.LtDate.Value.ToUtcDateTimeOffset(well.Timezone.Hours);
query = query.Where(o => o.DateStart <= ltDate);
}
if (request.GtDepth.HasValue)
query = query.Where(o => o.DepthEnd >= request.GtDepth.Value);
if (request.LtDepth.HasValue)
query = query.Where(o => o.DepthStart <= request.LtDepth.Value);
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 SubsystemOperationTimeDto Convert(SubsystemOperationTime operationTime, double? timezoneHours = null)
{
var dto = operationTime.Adapt<SubsystemOperationTimeDto>();
var hours = timezoneHours ?? operationTime.Telemetry.TimeZone.Hours;
dto.DateStart = operationTime.DateStart.ToRemoteDateTime(hours);
dto.DateEnd = operationTime.DateEnd.ToRemoteDateTime(hours);
return dto;
}
}

View File

@ -0,0 +1,257 @@
using AsbCloudApp.Data;
using AsbCloudApp.Data.DetectedOperation;
using AsbCloudApp.Data.Subsystems;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudInfrastructure.Services.DetectOperations;
namespace AsbCloudInfrastructure.Services.Subsystems;
internal class SubsystemService : ISubsystemService
{
private const int IdSubsystemAPD = 1;
private const int IdSubsystemAPDRotor = 11;
private const int IdSubsystemAPDSlide = 12;
private const int IdSubsystemOscillation = 65536;
private readonly ICrudRepository<SubsystemDto> subsystemRepository;
private readonly IWellService wellService;
private readonly IDetectedOperationService detectedOperationService;
private IDictionary<int, SubsystemDto> subsystems = new Dictionary<int, SubsystemDto>();
public SubsystemService(ICrudRepository<SubsystemDto> subsystemRepository,
IWellService wellService,
IDetectedOperationService detectedOperationService)
{
this.wellService = wellService;
this.subsystemRepository = subsystemRepository;
this.detectedOperationService = detectedOperationService;
}
public async Task<IEnumerable<SubsystemStatDto>> GetStatAsync(SubsystemRequest request, CancellationToken token)
{
var well = await wellService.GetOrDefaultAsync(request.IdWell, token)
?? throw new ArgumentInvalidException(nameof(request.IdWell), $"Well Id: {request.IdWell} does not exist");
if(!well.IdTelemetry.HasValue)
return Enumerable.Empty<SubsystemStatDto>();
var detectedOperationSummaryRequest = new DetectedOperationRequest
{
IdWell = request.IdWell,
IdsTelemetries = new[] { well.IdTelemetry.Value },
IdsCategories = WellOperationCategory.MechanicalDrillingSubIds,
GeDateStart = request.GeDate,
LeDateEnd = request.LeDate,
GeDepth = request.GeDepth,
LeDepth = request.LeDepth,
};
var operations = await detectedOperationService.GetOperationsAsync(detectedOperationSummaryRequest,
token);
if (request.IdDriller.HasValue)
operations = operations.Where(o => o.Driller is not null && o.Driller.Id == request.IdDriller.Value);
if (!operations.Any())
return Enumerable.Empty<SubsystemStatDto>();
var stat = await CalcStatAsync(operations, token);
return stat;
}
public async Task<IEnumerable<SubsystemActiveWellStatDto>> GetStatByActiveWells(int idCompany,
DateTime? gtDate,
DateTime? ltDate,
CancellationToken token)
{
var activeWells = await wellService.GetAsync(new() { IdCompany = idCompany, IdState = 1 }, token);
var result = await GetStatAsync(activeWells, gtDate, ltDate, token);
return result;
}
public async Task<IEnumerable<SubsystemActiveWellStatDto>> GetStatByActiveWells(IEnumerable<int> wellIds, CancellationToken token)
{
var activeWells = await wellService.GetAsync(new() { Ids = wellIds, IdState = 1 }, token);
var result = await GetStatAsync(activeWells, null, null, token);
return result;
}
private async Task<IEnumerable<SubsystemStatDto>> CalcStatAsync(IEnumerable<DetectedOperationDto> operations, CancellationToken token)
{
if (!subsystems.Any())
subsystems = (await subsystemRepository.GetAllAsync(token)).ToDictionary(s => s.Id, s => s);
var oscillationStat = CalcOscillationStat(operations);
var apdStat = CalcApdStat(operations);
var stat = new List<SubsystemStatDto> { oscillationStat };
stat.AddRange(apdStat);
return stat;
}
private SubsystemStatDto CalcOscillationStat(IEnumerable<DetectedOperationDto> operations)
{
operations = operations.Where(o => o.IdCategory == WellOperationCategory.IdSlide);
var (sumDepthInterval, usedTimeHours, operationCount) = AggregateOperations(IdSubsystemOscillation, operations);
var oscillationStat = new SubsystemStatDto
{
IdSubsystem = IdSubsystemOscillation,
SubsystemName = subsystems.TryGetValue(IdSubsystemOscillation, out var subsystemDto) ? subsystemDto.Name : "unknown",
UsedTimeHours = usedTimeHours,
SumOperationDepthInterval = operations.Sum(o => o.DepthEnd - o.DepthStart),
SumOperationDurationHours = operations.Sum(o => o.DurationMinutes / 60),
SumDepthInterval = sumDepthInterval,
OperationCount = operationCount,
};
oscillationStat.KUsage = oscillationStat.SumDepthInterval / oscillationStat.SumOperationDepthInterval;
return oscillationStat;
}
private IEnumerable<SubsystemStatDto> CalcApdStat(IEnumerable<DetectedOperationDto> operations)
{
var apdRotorAndSlide = operations
.Where(o => WellOperationCategory.MechanicalDrillingSubIds.Contains(o.IdCategory))
.GroupBy(o => o.IdCategory)
.Select(group =>
{
var idSubsystem = group.Key switch
{
WellOperationCategory.IdRotor => IdSubsystemAPDRotor,
WellOperationCategory.IdSlide => IdSubsystemAPDSlide,
_ => throw new ArgumentException($"IdCategory: {group.Key} does not supported in this method", nameof(group.Key))
};
var (sumDepthInterval, usedTimeHours, operationCount) = AggregateOperations(idSubsystem, group);
var subsystemStat = new SubsystemStatDto
{
IdSubsystem = idSubsystem,
SubsystemName = subsystems.TryGetValue(idSubsystem, out var subsystemDto) ? subsystemDto.Name : "unknown",
UsedTimeHours = usedTimeHours,
SumOperationDepthInterval = group.Sum(o => o.DepthEnd - o.DepthStart),
SumOperationDurationHours = group.Sum(o => o.DurationMinutes / 60),
SumDepthInterval = sumDepthInterval,
OperationCount = operationCount,
};
subsystemStat.KUsage = subsystemStat.SumDepthInterval / subsystemStat.SumOperationDepthInterval;
return subsystemStat;
});
if (!apdRotorAndSlide.Any())
return Enumerable.Empty<SubsystemStatDto>();
var apdSum = new SubsystemStatDto
{
IdSubsystem = IdSubsystemAPD,
SubsystemName = subsystems.TryGetValue(IdSubsystemAPD, out var subsystemDto) ? subsystemDto.Name : "unknown",
UsedTimeHours = apdRotorAndSlide.Sum(part => part.UsedTimeHours),
SumOperationDepthInterval = apdRotorAndSlide.Sum(part => part.SumOperationDepthInterval),
SumOperationDurationHours = apdRotorAndSlide.Sum(part => part.SumOperationDurationHours),
SumDepthInterval = apdRotorAndSlide.Sum(part => part.SumDepthInterval),
OperationCount = apdRotorAndSlide.Sum(part => part.OperationCount),
};
apdSum.KUsage = apdSum.SumDepthInterval / apdSum.SumOperationDepthInterval;
var apdStat = new List<SubsystemStatDto> { apdSum };
apdStat.AddRange(apdRotorAndSlide);
return apdStat;
}
private static (double SumDepthInterval, double UsedTimeHours, int Count) AggregateOperations(int idSubsystem,
IEnumerable<DetectedOperationDto> operations) =>
idSubsystem switch
{
IdSubsystemAPDRotor => CalcOperationsByEnableSubsystems(operations, EnabledSubsystemsFlags.AutoRotor),
IdSubsystemAPDSlide => CalcOperationsByEnableSubsystems(operations,
EnabledSubsystemsFlags.AutoSlide | EnabledSubsystemsFlags.AutoOscillation),
IdSubsystemOscillation => CalcOperationsByEnableSubsystems(operations, EnabledSubsystemsFlags.AutoOscillation),
_ => throw new ArgumentException($"IdSubsystem: {idSubsystem} does not supported in this method", nameof(idSubsystem))
};
private static (double SumDepthInterval, double UsedTimeHours, int OperationCount) CalcOperationsByEnableSubsystems(
IEnumerable<DetectedOperationDto> operations,
EnabledSubsystemsFlags enabledSubsystems)
{
var filtered = operations.Where(o => enabledSubsystems.HasEnabledSubsystems(o.EnabledSubsystems));
var sumDepthInterval = filtered.Sum(o => o.DepthEnd - o.DepthStart);
var usedTimeHours = filtered.Sum(o => o.DurationMinutes / 60);
var operationCount = filtered.Count();
return (sumDepthInterval, usedTimeHours, operationCount);
}
private async Task<IEnumerable<SubsystemActiveWellStatDto>> GetStatAsync(IEnumerable<WellDto> wells,
DateTime? geDate,
DateTime? leDate,
CancellationToken token)
{
if (!wells.Any())
return Enumerable.Empty<SubsystemActiveWellStatDto>();
var idsTelemetries = wells
.Where(w => w.IdTelemetry is not null)
.Select(w => w.IdTelemetry!.Value)
.Distinct();
var wellsStat = new List<SubsystemActiveWellStatDto>();
foreach (var well in wells)
{
var hoursOffset = well.Timezone.Hours;
var geDateStartUtc = geDate?.ToUtcDateTimeOffset(hoursOffset);
var leDateUtc = leDate?.ToUtcDateTimeOffset(hoursOffset);
var request = new DetectedOperationRequest
{
IdsTelemetries = idsTelemetries,
IdsCategories = WellOperationCategory.MechanicalDrillingSubIds,
GeDateStart = geDateStartUtc,
LeDateEnd = leDateUtc,
};
var operations = await detectedOperationService
.GetOperationsAsync(request, token);
var wellStat = new SubsystemActiveWellStatDto { Well = well };
var telemetryOperations = operations.Where(o => o.IdTelemetry == well.IdTelemetry);
if (!telemetryOperations.Any())
continue;
var subsystemStat = await CalcStatAsync(telemetryOperations, token);
if (!subsystemStat.Any())
continue;
wellStat.SubsystemAPD = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemAPD);
wellStat.SubsystemOscillation = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemOscillation);
}
return wellsStat;
}
}

View File

@ -1,56 +0,0 @@
using AsbCloudDb.Model.Subsystems;
using System;
namespace AsbCloudInfrastructure.Services.Subsystems
{
public class SubsystemDetector
{
private readonly int idTelemetry;
private readonly int idSubsystem;
private readonly Func<short?, bool> isEnable;
private readonly Func<SubsystemOperationTime, bool> isValid;
(bool isEnable, DateTimeOffset date, float depth) pre = default;
public SubsystemDetector(
int idTelemetry,
int idSubsystem,
Func<short?, bool> isEnable,
Func<SubsystemOperationTime, bool> isValid)
{
this.idTelemetry = idTelemetry;
this.idSubsystem = idSubsystem;
this.isEnable = isEnable;
this.isValid = isValid;
}
public bool TryDetect(short? mode, DateTimeOffset date, float depth, out SubsystemOperationTime? subsystemOperationTime)
{
var isEnable = this.isEnable(mode);
if (!pre.isEnable && isEnable)
{
pre = (true, date, depth);
}
else if (pre.isEnable && !isEnable)
{
var detected = new SubsystemOperationTime
{
IdTelemetry = idTelemetry,
IdSubsystem = idSubsystem,
DateStart = pre.date,
DateEnd = date,
DepthStart = pre.depth,
DepthEnd = depth,
};
pre.isEnable = false;
if (isValid(detected))
{
subsystemOperationTime = detected;
return true;
}
}
subsystemOperationTime = null;
return false;
}
}
}

View File

@ -11,6 +11,7 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsbCloudApp.Services.Notifications; using AsbCloudApp.Services.Notifications;
using AsbCloudDb.Model;
namespace AsbCloudInfrastructure.Services namespace AsbCloudInfrastructure.Services
{ {
@ -136,7 +137,6 @@ namespace AsbCloudInfrastructure.Services
private async Task SendMessageAsync(WellDto well, UserDto user, string documentCategory, string message, private async Task SendMessageAsync(WellDto well, UserDto user, string documentCategory, string message,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
const int idNotificationCategory = 20000;
const int idTransportType = 1; const int idTransportType = 1;
var factory = new WellFinalDocumentMailBodyFactory(configuration); var factory = new WellFinalDocumentMailBodyFactory(configuration);
@ -151,7 +151,7 @@ namespace AsbCloudInfrastructure.Services
await notificationService.NotifyAsync(new NotifyRequest await notificationService.NotifyAsync(new NotifyRequest
{ {
IdUser = user.Id, IdUser = user.Id,
IdNotificationCategory = idNotificationCategory, IdNotificationCategory = NotificationCategory.IdSystemNotificationCategory,
Title = subject, Title = subject,
Message = body, Message = body,
IdTransportType = idTransportType IdTransportType = idTransportType

View File

@ -4,9 +4,7 @@ using AsbCloudApp.Data.WITS;
using AsbCloudApp.Repositories; using AsbCloudApp.Repositories;
using AsbCloudApp.Requests; using AsbCloudApp.Requests;
using AsbCloudApp.Services; using AsbCloudApp.Services;
using AsbCloudApp.Services.Subsystems;
using AsbCloudInfrastructure.Background; using AsbCloudInfrastructure.Background;
using AsbCloudInfrastructure.Services.SAUB;
using Mapster; using Mapster;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System; using System;
@ -35,7 +33,7 @@ public class WellInfoService
var wellService = services.GetRequiredService<IWellService>(); var wellService = services.GetRequiredService<IWellService>();
var operationsStatService = services.GetRequiredService<IOperationsStatService>(); var operationsStatService = services.GetRequiredService<IOperationsStatService>();
var processMapPlanWellDrillingRepository = services.GetRequiredService<IProcessMapPlanRepository<ProcessMapPlanWellDrillingDto>>(); var processMapPlanWellDrillingRepository = services.GetRequiredService<IProcessMapPlanRepository<ProcessMapPlanWellDrillingDto>>();
var subsystemOperationTimeService = services.GetRequiredService<ISubsystemOperationTimeService>(); var subsystemService = services.GetRequiredService<ISubsystemService>();
var telemetryDataSaubCache = services.GetRequiredService<ITelemetryDataCache<TelemetryDataSaubDto>>(); var telemetryDataSaubCache = services.GetRequiredService<ITelemetryDataCache<TelemetryDataSaubDto>>();
var messageHub = services.GetRequiredService<IIntegrationEventHandler<UpdateWellInfoEvent>>(); var messageHub = services.GetRequiredService<IIntegrationEventHandler<UpdateWellInfoEvent>>();
@ -57,7 +55,7 @@ public class WellInfoService
var operationsStat = await operationsStatService.GetWellsStatAsync(wellsIds, token); var operationsStat = await operationsStatService.GetWellsStatAsync(wellsIds, token);
var subsystemStat = await subsystemOperationTimeService var subsystemStat = await subsystemService
.GetStatByActiveWells(wellsIds, token); .GetStatByActiveWells(wellsIds, token);
subsystemStat = subsystemStat.ToArray(); subsystemStat = subsystemStat.ToArray();
@ -157,8 +155,8 @@ public class WellInfoService
}; };
var wellSubsystemStat = subsystemStat.FirstOrDefault(s => s.Well.Id == well.Id); var wellSubsystemStat = subsystemStat.FirstOrDefault(s => s.Well.Id == well.Id);
wellMapInfo.SaubUsage = wellSubsystemStat?.SubsystemAKB?.KUsage ?? 0d; wellMapInfo.SaubUsage = wellSubsystemStat?.SubsystemAPD?.KUsage ?? 0d;
wellMapInfo.SpinUsage = wellSubsystemStat?.SubsystemSpinMaster?.KUsage ?? 0d; wellMapInfo.SpinUsage = wellSubsystemStat?.SubsystemOscillation?.KUsage ?? 0d;
wellMapInfo.TorqueKUsage = wellSubsystemStat?.SubsystemTorqueMaster?.KUsage ?? 0d; wellMapInfo.TorqueKUsage = wellSubsystemStat?.SubsystemTorqueMaster?.KUsage ?? 0d;
wellMapInfo.TvdLagDays = wellOperationsStat?.TvdLagDays; wellMapInfo.TvdLagDays = wellOperationsStat?.TvdLagDays;
wellMapInfo.TvdDrillingDays = wellOperationsStat?.TvdDrillingDays; wellMapInfo.TvdDrillingDays = wellOperationsStat?.TvdDrillingDays;

View File

@ -1,4 +1,3 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -36,17 +35,15 @@ public class WellOperationExportService : IWellOperationExportService
IdWell = idWell IdWell = idWell
}, cancellationToken); }, cancellationToken);
var timezone = wellService.GetTimezone(idWell); return MakeExcelFileStream(operations);
return MakeExcelFileStream(operations, timezone.Hours);
} }
private Stream MakeExcelFileStream(IEnumerable<WellOperationDto> operations, double timezoneOffset) private Stream MakeExcelFileStream(IEnumerable<WellOperationDto> operations)
{ {
using Stream ecxelTemplateStream = wellOperationImportTemplateService.GetExcelTemplateStream(); using Stream ecxelTemplateStream = wellOperationImportTemplateService.GetExcelTemplateStream();
using var workbook = new XLWorkbook(ecxelTemplateStream, XLEventTracking.Disabled); using var workbook = new XLWorkbook(ecxelTemplateStream, XLEventTracking.Disabled);
AddOperationsToWorkbook(workbook, operations, timezoneOffset); AddOperationsToWorkbook(workbook, operations);
var memoryStream = new MemoryStream(); var memoryStream = new MemoryStream();
workbook.SaveAs(memoryStream, new SaveOptions { }); workbook.SaveAs(memoryStream, new SaveOptions { });
@ -54,14 +51,14 @@ public class WellOperationExportService : IWellOperationExportService
return memoryStream; return memoryStream;
} }
private void AddOperationsToWorkbook(XLWorkbook workbook, IEnumerable<WellOperationDto> operations, double timezoneOffset) private void AddOperationsToWorkbook(XLWorkbook workbook, IEnumerable<WellOperationDto> operations)
{ {
var planOperations = operations.Where(o => o.IdType == 0); var planOperations = operations.Where(o => o.IdType == 0);
if (planOperations.Any()) if (planOperations.Any())
{ {
var sheetPlan = workbook.Worksheets.FirstOrDefault(ws => ws.Name == DefaultTemplateInfo.SheetNamePlan); var sheetPlan = workbook.Worksheets.FirstOrDefault(ws => ws.Name == DefaultTemplateInfo.SheetNamePlan);
if (sheetPlan is not null) if (sheetPlan is not null)
AddOperationsToSheet(sheetPlan, planOperations, timezoneOffset); AddOperationsToSheet(sheetPlan, planOperations);
} }
var factOperations = operations.Where(o => o.IdType == 1); var factOperations = operations.Where(o => o.IdType == 1);
@ -69,11 +66,11 @@ public class WellOperationExportService : IWellOperationExportService
{ {
var sheetFact = workbook.Worksheets.FirstOrDefault(ws => ws.Name == DefaultTemplateInfo.SheetNameFact); var sheetFact = workbook.Worksheets.FirstOrDefault(ws => ws.Name == DefaultTemplateInfo.SheetNameFact);
if (sheetFact is not null) if (sheetFact is not null)
AddOperationsToSheet(sheetFact, factOperations, timezoneOffset); AddOperationsToSheet(sheetFact, factOperations);
} }
} }
private void AddOperationsToSheet(IXLWorksheet sheet, IEnumerable<WellOperationDto> operations, double timezoneOffset) private void AddOperationsToSheet(IXLWorksheet sheet, IEnumerable<WellOperationDto> operations)
{ {
var operationsToArray = operations.ToArray(); var operationsToArray = operations.ToArray();
@ -83,19 +80,19 @@ public class WellOperationExportService : IWellOperationExportService
for (int i = 0; i < operationsToArray.Length; i++) for (int i = 0; i < operationsToArray.Length; i++)
{ {
var row = sheet.Row(1 + i + DefaultTemplateInfo.HeaderRowsCount); var row = sheet.Row(1 + i + DefaultTemplateInfo.HeaderRowsCount);
AddOperationToRow(row, operationsToArray[i], sections, categories, timezoneOffset); AddOperationToRow(row, operationsToArray[i], sections, categories);
} }
} }
private static void AddOperationToRow(IXLRow row, WellOperationDto operation, IEnumerable<WellSectionTypeDto> sections, private static void AddOperationToRow(IXLRow row, WellOperationDto operation, IEnumerable<WellSectionTypeDto> sections,
IEnumerable<WellOperationCategoryDto> categories, double timezoneOffset) IEnumerable<WellOperationCategoryDto> categories)
{ {
row.Cell(DefaultTemplateInfo.ColumnSection).Value = sections.First(s => s.Id == operation.IdWellSectionType).Caption; row.Cell(DefaultTemplateInfo.ColumnSection).Value = sections.First(s => s.Id == operation.IdWellSectionType).Caption;
row.Cell(DefaultTemplateInfo.ColumnCategory).Value = categories.First(o => o.Id == operation.IdCategory).Name; row.Cell(DefaultTemplateInfo.ColumnCategory).Value = categories.First(o => o.Id == operation.IdCategory).Name;
row.Cell(DefaultTemplateInfo.ColumnCategoryInfo).Value = operation.CategoryInfo; row.Cell(DefaultTemplateInfo.ColumnCategoryInfo).Value = operation.CategoryInfo;
row.Cell(DefaultTemplateInfo.ColumnDepthStart).Value = operation.DepthStart; row.Cell(DefaultTemplateInfo.ColumnDepthStart).Value = operation.DepthStart;
row.Cell(DefaultTemplateInfo.ColumnDepthEnd).Value = operation.DepthEnd; row.Cell(DefaultTemplateInfo.ColumnDepthEnd).Value = operation.DepthEnd;
row.Cell(DefaultTemplateInfo.ColumnDate).Value = new DateTimeOffset(operation.DateStart).ToRemoteDateTime(timezoneOffset); row.Cell(DefaultTemplateInfo.ColumnDate).Value = operation.DateStart;
row.Cell(DefaultTemplateInfo.ColumnDuration).Value = operation.DurationHours; row.Cell(DefaultTemplateInfo.ColumnDuration).Value = operation.DurationHours;
row.Cell(DefaultTemplateInfo.ColumnComment).Value = operation.Comment; row.Cell(DefaultTemplateInfo.ColumnComment).Value = operation.Comment;
} }

View File

@ -77,7 +77,8 @@ public class WellOperationImportService : IWellOperationImportService
DepthStart = row.DepthStart, DepthStart = row.DepthStart,
DepthEnd = row.DepthEnd, DepthEnd = row.DepthEnd,
DateStart = row.Date, DateStart = row.Date,
DurationHours = row.Duration DurationHours = row.Duration,
Comment = row.Comment
}); });
} }
catch (FileFormatException ex) catch (FileFormatException ex)

View File

@ -10,7 +10,6 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsbCloudApp.Data.SAUB; using AsbCloudApp.Data.SAUB;
using AsbCloudInfrastructure.Services.SAUB;
using AsbCloudApp.Repositories; using AsbCloudApp.Repositories;
namespace AsbCloudInfrastructure.Services.WellOperationService; namespace AsbCloudInfrastructure.Services.WellOperationService;
@ -219,7 +218,7 @@ public class OperationsStatService : IOperationsStatService
var factEnd = lastCorrespondingFactOperation.DateStart.AddHours(lastCorrespondingFactOperation.DurationHours); var factEnd = lastCorrespondingFactOperation.DateStart.AddHours(lastCorrespondingFactOperation.DurationHours);
var planEnd = lastCorrespondingPlanOperation.DateStart.AddHours(lastCorrespondingPlanOperation.DurationHours); var planEnd = lastCorrespondingPlanOperation.DateStart.AddHours(lastCorrespondingPlanOperation.DurationHours);
var lagDays = (planEnd - factEnd).TotalDays; var lagDays = (factEnd - planEnd).TotalDays;
return lagDays; return lagDays;
} }

View File

@ -97,6 +97,7 @@ namespace AsbCloudInfrastructure.Services
dto ??= well.Adapt<WellMapInfoWithTelemetryStat>(); dto ??= well.Adapt<WellMapInfoWithTelemetryStat>();
dto.Latitude ??= gCluster.Key.Latitude ?? gDeposit.Key.Latitude; dto.Latitude ??= gCluster.Key.Latitude ?? gDeposit.Key.Latitude;
dto.Longitude ??= gCluster.Key.Longitude ?? gDeposit.Key.Longitude; dto.Longitude ??= gCluster.Key.Longitude ?? gDeposit.Key.Longitude;
dto.Companies = well.RelationCompaniesWells.Select(r => Convert(r.Company));
return dto; return dto;
}), }),
}), }),

View File

@ -32,8 +32,6 @@ namespace AsbCloudInfrastructure
backgroundWorker.Add<WorkToDeleteOldReports>(TimeSpan.FromDays(1)); backgroundWorker.Add<WorkToDeleteOldReports>(TimeSpan.FromDays(1));
backgroundWorker.Add<WellInfoService.WorkWellInfoUpdate>(TimeSpan.FromMinutes(30)); backgroundWorker.Add<WellInfoService.WorkWellInfoUpdate>(TimeSpan.FromMinutes(30));
backgroundWorker.Add<WorkOperationDetection>(TimeSpan.FromMinutes(15)); backgroundWorker.Add<WorkOperationDetection>(TimeSpan.FromMinutes(15));
backgroundWorker.Add<WorkSubsystemAbfOperationTimeCalc>(TimeSpan.FromMinutes(30));
backgroundWorker.Add<WorkSubsystemOscillationOperationTimeCalc>(TimeSpan.FromMinutes(30));
backgroundWorker.Add<WorkLimitingParameterCalc>(TimeSpan.FromMinutes(30)); backgroundWorker.Add<WorkLimitingParameterCalc>(TimeSpan.FromMinutes(30));
backgroundWorker.Add(MakeMemoryMonitoringWork(), TimeSpan.FromMinutes(1)); backgroundWorker.Add(MakeMemoryMonitoringWork(), TimeSpan.FromMinutes(1));

View File

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AsbCloudWebApi.Tests
{
public static class ReflectionExtensions
{
private static readonly Dictionary<Type, object> _commonTypeDictionary = new()
{
{ typeof(int), default(int) },
{ typeof(Guid), default(Guid) },
{ typeof(DateOnly), default(DateOnly) },
{ typeof(DateTime), default(DateTime) },
{ typeof(DateTimeOffset), default(DateTimeOffset) },
{ typeof(TimeOnly), default(TimeOnly) },
{ typeof(long), default(long) },
{ typeof(bool), default(bool) },
{ typeof(double), default(double) },
{ typeof(short), default(short) },
{ typeof(float), default(float) },
{ typeof(byte), default(byte) },
{ typeof(char), default(char) },
{ typeof(uint), default(uint) },
{ typeof(ushort), default(ushort) },
{ typeof(ulong), default(ulong) },
{ typeof(sbyte), default(sbyte) }
};
public static object? GetDefaultValue(this Type type)
{
if (!type.IsValueType)
{
return null;
}
return _commonTypeDictionary.TryGetValue(type, out var value)
? value
: Activator.CreateInstance(type);
}
public static bool IsDefaultValue(this Type type, object? value)
=> (value?.Equals(type.GetDefaultValue()) != false);
}
}

View File

@ -17,7 +17,6 @@ using AsbCloudApp.Repositories;
using AsbCloudApp.Requests; using AsbCloudApp.Requests;
using AsbCloudApp.Services; using AsbCloudApp.Services;
using AsbCloudApp.Services.ProcessMaps.WellDrilling; using AsbCloudApp.Services.ProcessMaps.WellDrilling;
using AsbCloudApp.Services.Subsystems;
using AsbCloudInfrastructure.Services.DailyReport; using AsbCloudInfrastructure.Services.DailyReport;
using NSubstitute; using NSubstitute;
using Xunit; using Xunit;
@ -30,8 +29,6 @@ public class DailyReportServiceTest
private const int idUser = 3; private const int idUser = 3;
private const int idWell = 2; private const int idWell = 2;
private readonly DateTime dateDailyReport = new DateOnly(2023, 10, 26).ToDateTime(TimeOnly.MinValue);
private readonly SubsystemBlockDto fakeSubsystemBlock = new() private readonly SubsystemBlockDto fakeSubsystemBlock = new()
{ {
IdUser = idUser, IdUser = idUser,
@ -191,7 +188,7 @@ public class DailyReportServiceTest
} }
}; };
private readonly SubsystemStatDto fakeStatSubsystemOperationTime = new() private readonly SubsystemStatDto fakeSubsystemsStat = new()
{ {
SubsystemName = "АПД", SubsystemName = "АПД",
SumDepthInterval = 250, SumDepthInterval = 250,
@ -199,12 +196,17 @@ public class DailyReportServiceTest
KUsage = 30 KUsage = 30
}; };
private readonly SimpleTimezoneDto fakeWellTimezone = new()
{
Hours = 5,
};
private readonly IWellService wellServiceMock = Substitute.For<IWellService>(); private readonly IWellService wellServiceMock = Substitute.For<IWellService>();
private readonly ITrajectoryNnbRepository trajectoryFactNnbRepositoryMock = Substitute.For<ITrajectoryNnbRepository>(); private readonly ITrajectoryNnbRepository trajectoryFactNnbRepositoryMock = Substitute.For<ITrajectoryNnbRepository>();
private readonly IDailyReportRepository dailyReportRepositoryMock = Substitute.For<IDailyReportRepository>(); private readonly IDailyReportRepository dailyReportRepositoryMock = Substitute.For<IDailyReportRepository>();
private readonly IScheduleRepository scheduleRepositoryMock = Substitute.For<IScheduleRepository>(); private readonly IScheduleRepository scheduleRepositoryMock = Substitute.For<IScheduleRepository>();
private readonly IWellOperationRepository wellOperationRepositoryMock = Substitute.For<IWellOperationRepository>(); private readonly IWellOperationRepository wellOperationRepositoryMock = Substitute.For<IWellOperationRepository>();
private readonly ISubsystemOperationTimeService subsystemOperationTimeServiceMock = Substitute.For<ISubsystemOperationTimeService>(); private readonly ISubsystemService subsystemServiceMock = Substitute.For<ISubsystemService>();
private readonly IProcessMapReportWellDrillingService processMapReportWellDrillingServiceMock = Substitute.For<IProcessMapReportWellDrillingService>(); private readonly IProcessMapReportWellDrillingService processMapReportWellDrillingServiceMock = Substitute.For<IProcessMapReportWellDrillingService>();
private readonly IDetectedOperationService detectedOperationServiceMock = Substitute.For<IDetectedOperationService>(); private readonly IDetectedOperationService detectedOperationServiceMock = Substitute.For<IDetectedOperationService>();
@ -220,7 +222,7 @@ public class DailyReportServiceTest
{ {
Id = idDailyReport, Id = idDailyReport,
IdWell = idWell, IdWell = idWell,
Date = dateDailyReport, Date = new(2023, 10, 26),
DateLastUpdate = null DateLastUpdate = null
}; };
@ -245,14 +247,14 @@ public class DailyReportServiceTest
dailyReportRepositoryMock, dailyReportRepositoryMock,
scheduleRepositoryMock, scheduleRepositoryMock,
wellOperationRepositoryMock, wellOperationRepositoryMock,
subsystemOperationTimeServiceMock, subsystemServiceMock,
processMapReportWellDrillingServiceMock, processMapReportWellDrillingServiceMock,
detectedOperationServiceMock); detectedOperationServiceMock);
dailyReportRepositoryMock.InsertAsync(Arg.Any<DailyReportDto>(), Arg.Any<CancellationToken>()) dailyReportRepositoryMock.InsertAsync(Arg.Any<DailyReportDto>(), Arg.Any<CancellationToken>())
.ReturnsForAnyArgs(idDailyReport); .ReturnsForAnyArgs(idDailyReport);
dailyReportRepositoryMock.GetOrDefaultAsync(Arg.Any<int>(), Arg.Any<DateTime>(), Arg.Any<CancellationToken>()) dailyReportRepositoryMock.GetOrDefaultAsync(Arg.Any<int>(), Arg.Any<DateOnly>(), Arg.Any<CancellationToken>())
.ReturnsForAnyArgs(fakeDailyReport); .ReturnsForAnyArgs(fakeDailyReport);
dailyReportRepositoryMock.UpdateAsync(Arg.Any<DailyReportDto>(), Arg.Any<CancellationToken>()) dailyReportRepositoryMock.UpdateAsync(Arg.Any<DailyReportDto>(), Arg.Any<CancellationToken>())
@ -276,21 +278,24 @@ public class DailyReportServiceTest
detectedOperationServiceMock.GetAsync(Arg.Any<DetectedOperationRequest>(), Arg.Any<CancellationToken>()) detectedOperationServiceMock.GetAsync(Arg.Any<DetectedOperationRequest>(), Arg.Any<CancellationToken>())
.ReturnsForAnyArgs(fakeWellOperationSlipsTime); .ReturnsForAnyArgs(fakeWellOperationSlipsTime);
subsystemOperationTimeServiceMock.GetStatAsync(Arg.Any<SubsystemOperationTimeRequest>(), Arg.Any<CancellationToken>()) subsystemServiceMock.GetStatAsync(Arg.Any<SubsystemRequest>(), Arg.Any<CancellationToken>())
.ReturnsForAnyArgs(new[] { fakeStatSubsystemOperationTime }); .ReturnsForAnyArgs(new[] { fakeSubsystemsStat });
scheduleRepositoryMock.GetAsync(idWell, dateDailyReport, Arg.Any<CancellationToken>()) scheduleRepositoryMock.GetAsync(Arg.Any<int>(), Arg.Any<DateTime>(), Arg.Any<CancellationToken>())
.ReturnsForAnyArgs(new[] { fakeShedule }); .ReturnsForAnyArgs(new[] { fakeShedule });
processMapReportWellDrillingServiceMock.GetAsync(idWell, Arg.Any<CancellationToken>()) processMapReportWellDrillingServiceMock.GetAsync(Arg.Any<int>(), Arg.Any<CancellationToken>())
.ReturnsForAnyArgs(new[] { fakeProcessMapReportWellDrilling }); .ReturnsForAnyArgs(new[] { fakeProcessMapReportWellDrilling });
wellServiceMock.GetTimezone(Arg.Any<int>())
.ReturnsForAnyArgs(fakeWellTimezone);
} }
[Fact] [Fact]
public async Task UpdateOrInsertAsync_ShouldReturn_UpdatedSubsystemBlock() public async Task UpdateOrInsertAsync_ShouldReturn_UpdatedSubsystemBlock()
{ {
//act //act
var result = await dailyReportService.UpdateOrInsertAsync(idWell, dateDailyReport, idUser, fakeSubsystemBlock, CancellationToken.None); var result = await dailyReportService.UpdateOrInsertAsync(idWell, fakeDailyReport.Date, idUser, fakeSubsystemBlock, CancellationToken.None);
//assert //assert
Assert.NotNull(fakeSubsystemBlock.LastUpdateDate); Assert.NotNull(fakeSubsystemBlock.LastUpdateDate);
@ -301,14 +306,13 @@ public class DailyReportServiceTest
} }
[Theory] [Theory]
[InlineData("2090.01.01")] [MemberData(nameof(DateDailyReport))]
[InlineData("2000.01.01")] public async Task UpdateOrInsertAsync_ShouldReturn_UnableToUpdateDailyReport(DateOnly dateDailyReport)
public async Task UpdateOrInsertAsync_ShouldReturn_UnableToUpdateDailyReport(DateTime dateDaileReport)
{ {
//act //act
var result = await Assert.ThrowsAsync<ArgumentInvalidException>(() => dailyReportService.UpdateOrInsertAsync( var result = await Assert.ThrowsAsync<ArgumentInvalidException>(() => dailyReportService.UpdateOrInsertAsync(
idWell, idWell,
dateDaileReport, dateDailyReport,
idUser, idUser,
fakeSignBlock, fakeSignBlock,
CancellationToken.None)); CancellationToken.None));
@ -321,7 +325,7 @@ public class DailyReportServiceTest
public async Task UpdateOrInsertAsync_ShouldReturn_UpdatedSignBlock() public async Task UpdateOrInsertAsync_ShouldReturn_UpdatedSignBlock()
{ {
//act //act
var result = await dailyReportService.UpdateOrInsertAsync(idWell, dateDailyReport, idUser, fakeSignBlock, CancellationToken.None); var result = await dailyReportService.UpdateOrInsertAsync(idWell, fakeDailyReport.Date, idUser, fakeSignBlock, CancellationToken.None);
//assert //assert
Assert.NotNull(fakeSignBlock.LastUpdateDate); Assert.NotNull(fakeSignBlock.LastUpdateDate);
@ -335,7 +339,7 @@ public class DailyReportServiceTest
public async Task UpdateOrInsertAsync_ShouldReturn_UpdatedTimeBalanceBlock() public async Task UpdateOrInsertAsync_ShouldReturn_UpdatedTimeBalanceBlock()
{ {
//act //act
var result = await dailyReportService.UpdateOrInsertAsync(idWell, dateDailyReport, idUser, fakeTimeBalanceBlock, var result = await dailyReportService.UpdateOrInsertAsync(idWell, fakeDailyReport.Date, idUser, fakeTimeBalanceBlock,
CancellationToken.None); CancellationToken.None);
//assert //assert
@ -347,13 +351,12 @@ public class DailyReportServiceTest
} }
[Theory] [Theory]
[InlineData("2090.01.01")] [MemberData(nameof(DateDailyReport))]
[InlineData("2000.01.01")] public async Task GetAsync_ShouldReturn_UnableToGetDailyReport(DateOnly dateDailyReport)
public async Task GetAsync_ShouldReturn_UnableToGetDailyReport(DateTime dateDaileReport)
{ {
//act //act
var result = await Assert.ThrowsAsync<ArgumentInvalidException>(() => dailyReportService.GetAsync(idWell, var result = await Assert.ThrowsAsync<ArgumentInvalidException>(() => dailyReportService.GetAsync(idWell,
dateDaileReport, dateDailyReport,
CancellationToken.None)); CancellationToken.None));
//assert //assert
@ -364,7 +367,7 @@ public class DailyReportServiceTest
public async Task GetAsync_ShouldReturn_AddedWellInfo() public async Task GetAsync_ShouldReturn_AddedWellInfo()
{ {
//act //act
var result = await dailyReportService.GetAsync(idWell, dateDailyReport, CancellationToken.None); var result = await dailyReportService.GetAsync(idWell, fakeDailyReport.Date, CancellationToken.None);
//assert //assert
Assert.Equal(result.IdWell, fakeWell.Id); Assert.Equal(result.IdWell, fakeWell.Id);
@ -382,7 +385,7 @@ public class DailyReportServiceTest
public async Task GetAsync_ShouldReturn_AddedTrajectoryBlock() public async Task GetAsync_ShouldReturn_AddedTrajectoryBlock()
{ {
//act //act
var result = await dailyReportService.GetAsync(idWell, dateDailyReport, CancellationToken.None); var result = await dailyReportService.GetAsync(idWell, fakeDailyReport.Date, CancellationToken.None);
//assert //assert
Assert.Equal(fakeLastFactTrajectory.WellboreDepth, result.TrajectoryBlock.WellboreDepth); Assert.Equal(fakeLastFactTrajectory.WellboreDepth, result.TrajectoryBlock.WellboreDepth);
@ -395,7 +398,7 @@ public class DailyReportServiceTest
public async Task GetAsync_ShouldReturn_AddedFactWellOperationBlock() public async Task GetAsync_ShouldReturn_AddedFactWellOperationBlock()
{ {
//act //act
var result = await dailyReportService.GetAsync(idWell, dateDailyReport, CancellationToken.None); var result = await dailyReportService.GetAsync(idWell, fakeDailyReport.Date, CancellationToken.None);
//assert //assert
Assert.Equal(16, result.FactWellOperationBlock.SectionDrillingHours); Assert.Equal(16, result.FactWellOperationBlock.SectionDrillingHours);
@ -411,7 +414,7 @@ public class DailyReportServiceTest
public async Task GetAsync_ShouldReturn_AddedScheduleBlock() public async Task GetAsync_ShouldReturn_AddedScheduleBlock()
{ {
//act //act
var result = await dailyReportService.GetAsync(idWell, dateDailyReport, CancellationToken.None); var result = await dailyReportService.GetAsync(idWell, fakeDailyReport.Date, CancellationToken.None);
//assert //assert
Assert.Single(result.ScheduleBlock); Assert.Single(result.ScheduleBlock);
@ -432,7 +435,7 @@ public class DailyReportServiceTest
fakeDailyReport.TimeBalanceBlock = fakeTimeBalanceBlock; fakeDailyReport.TimeBalanceBlock = fakeTimeBalanceBlock;
//act //act
var result = await dailyReportService.GetAsync(idWell, dateDailyReport, CancellationToken.None); var result = await dailyReportService.GetAsync(idWell, fakeDailyReport.Date, CancellationToken.None);
//assert //assert
Assert.NotNull(result.TimeBalanceBlock); Assert.NotNull(result.TimeBalanceBlock);
@ -447,7 +450,7 @@ public class DailyReportServiceTest
public async Task GetAsync_ShouldReturn_AddedProcessMapWellDrillingBlock() public async Task GetAsync_ShouldReturn_AddedProcessMapWellDrillingBlock()
{ {
//act //act
var result = await dailyReportService.GetAsync(idWell, dateDailyReport, CancellationToken.None); var result = await dailyReportService.GetAsync(idWell, fakeDailyReport.Date, CancellationToken.None);
//assert //assert
Assert.Single(result.ProcessMapWellDrillingBlock); Assert.Single(result.ProcessMapWellDrillingBlock);
@ -468,7 +471,7 @@ public class DailyReportServiceTest
fakeDailyReport.SubsystemBlock = fakeSubsystemBlock; fakeDailyReport.SubsystemBlock = fakeSubsystemBlock;
//act //act
var result = await dailyReportService.GetAsync(idDailyReport, dateDailyReport, CancellationToken.None); var result = await dailyReportService.GetAsync(idDailyReport, fakeDailyReport.Date, CancellationToken.None);
//assert //assert
Assert.NotNull(result.SubsystemBlock); Assert.NotNull(result.SubsystemBlock);
@ -527,6 +530,18 @@ public class DailyReportServiceTest
Assert.True(result.To < DateTime.UtcNow.Date); Assert.True(result.To < DateTime.UtcNow.Date);
} }
public static IEnumerable<object[]> DateDailyReport()
{
yield return new object[]
{
new DateOnly(2090, 01, 01),
};
yield return new object[]
{
new DateOnly(2000, 01, 01)
};
}
public static IEnumerable<object[]> FactWellOperationDatesRange() public static IEnumerable<object[]> FactWellOperationDatesRange()
{ {
yield return new object[] yield return new object[]

View File

@ -0,0 +1,200 @@
using AsbCloudApp.Data;
using AsbCloudApp.Data.WellOperationImport.Options;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudApp.Services.WellOperationImport;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Services.WellOperationImport;
using AsbCloudInfrastructure.Services.WellOperationImport.FileParser;
using NSubstitute;
using SignalRSwaggerGen.Enums;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;
namespace AsbCloudWebApi.Tests.UnitTests.Services.WellOperationExport
{
public class WellOperationExportServiceTest
{
private const int idWell = 4;
private IWellService wellService;
private IWellOperationRepository wellOperationRepository;
private IWellOperationImportTemplateService wellOperationImportTemplateService;
private WellOperationExportService wellOperationExportService;
private WellOperationImportService wellOperationImportService;
private IWellOperationExcelParser<WellOperationImportDefaultOptionsDto> wellOperationDefaultExcelParser;
private readonly WellOperationDto[] operations = new WellOperationDto[2] {
new WellOperationDto() {
Id = 5,
IdWell = idWell,
IdUser = 1,
IdType = 0,
IdWellSectionType = 1,
WellSectionTypeName = "1",
IdCategory = 1,
CategoryName = "1",
CategoryInfo = "CategoryInfo 1",
DepthStart = 10,
DepthEnd = 20,
DateStart = GetDate(days: 0),
DurationHours = 10,
Comment = "Комментарий 1",
},
new WellOperationDto() {
Id = 6,
IdWell = idWell,
IdUser = 1,
IdType = 0,
IdWellSectionType = 2,
WellSectionTypeName = "2",
IdCategory = 2,
CategoryName = "2",
CategoryInfo = "CategoryInfo 2",
DepthStart = 20,
DepthEnd = 30,
DateStart = GetDate(days: 1),
DurationHours = 20,
Comment = "Комментарий 2",
}
};
private readonly WellSectionTypeDto[] sectionTypes = new WellSectionTypeDto[2]
{
new WellSectionTypeDto()
{
Caption = "1",
Id = 1,
Order = 0
},
new WellSectionTypeDto()
{
Caption = "2",
Id = 2,
Order = 1
}
};
private readonly WellOperationCategoryDto[] categories = new WellOperationCategoryDto[2]
{
new WellOperationCategoryDto()
{
Id = 1,
IdParent = 1,
KeyValueName = "1",
KeyValueUnits = "1",
Name = "1"
},
new WellOperationCategoryDto()
{
Id = 2,
IdParent = 2,
KeyValueName = "2",
KeyValueUnits = "2",
Name = "2"
}
};
private readonly ITestOutputHelper output;
private static DateTime GetDate(int days)
{
var date = DateTime.Now.AddDays(days);
return new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second);
}
public WellOperationExportServiceTest(ITestOutputHelper output)
{
wellService = Substitute.For<IWellService>();
wellOperationRepository = Substitute.For<IWellOperationRepository>();
wellOperationImportTemplateService = new WellOperationImportTemplateService();
wellOperationExportService = new WellOperationExportService(wellOperationRepository, wellService, wellOperationImportTemplateService);
wellOperationImportService = new WellOperationImportService(wellOperationRepository);
wellOperationDefaultExcelParser = new WellOperationDefaultExcelParser();
this.output = output;
}
[Fact]
public async Task Check_Exported_WellOperations_With_Operations_In_Db()
{
wellService.GetTimezone(idWell).Returns(new SimpleTimezoneDto()
{
Hours = 5
});
wellOperationRepository.GetAsync(Arg.Any<WellOperationRequest>(), Arg.Any<CancellationToken>())
.ReturnsForAnyArgs(operations);
wellOperationRepository.GetSectionTypes().Returns(sectionTypes);
wellOperationRepository.GetCategories(false).Returns(categories);
var stream = await wellOperationExportService.ExportAsync(idWell, CancellationToken.None);
var options = new WellOperationImportDefaultOptionsDto
{
IdType = WellOperation.IdOperationTypePlan
};
var sheet = wellOperationDefaultExcelParser.Parse(stream, options);
var result = wellOperationImportService.Import(idWell, 1, options.IdType, sheet);
var expected = JsonSerializer.Serialize(operations);
var actual = JsonSerializer.Serialize(result);
Assert.Equal(expected, actual);
}
[Fact]
public void TestDataContainsNotDefaultProps()
{
var initOk = true;
for (int i = 0; i < operations.Length; i++)
{
var operation = operations[i];
var propsWithDefault = GetPropsWithDefaultValue(operation,
nameof(WellOperationDto.Id),
nameof(WellOperationDto.IdType),
nameof(WellOperationDto.IdPlan),
nameof(WellOperationDto.IdParentCategory),
nameof(WellOperationDto.Day),
nameof(WellOperationDto.NptHours),
nameof(WellOperationDto.UserName),
nameof(WellOperationDto.LastUpdateDate)
)
.ToArray();
if (propsWithDefault.Any())
{
initOk = false;
foreach (var propertyName in propsWithDefault)
output.WriteLine($"{nameof(operations)}[{i}].{propertyName} is default");
}
}
Assert.True(initOk);
}
private static IEnumerable<string> GetPropsWithDefaultValue<T>(T dto, params string[] excludeProps)
{
IEnumerable<PropertyInfo> props = typeof(T).GetProperties(
BindingFlags.Public
| BindingFlags.Instance);
props = props
.Where(prop=>prop.CanWrite)
.Where(prop => !excludeProps.Contains(prop.Name));
foreach (var prop in props)
{
var value = prop.GetValue(dto);
if (prop.PropertyType.IsDefaultValue(value))
yield return prop.Name;
}
}
}
}

View File

@ -175,8 +175,7 @@ public class DailyReportController : ControllerBase
{ {
await AssertUserAccessToWell(idWell, cancellationToken); await AssertUserAccessToWell(idWell, cancellationToken);
var dailyReport = await dailyReportExportService.ExportAsync(idWell, var dailyReport = await dailyReportExportService.ExportAsync(idWell, dateDailyReport, cancellationToken);
dateDailyReport.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc), cancellationToken);
return File(dailyReport.File, "application/octet-stream", dailyReport.FileName); return File(dailyReport.File, "application/octet-stream", dailyReport.FileName);
} }
@ -187,8 +186,7 @@ public class DailyReportController : ControllerBase
{ {
await AssertUserAccessToWell(idWell, cancellationToken); await AssertUserAccessToWell(idWell, cancellationToken);
var id = await dailyReportService.UpdateOrInsertAsync(idWell, dateDailyReport.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc), IdUser, var id = await dailyReportService.UpdateOrInsertAsync(idWell, dateDailyReport, IdUser, block, cancellationToken);
block, cancellationToken);
return Ok(id); return Ok(id);
} }

View File

@ -152,10 +152,10 @@ public abstract class ProcessMapBaseController<T> : ControllerBase
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
[HttpGet] [HttpGet]
[ProducesResponseType(typeof(ValidationResultDto<>), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status403Forbidden)]
public async Task<ActionResult<ValidationResultDto<T>>> GetAsync(int idWell, CancellationToken cancellationToken) public async Task<ActionResult<IEnumerable<ValidationResultDto<T>>>> GetAsync(int idWell, CancellationToken cancellationToken)
{ {
var processMaps = await service.GetAsync(idWell, cancellationToken); var processMaps = await service.GetAsync(idWell, cancellationToken);

View File

@ -65,8 +65,7 @@ namespace AsbCloudWebApi.Controllers
.GetReportProgress(progress, token); .GetReportProgress(progress, token);
}, token); }, token);
var id = reportService.EnqueueCreateReportWork(idWell, (int)idUser, var id = reportService.EnqueueCreateReportWork(idWell, (int)idUser, request, HandleReportProgressAsync);
request.StepSeconds, request.Format, request.Begin, request.End, HandleReportProgressAsync);
return Ok(id); return Ok(id);
} }

View File

@ -45,7 +45,6 @@ namespace AsbCloudWebApi.Controllers.SAUB
/// <param name = "token" >Токен завершения задачи</param> /// <param name = "token" >Токен завершения задачи</param>
/// <returns></returns> /// <returns></returns>
[HttpGet("{idWell}")] [HttpGet("{idWell}")]
[Permission]
public async Task<ActionResult<IEnumerable<WitsRecordDto>>> GetDataAsync([Required] int idWell, public async Task<ActionResult<IEnumerable<WitsRecordDto>>> GetDataAsync([Required] int idWell,
[FromQuery] GtrWithGetDataRequest request, [FromQuery] GtrWithGetDataRequest request,
CancellationToken token) CancellationToken token)
@ -75,7 +74,6 @@ namespace AsbCloudWebApi.Controllers.SAUB
/// <param name="token"></param> /// <param name="token"></param>
/// <returns></returns> /// <returns></returns>
[HttpGet("{idWell}/{idRecord}")] [HttpGet("{idWell}/{idRecord}")]
[Permission]
public async Task<ActionResult<IEnumerable<WitsItemRecordDto>>> GetLastDataByRecordIdAsync(int idWell, int idRecord, CancellationToken token) public async Task<ActionResult<IEnumerable<WitsItemRecordDto>>> GetLastDataByRecordIdAsync(int idWell, int idRecord, CancellationToken token)
{ {
int? idCompany = User.GetCompanyId(); int? idCompany = User.GetCompanyId();

View File

@ -0,0 +1,92 @@
using AsbCloudApp.Data;
using AsbCloudApp.Data.Subsystems;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudWebApi.Controllers.Subsystems
{
/// <summary>
/// Наработка подсистем
/// </summary>
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class SubsystemController : ControllerBase
{
private readonly ISubsystemService subsystemService;
private readonly ITelemetryDataSaubService telemetryDataSaubService;
private readonly IWellService wellService;
public SubsystemController(
ISubsystemService subsystemService,
IWellService wellService,
ITelemetryDataSaubService telemetryDataSaubService)
{
this.subsystemService = subsystemService;
this.wellService = wellService;
this.telemetryDataSaubService = telemetryDataSaubService;
}
/// <summary>
/// получить статистику
/// </summary>
[HttpGet("stat")]
[ProducesResponseType(typeof(IEnumerable<SubsystemStatDto>), (int)System.Net.HttpStatusCode.OK)]
[ProducesResponseType(typeof(ValidationProblemDetails), (int)System.Net.HttpStatusCode.BadRequest)]
public async Task<IActionResult> GetStatAsync([FromQuery] SubsystemRequest request, CancellationToken token)
{
if (!await UserHasAccessToWellAsync(request.IdWell, token))
return Forbid();
var subsystemResult = await subsystemService.GetStatAsync(request, token);
return Ok(subsystemResult);
}
/// <summary>
/// получить период, за который будет рассчитываться статистика
/// </summary>
[HttpGet("operationsPeriod/{idWell}")]
[ProducesResponseType(typeof(DatesRangeDto), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetStatDateRangeAsync([FromRoute] int idWell, CancellationToken token)
{
if (!await UserHasAccessToWellAsync(idWell, token))
return Forbid();
var dateRange = telemetryDataSaubService.GetRange(idWell);
return Ok(dateRange);
}
/// <summary>
/// получить статистику по активным скважинам
/// </summary>
/// <param name="gtDate"> Больше или равно дате </param>
/// <param name="ltDate"> Меньше или равно дате </param>
/// <param name="token"> Токен </param>
/// <returns> </returns>
[HttpGet("statByActiveWell")]
[ProducesResponseType(typeof(IEnumerable<SubsystemActiveWellStatDto>), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetStatByWellAsync(DateTime? gtDate, DateTime? ltDate, CancellationToken token)
{
var idCompany = User.GetCompanyId();
if (!idCompany.HasValue)
return Forbid();
var subsystemResult = await subsystemService.GetStatByActiveWells(idCompany.Value, gtDate, ltDate, token);
return Ok(subsystemResult);
}
private async Task<bool> UserHasAccessToWellAsync(int idWell, CancellationToken token)
{
var idCompany = User.GetCompanyId();
if (idCompany is not null &&
await wellService.IsCompanyInvolvedInWellAsync((int)idCompany, idWell, token)
.ConfigureAwait(false))
return true;
return false;
}
}
}

View File

@ -1,162 +0,0 @@
using AsbCloudApp.Data;
using AsbCloudApp.Data.Subsystems;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudApp.Services.Subsystems;
using AsbCloudDb.Model;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudWebApi.Controllers.Subsystems
{
/// <summary>
/// Наработка подсистем
/// </summary>
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class SubsystemOperationTimeController : ControllerBase
{
private readonly ISubsystemOperationTimeService subsystemOperationTimeService;
private readonly ITelemetryDataSaubService telemetryDataSaubService;
private readonly IWellService wellService;
private readonly Dictionary<int, string> subsystemNames = new()
{
{ 1, "SAUB" },
{ 65537, "Torque Master" },
{ 65536, "Spin Master" }
};
public SubsystemOperationTimeController(
ISubsystemOperationTimeService subsystemOperationTimeService,
IWellService wellService,
ITelemetryDataSaubService telemetryDataSaubService)
{
this.subsystemOperationTimeService = subsystemOperationTimeService;
this.wellService = wellService;
this.telemetryDataSaubService = telemetryDataSaubService;
}
/// <summary>
/// получить статистику
/// </summary>
[HttpGet("stat")]
[ProducesResponseType(typeof(IEnumerable<SubsystemStatDto>), (int)System.Net.HttpStatusCode.OK)]
[ProducesResponseType(typeof(ValidationProblemDetails), (int)System.Net.HttpStatusCode.BadRequest)]
public async Task<IActionResult> GetStatAsync([FromQuery] SubsystemOperationTimeRequest request, CancellationToken token)
{
if (!await UserHasAccessToWellAsync(request.IdWell, token))
return Forbid();
var subsystemResult = await subsystemOperationTimeService.GetStatAsync(request, token);
return Ok(subsystemResult);
}
/// <summary>
/// получить период, за который будет рассчитываться статистика
/// </summary>
[HttpGet("operationsPeriod/{idWell}")]
[ProducesResponseType(typeof(DatesRangeDto), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetStatDateRangeAsync([FromRoute] int idWell, CancellationToken token)
{
if (!await UserHasAccessToWellAsync(idWell, token))
return Forbid();
var dateRange = telemetryDataSaubService.GetRange(idWell);
return Ok(dateRange);
}
/// <summary>
/// получить статистику по активным скважинам
/// </summary>
/// <param name="GtDate"> Больше или равно дате </param>
/// <param name="LtDate"> Меньше или равно дате </param>
/// <param name="token"> Токен </param>
/// <returns> </returns>
[HttpGet("statByActiveWell")]
[ProducesResponseType(typeof(IEnumerable<SubsystemActiveWellStatDto>), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetStatByWellAsync(DateTime? GtDate, DateTime? LtDate, CancellationToken token)
{
var idCompany = User.GetCompanyId();
if (!idCompany.HasValue)
return Forbid();
var subsystemResult = await subsystemOperationTimeService.GetStatByActiveWells(idCompany.Value, GtDate, LtDate, token);
return Ok(subsystemResult);
}
/// <summary>
/// получить доступный диапазон дат наработки подсистемы.
/// </summary>
[HttpGet("datesRange")]
[ProducesResponseType(typeof(DatesRangeDto), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetDateRangeOperationTimeAsync([FromQuery] SubsystemOperationTimeRequest request, CancellationToken token)
{
if (!await UserHasAccessToWellAsync(request.IdWell, token))
return Forbid();
var result = await subsystemOperationTimeService.GetDateRangeOperationTimeAsync(request, token);
return Ok(result);
}
/// <summary>
/// получить список наработок подсистем
/// </summary>
[HttpGet("operationTime")]
[ProducesResponseType(typeof(IEnumerable<SubsystemOperationTimeDto>), (int)System.Net.HttpStatusCode.OK)]
[ProducesResponseType(typeof(ValidationProblemDetails), (int)System.Net.HttpStatusCode.BadRequest)]
public async Task<IActionResult> GetOperationTimeAsync(
[FromQuery] SubsystemOperationTimeRequest request,
CancellationToken token)
{
if (!await UserHasAccessToWellAsync(request.IdWell, token))
return Forbid();
var result = await subsystemOperationTimeService.GetOperationTimeAsync(request, token);
return Ok(result);
}
/// <summary>
/// Удалить наработки.
/// </summary>
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpDelete]
[Permission]
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
[ProducesResponseType(typeof(ValidationProblemDetails), (int)System.Net.HttpStatusCode.BadRequest)]
public async Task<IActionResult> DeleteAsync(
[FromQuery] SubsystemOperationTimeRequest request,
CancellationToken token)
{
if (!await UserHasAccessToWellAsync(request.IdWell, token))
return Forbid();
var result = await subsystemOperationTimeService.DeleteAsync(request, token);
return Ok(result);
}
/// <summary>
/// Получение словаря названий подсистем
/// </summary>
/// <returns></returns>
[HttpGet("names")]
[ProducesResponseType(typeof(Dictionary<int, string>), (int)System.Net.HttpStatusCode.OK)]
public IActionResult GetSubsystemsNames()
{
return Ok(subsystemNames);
}
protected async Task<bool> UserHasAccessToWellAsync(int idWell, CancellationToken token)
{
var idCompany = User.GetCompanyId();
if (idCompany is not null &&
await wellService.IsCompanyInvolvedInWellAsync((int)idCompany, idWell, token)
.ConfigureAwait(false))
return true;
return false;
}
}
}

View File

@ -403,7 +403,8 @@ namespace AsbCloudWebApi.Controllers
/// <summary> /// <summary>
/// Импорт операций из excel (xlsx) файла. ГПНХ (Хантос) /// Импорт операций из excel (xlsx) файла. ГПНХ (Хантос)
/// </summary> /// </summary>
/// <param name="idWell">id скважины</param> /// <param name="idWell">Id скважины</param>
/// <param name="options">Параметры парсинга</param>
/// <param name="files">Коллекция из одного файла xlsx</param> /// <param name="files">Коллекция из одного файла xlsx</param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
@ -412,13 +413,11 @@ namespace AsbCloudWebApi.Controllers
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
[Permission] [Permission]
public Task<IActionResult> ImportPlanGazpromKhantosExcelFileAsync(int idWell, public Task<IActionResult> ImportPlanGazpromKhantosExcelFileAsync(int idWell,
[FromQuery] WellOperationImportGazpromKhantosOptionsDto options,
[FromForm] IFormFileCollection files, [FromForm] IFormFileCollection files,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var options = new WellOperationImportGazpromKhantosOptionsDto options.IdType = WellOperation.IdOperationTypePlan;
{
IdType = WellOperation.IdOperationTypePlan
};
return ImportExcelFileAsync(idWell, files, options, return ImportExcelFileAsync(idWell, files, options,
(stream, _) => wellOperationGazpromKhantosExcelParser.Parse(stream, options), (stream, _) => wellOperationGazpromKhantosExcelParser.Parse(stream, options),

View File

@ -54,8 +54,7 @@ namespace AsbCloudWebApi.SignalR
.GetReportProgress(progress, CancellationToken.None); .GetReportProgress(progress, CancellationToken.None);
}, CancellationToken.None); }, CancellationToken.None);
var id = reportService.EnqueueCreateReportWork(idWell, (int)idUser, var id = reportService.EnqueueCreateReportWork(idWell, (int)idUser, request, HandleReportProgressAsync);
request.StepSeconds, request.Format, request.Begin, request.End, HandleReportProgressAsync);
} }
} }
} }