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>
public DateTime Date { get; set; }
public DateOnly Date { get; set; }
/// <summary>
/// Дата последнего обновления

View File

@ -12,6 +12,11 @@ namespace AsbCloudApp.Data.DetectedOperation
/// <inheritdoc/>
public int IdWell { get; set; }
/// <summary>
/// Id телеметрии
/// </summary>
public int IdTelemetry { get; set; }
/// <summary>
/// Id названия/описания операции
@ -72,5 +77,10 @@ namespace AsbCloudApp.Data.DetectedOperation
/// Ключевой параметр операции
/// </summary>
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,30 +1,24 @@
using System.Collections.Generic;
namespace AsbCloudApp.Data.Subsystems
namespace AsbCloudApp.Data.Subsystems;
/// <summary>
/// Статистика наработки подсистем по активным скважинам
/// </summary>
public class SubsystemActiveWellStatDto
{
/// <summary>
/// Статистика наработки подсистем по активным скважинам
/// </summary>
public class SubsystemActiveWellStatDto
{
/// <summary>
/// Активная скважина
/// </summary>
public WellInfoDto Well { get; set; } = null!;
/// <summary>
/// Наработки подсистемы АКБ
/// </summary>
public SubsystemStatDto? SubsystemAKB { get; set; }
/// <summary>
/// Наработки подсистемы МСЕ
/// </summary>
public SubsystemStatDto? SubsystemMSE { get; set; }
/// <summary>
/// Наработки подсистемы СПИН
/// </summary>
public SubsystemStatDto? SubsystemSpinMaster { get; set; }
/// <summary>
/// Наработки подсистемы ТОРК
/// </summary>
public SubsystemStatDto? SubsystemTorqueMaster { get; set; }
}
/// <summary>
/// Активная скважина
/// </summary>
public WellInfoDto Well { get; set; } = null!;
/// <summary>
/// Наработки подсистемы АПД
/// </summary>
public SubsystemStatDto? SubsystemAPD { get; set; }
/// <summary>
/// Наработки подсистемы с осцилляцией
/// </summary>
public SubsystemStatDto? SubsystemOscillation { get; set; }
/// <summary>
/// Наработки подсистемы ТОРК
/// </summary>
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 System;
using System.Collections.Generic;
using System.Linq;
namespace AsbCloudApp.Data
{
@ -37,6 +39,11 @@ namespace AsbCloudApp.Data
/// Дата полседнего получения данных от станции контроля параметров цементирования (СКЦ)
/// </summary>
public DateTime? LastDataCpmsDate { get; set; }
/// <summary>
/// Компании
/// </summary>
public IEnumerable<CompanyDto> Companies { get; set; } = Enumerable.Empty<CompanyDto>();
}
/// <summary>

View File

@ -29,5 +29,5 @@ public interface IDailyReportRepository : ICrudRepository<DailyReportDto>
/// <param name="date"></param>
/// <param name="cancellationToken"></param>
/// <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.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace AsbCloudApp.Requests
{
@ -14,36 +15,42 @@ namespace AsbCloudApp.Requests
/// </summary>
[Required]
public int IdWell { get; set; }
/// <summary>
/// Список id телеметрий
/// пустой список - нет фильтрации
/// </summary>
public IEnumerable<int> IdsTelemetries { get; set; } = Array.Empty<int>();
/// <summary>
/// категории операций
/// </summary>
public IEnumerable<int>? IdsCategories { get; set; }
public IEnumerable<int> IdsCategories { get; set; } = Array.Empty<int>();
/// <summary>
/// Больше или равно дате
/// </summary>
public DateTime? GtDate { get; set; }
public DateTimeOffset? GeDateStart { get; set; }
/// <summary>
/// Меньше или равно дате
/// </summary>
public DateTime? LtDate { get; set; }
public DateTimeOffset? LeDateEnd { get; set; }
/// <summary>
/// Больше или равно глубины забоя
/// </summary>
public double? GtDepth { get; set; }
public double? GeDepth { get; set; }
/// <summary>
/// Меньше или равно глубины забоя
/// </summary>
public double? LtDepth { get; set; }
public double? LeDepth { get; set; }
/// <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>
/// <param name="idWell"></param>
/// <param name="dailyReportDateStart"></param>
/// <param name="dailyReportDate"></param>
/// <param name="cancellationToken"></param>
/// <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="idWell"></param>
/// <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)
where TBlock : ItemInfoDto;
@ -32,7 +32,7 @@ public interface IDailyReportService
/// <param name="dateDailyReport"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<DailyReportDto> GetAsync(int idWell, DateTime dateDailyReport, CancellationToken cancellationToken);
Task<DailyReportDto> GetAsync(int idWell, DateOnly dateDailyReport, CancellationToken cancellationToken);
/// <summary>
/// Получить список суточных отчётов по скважине

View File

@ -35,15 +35,7 @@ namespace AsbCloudApp.Services
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
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);
Task<IEnumerable<DetectedOperationDto>> GetOperationsAsync(DetectedOperationRequest request, CancellationToken token);
/// <summary>
/// Удалить операции

View File

@ -1,4 +1,6 @@
using AsbCloudApp.Data;
using AsbCloudApp.Data.Progress;
using AsbCloudApp.Requests;
using System;
using System.Collections.Generic;
using System.Threading;
@ -11,26 +13,30 @@ namespace AsbCloudApp.Services
/// </summary>
public interface IReportService
{
/// <summary>
/// категория рапорта
/// </summary>
int ReportCategoryId { get; }
/// <summary>
/// Поставить рапорт в очередь на формирование
/// </summary>
/// <param name="idWell"></param>
/// <param name="idUser"></param>
/// <param name="stepSeconds"></param>
/// <param name="format"></param>
/// <param name="begin"></param>
/// <param name="end"></param>
/// <param name="request"></param>
/// <param name="handleReportProgress"></param>
/// <returns></returns>
string EnqueueCreateReportWork(int idWell, int idUser, int stepSeconds,
int format, DateTime begin, DateTime end,
string EnqueueCreateReportWork(int idWell, int idUser, ReportParametersRequest request,
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>

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

View File

@ -1,5 +1,4 @@
using AsbCloudDb.Model.GTR;
using AsbCloudDb.Model.Subsystems;
using Microsoft.EntityFrameworkCore;
using System.Threading;
using System.Threading.Tasks;
@ -38,7 +37,6 @@ namespace AsbCloudDb.Model
public virtual DbSet<ReportProperty> ReportProperties => Set<ReportProperty>();
public virtual DbSet<SetpointsRequest> SetpointsRequests => Set<SetpointsRequest>();
public virtual DbSet<Subsystem> Subsystems => Set<Subsystem>();
public virtual DbSet<SubsystemOperationTime> SubsystemOperationTimes => Set<SubsystemOperationTime>();
public virtual DbSet<Telemetry> Telemetries => Set<Telemetry>();
public virtual DbSet<TelemetryDataSaub> TelemetryDataSaub => Set<TelemetryDataSaub>();
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("Дата последнего обновления")]
public DateTime? DateLastUpdate { get; set; }
[Column("date", TypeName = "timestamp with time zone"), Comment("Дата формирования отчёта")]
public DateTime Date { get; set; }
[Column("date", TypeName = "date"), Comment("Дата формирования отчёта")]
public DateOnly Date { get; set; }
[Column("sign_block", TypeName = "jsonb"), Comment("Подпись")]
public SignBlock? SignBlock { get; set; }

View File

@ -24,7 +24,7 @@ namespace AsbCloudDb.Model.DefaultData
{ typeof(WellType), new EntityFillerWellType()},
{ typeof(MeasureCategory), new EntityFillerMeasureCategory()},
{ typeof(CompanyType), new EntityFillerCompanyType()},
{ typeof(Subsystems.Subsystem), new EntityFillerSubsystem() },
{ typeof(Subsystem), new EntityFillerSubsystem() },
{ typeof(NotificationCategory), new EntityNotificationCategory()},
};
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>
{
public override Subsystem[] GetData() => new Subsystem[]{
// САУБ - ид подсистем с 1 до 65_535
new () {Id = 1, Name = "АКБ", Description = "Совместная работа режимов \"Бурение в роторе\" и \"Бурение в слайде\""},
new () {Id = 1, Name = "АПД", Description = "Совместная работа режимов \"Бурение в роторе\" и \"Бурение в слайде\""},
new () {Id = 11, Name = "АПД ротор", Description = "Режим работы \"Бурение в роторе\""},
new () {Id = 12, Name = "АПД слайд", Description = "Режим работы \"Бурение в слайде\""},
new () {Id = 2, Name = "MSE", Description = "Алгоритм поиска оптимальных параметров бурения САУБ"},
//Spin master - id подсистем с 65_536 до 131_071
new () {Id = 65536, Name = "Осцилляция", Description = "Осцилляция"},
new () {Id = 65537, Name = "Демпфер", Description = "Демпфер"}

View File

@ -4,6 +4,6 @@ public class EntityNotificationCategory : EntityFiller<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("Ключевой показатель операции")]
public double Value { get; set; }
[Column("enabled_subsystems"), Comment("флаги аключенных подсистем")]
[Column("enabled_subsystems"), Comment("флаги включенных подсистем")]
public int EnabledSubsystems { get; set; }
[Column("extra_data", TypeName = "jsonb"), Comment("доп. инфо по операции")]
@ -57,57 +57,5 @@ namespace AsbCloudDb.Model
public override string ToString()
=> $"{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.Subsystems;
using AsbCloudDb.Model.WITS;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
@ -41,7 +40,6 @@ namespace AsbCloudDb.Model
DbSet<RelationContactWell> RelationContactsWells { get; }
DbSet<ReportProperty> ReportProperties { get; }
DbSet<Subsystem> Subsystems { get; }
DbSet<SubsystemOperationTime> SubsystemOperationTimes { get; }
DbSet<Telemetry> Telemetries { get; }
DbSet<TelemetryDataSaub> TelemetryDataSaub { get; }
DbSet<TelemetryDataSaubStat> TelemetryDataSaubStats { get; }

View File

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

View File

@ -1,8 +1,8 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
namespace AsbCloudDb.Model.Subsystems
namespace AsbCloudDb.Model
{
[Table("t_subsystem"), Comment("Описание подсистем")]
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.ProcessMaps;
using AsbCloudApp.Services.ProcessMaps.WellDrilling;
using AsbCloudApp.Services.Subsystems;
using AsbCloudApp.Services.WellOperationImport;
using AsbCloudDb.Model;
using AsbCloudDb.Model.Manuals;
using AsbCloudDb.Model.ProcessMaps;
using AsbCloudDb.Model.Subsystems;
using AsbCloudDb.Model.Trajectory;
using AsbCloudInfrastructure.Background;
using AsbCloudInfrastructure.Repository;
@ -211,7 +209,7 @@ namespace AsbCloudInfrastructure
services.AddTransient<IWellOperationRepository, WellOperationRepository>();
services.AddTransient<IDailyReportService, DailyReportService>();
services.AddTransient<IDetectedOperationService, DetectedOperationService>();
services.AddTransient<ISubsystemOperationTimeService, SubsystemOperationTimeService>();
services.AddTransient<ISubsystemService, SubsystemService>();
services.AddTransient<IScheduleRepository, ScheduleRepository>();
services.AddTransient<IRepositoryWellRelated<OperationValueDto>, CrudWellRelatedRepositoryBase<OperationValueDto, OperationValue>>();
services.AddTransient<IUserSettingsRepository, UserSettingsRepository>();
@ -244,7 +242,8 @@ namespace AsbCloudInfrastructure
new CrudCacheRepositoryBase<DepositDto, Deposit>(
s.GetRequiredService<IAsbCloudDbContext>(),
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 =>
new CrudCacheRepositoryBase<CompanyDto, Company>(
s.GetRequiredService<IAsbCloudDbContext>(),

View File

@ -33,25 +33,14 @@ public class DailyReportRepository : CrudRepositoryBase<DailyReportDto, DailyRep
var query = GetQuery().Where(d => d.IdWell == idWell);
if (request.GeDate.HasValue)
{
var geDate = request.GeDate.Value.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc);
query = query.Where(d => d.Date >= geDate);
}
query = query.Where(d => d.Date >= request.GeDate.Value);
if (request.LeDate.HasValue)
{
var leDate = request.LeDate.Value.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc);
query = query.Where(d => d.Date <= leDate);
}
query = query.Where(d => d.Date <= request.LeDate.Value);
if (request.SortFields?.Any() == true)
{
query = query.SortBy(request.SortFields);
}
else
{
query = query.OrderBy(d => d.Date);
}
query = request.SortFields?.Any() == true ?
query.SortBy(request.SortFields) :
query.OrderBy(d => d.Date);
var entities = await query
.Skip(skip)
@ -64,7 +53,7 @@ public class DailyReportRepository : CrudRepositoryBase<DailyReportDto, DailyRep
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()
.AsNoTracking()

View File

@ -3,12 +3,14 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data.ProcessMaps;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudDb.Model.ProcessMaps;
using Microsoft.EntityFrameworkCore;
using Org.BouncyCastle.Asn1.Ocsp;
namespace AsbCloudInfrastructure.Repository;
@ -20,54 +22,69 @@ public class ProcessMapPlanRepository<TDto, TEntity> : CrudWellRelatedRepository
private readonly IWellService wellService;
public ProcessMapPlanRepository(IAsbCloudDbContext context, IWellService wellService)
: base(context, dbSet =>
dbSet
.Include(p => p.WellSectionType)
.Include(p => p.Well))
: base(context, dbSet =>
dbSet
.Include(p => p.WellSectionType)
.Include(p => p.Well))
{
this.wellService = wellService;
this.wellService = wellService;
}
public async Task<IEnumerable<TDto>> GetAsync(IEnumerable<ProcessMapPlanRequest> requests, CancellationToken cancellationToken)
{
var query = BuildQuery(requests);
var query = BuildQuery(requests);
var entities = await query.ToArrayAsync(cancellationToken);
var entities = await query.ToArrayAsync(cancellationToken);
return entities.Select(Convert);
return entities.Select(Convert);
}
public Task<int> RemoveByWellAsync(int idWell)
{
var query = GetQuery().Where(x => x.IdWell == idWell);
var query = GetQuery().Where(x => x.IdWell == idWell);
dbSet.RemoveRange(query);
dbSet.RemoveRange(query);
return dbContext.SaveChangesAsync(CancellationToken.None);
return dbContext.SaveChangesAsync(CancellationToken.None);
}
private IQueryable<TEntity> BuildQuery(IEnumerable<ProcessMapPlanRequest> requests)
{
var query = GetQuery();
var queries = requests
.Select(request => BuildQuery(request))
.ToArray();
var query = queries.FirstOrDefault()
?? throw new ArgumentInvalidException(nameof(requests), "Пустые запросы недопустимы");
foreach (var request in requests)
{
query = query.Where(p => p.IdWell == request.IdWell);
for ( var i = 1; i < queries.Length; i++)
query = query.Union(queries[i]);
if (request.IdWellSectionType is not null)
query = query.Where(p => p.IdWellSectionType == request.IdWellSectionType);
query = query
.Distinct()
.OrderBy(e => e.DepthStart)
.ThenBy(e => e.Id)
.AsNoTracking();
if (request.UpdateFrom is not null)
{
var timezone = wellService.GetTimezone(request.IdWell);
var updateFromUtc = request.UpdateFrom?.ToUtcDateTimeOffset(timezone.Hours);
return query;
}
query = query.Where(p => p.LastUpdate >= updateFromUtc);
}
}
private IQueryable<TEntity> BuildQuery(ProcessMapPlanRequest request)
{
var query = GetQuery();
query = query.Where(p => p.IdWell == request.IdWell);
return query.OrderBy(e => e.DepthStart)
.ThenBy(e => e.Id)
.AsNoTracking();
if (request.IdWellSectionType is not null)
query = query.Where(p => p.IdWellSectionType == request.IdWellSectionType);
if (request.UpdateFrom is not null)
{
var timezone = wellService.GetTimezone(request.IdWell);
var updateFromUtc = request.UpdateFrom?.ToUtcDateTimeOffset(timezone.Hours);
query = query.Where(p => p.LastUpdate >= updateFromUtc);
}
return query;
}
}

View File

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

View File

@ -186,15 +186,20 @@ public class WellOperationRepository : IWellOperationRepository
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);
if (!await query.AnyAsync(cancellationToken))
return null;
var minDate = await query.MinAsync(o => o.DateStart, cancellationToken);
var maxDate = await query.MaxAsync(o => o.DateStart, cancellationToken);
return new DatesRangeDto
{
From = (await query.MinAsync(o => o.DateStart, cancellationToken)).Date,
To = (await query.MaxAsync(o => o.DateStart, cancellationToken)).Date
From = minDate.ToRemoteDateTime(timezone.Hours),
To = maxDate.ToRemoteDateTime(timezone.Hours)
};
}

View File

@ -89,9 +89,9 @@ public class DailyReportExportService : IDailyReportExportService
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);
@ -236,12 +236,16 @@ public class DailyReportExportService : IDailyReportExportService
private static void AddFactWellOperationBlockToSheet(IXLWorksheet sheet, WellOperationBlockDto factWellOperationBlock)
{
var rowCurrent = rowStartFactWellOperationBlock;
sheet.Cell(cellSectionDrillingHours).Value = factWellOperationBlock.SectionDrillingHours;
foreach (var factOperation in factWellOperationBlock.WellOperations.OrderBy(w => w.CategoryName))
{
sheet.Cell(rowStartFactWellOperationBlock, columnWellOperationCategory).Value = factOperation.CategoryName;
sheet.Cell(rowStartFactWellOperationBlock, columnWellOperationDurationHours).Value = factOperation.DurationHours;
sheet.Cell(rowCurrent, columnWellOperationCategory).Value = factOperation.CategoryName;
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.Services.DailyReport;
using AsbCloudApp.Services.ProcessMaps.WellDrilling;
using AsbCloudApp.Services.Subsystems;
using AsbCloudDb.Model;
using Mapster;
@ -29,7 +28,7 @@ public class DailyReportService : IDailyReportService
private readonly IDailyReportRepository dailyReportRepository;
private readonly IScheduleRepository scheduleRepository;
private readonly IWellOperationRepository wellOperationRepository;
private readonly ISubsystemOperationTimeService subsystemOperationTimeService;
private readonly ISubsystemService subsystemService;
private readonly IProcessMapReportWellDrillingService processMapReportWellDrillingService;
private readonly IDetectedOperationService detectedOperationService;
@ -38,7 +37,7 @@ public class DailyReportService : IDailyReportService
IDailyReportRepository dailyReportRepository,
IScheduleRepository scheduleRepository,
IWellOperationRepository wellOperationRepository,
ISubsystemOperationTimeService subsystemOperationTimeService,
ISubsystemService subsystemService,
IProcessMapReportWellDrillingService processMapReportWellDrillingService,
IDetectedOperationService detectedOperationService)
{
@ -47,12 +46,12 @@ public class DailyReportService : IDailyReportService
this.dailyReportRepository = dailyReportRepository;
this.scheduleRepository = scheduleRepository;
this.wellOperationRepository = wellOperationRepository;
this.subsystemOperationTimeService = subsystemOperationTimeService;
this.subsystemService = subsystemService;
this.processMapReportWellDrillingService = processMapReportWellDrillingService;
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)
where TBlock : ItemInfoDto
{
@ -92,7 +91,7 @@ public class DailyReportService : IDailyReportService
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)
?? 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
{
Date = dateDailyReport.Date,
Date = dateDailyReport,
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
{
IdWell = idWell,
OperationType = WellOperation.IdOperationTypeFact,
GeDate = dateDailyReport,
LtDate = dateDailyReport.AddHours(24)
GeDate = geDate,
LtDate = ltDate
};
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++)
{
var dateDailyReport = datesRange.To.AddDays(-day).Date;
var dateDailyReport = DateOnly.FromDateTime(datesRange.To.AddDays(-day));
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++)
{
var dateDailyReport = datesRange.From.AddDays(day).Date;
var dateDailyReport = DateOnly.FromDateTime(datesRange.From.AddDays(day));
AddDailyReport(dateDailyReport);
}
@ -217,7 +219,7 @@ public class DailyReportService : IDailyReportService
return result;
void AddDailyReport(DateTime date)
void AddDailyReport(DateOnly date)
{
var dailyReport = existingDailyReports.FirstOrDefault(d => d.IdWell == idWell && d.Date == date)
?? new DailyReportDto
@ -226,7 +228,11 @@ public class DailyReportService : IDailyReportService
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);
@ -236,16 +242,29 @@ public class DailyReportService : IDailyReportService
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,
cancellationToken);
if (factOperationDatesRange is 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
{
From = factOperationDatesRange.From.AddDays(1) <= DateTime.UtcNow ? factOperationDatesRange.From : DateTime.UtcNow.Date.AddDays(-1),
To = factOperationDatesRange.To.AddDays(1) <= DateTime.UtcNow ? factOperationDatesRange.To : DateTime.UtcNow.Date.AddDays(-1)
From = from,
To = to
};
}
@ -259,13 +278,16 @@ public class DailyReportService : IDailyReportService
dailyReport.TimeBalanceBlock.SectionName = wellOperationRepository.GetSectionTypes()
.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(
new DetectedOperationRequest
{
IdsCategories = new[] { idWellOperationSlipsTime },
IdWell = dailyReport.IdWell,
GtDate = dailyReport.Date,
LtDate = dailyReport.Date.AddHours(24)
GeDateStart = geDateStart,
LeDateEnd = leDateEnd
}, cancellationToken))?.Stats.Sum(s => s.Count);
dailyReport.TimeBalanceBlock.WellDepth.Fact = factWellOperations
@ -276,11 +298,14 @@ public class DailyReportService : IDailyReportService
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
{
IdWell = dailyReport.IdWell,
GeDate = dailyReport.Date,
LeDate = dailyReport.Date.AddHours(24)
GeDate = geDate,
LeDate = leDate
}, cancellationToken)).MaxBy(t => t.WellboreDepth);
dailyReport.TrajectoryBlock = new TrajectoryBlockDto
@ -292,8 +317,11 @@ public class DailyReportService : IDailyReportService
};
}
private async Task AddScheduleBlockAsync(DailyReportDto dailyReport, CancellationToken cancellationToken) =>
dailyReport.ScheduleBlock = (await scheduleRepository.GetAsync(dailyReport.IdWell, dailyReport.Date, cancellationToken))
private async Task AddScheduleBlockAsync(DailyReportDto dailyReport, CancellationToken cancellationToken)
{
var workDate = dailyReport.Date.ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified);
dailyReport.ScheduleBlock = (await scheduleRepository.GetAsync(dailyReport.IdWell, workDate, cancellationToken))
.Select(s => new ScheduleRecordDto
{
ShiftStart = s.ShiftStart,
@ -302,6 +330,7 @@ public class DailyReportService : IDailyReportService
Surname = s.Driller?.Surname,
Patronymic = s.Driller?.Patronymic
});
}
private async Task UpdateSubsystemBlockAsync(DailyReportDto dailyReport, CancellationToken cancellationToken)
{
@ -311,24 +340,27 @@ public class DailyReportService : IDailyReportService
async Task<IEnumerable<SubsystemRecordDto>> GetSubsystemsAsync()
{
var subsystemOperationTimesPerWell = await subsystemOperationTimeService.GetStatAsync(new SubsystemOperationTimeRequest
var subsystemsStatPerWell = await subsystemService.GetStatAsync(new SubsystemRequest
{
IdWell = dailyReport.IdWell
}, 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,
GtDate = dailyReport.Date,
LtDate = dailyReport.Date.AddHours(24)
GeDate = geDate,
LeDate = leDate
}, cancellationToken);
var subsystems = subsystemOperationTimesPerWell
.Select(subsystemOperationTime => new SubsystemRecordDto
var subsystems = subsystemsStatPerWell
.Select(subsystemStatPerWell => new SubsystemRecordDto
{
Name = subsystemOperationTime.SubsystemName,
UsagePerDay = subsystemOperationTimesPerDay.FirstOrDefault(s => s.IdSubsystem == subsystemOperationTime.IdSubsystem)?.Adapt<SubsystemParametersDto>(),
UsagePerWell = subsystemOperationTime.Adapt<SubsystemParametersDto>()
Name = subsystemStatPerWell.SubsystemName,
UsagePerDay = subsystemsStatPerDay.FirstOrDefault(s => s.IdSubsystem == subsystemStatPerWell.IdSubsystem)?.Adapt<SubsystemParametersDto>(),
UsagePerWell = subsystemStatPerWell.Adapt<SubsystemParametersDto>()
}).ToList();
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)
{
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,
cancellationToken)).Where(p => p.DateStart >= dailyReport.Date &&
p.DateStart <= dailyReport.Date.AddHours(24))
cancellationToken)).Where(p => p.DateStart >= geDate && p.DateStart <= leDate)
.GroupBy(p => p.DrillingMode)
.Select(g => new ProcessMapWellDrillingRecordDto
{
@ -363,7 +397,7 @@ public class DailyReportService : IDailyReportService
dailyReport.FactWellOperationBlock = new WellOperationBlockDto
{
WellOperations = factWellOperations.GroupBy(o => o.IdParentCategory)
WellOperations = factWellOperations.GroupBy(o => o.IdCategory)
.Select(g => new WellOperationRecordDto
{
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);
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.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data.DetectedOperation;
using AsbCloudInfrastructure.Services.DetectOperations.Detectors;
using AsbCloudApp.Repositories;
using Microsoft.AspNetCore.Http.Extensions;
using AsbCloudApp.Exceptions;
namespace AsbCloudInfrastructure.Services.DetectOperations;
@ -56,21 +58,21 @@ public class DetectedOperationExportService
/// <param name="host">хост</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentInvalidException"></exception>
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)
.ThenInclude(c => c.Deposit)
.SingleOrDefaultAsync(w => w.Id == idWell, cancellationToken);
.FirstOrDefaultAsync(w => w.Id == idWell, cancellationToken);
if (well is null)
throw new ArgumentNullException(nameof(well));
throw new ArgumentInvalidException(nameof(idWell), $"Well {idWell} does not exist");
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);
return await GenerateExcelFileStreamAsync(well, host, operations, cancellationToken);
}
@ -157,9 +159,8 @@ public class DetectedOperationExportService
private static string GetCategoryName(IEnumerable<WellOperationCategory> wellOperationCategories, DetectedOperation current)
{
var idCategory = current.IdCategory;
if (idCategory == WellOperationCategory.IdSlide
&& current.ExtraData[DetectorDrilling.ExtraDataKeyHasOscillation] is bool hasOscillation
&& hasOscillation)
if (idCategory == WellOperationCategory.IdSlide &&
EnabledSubsystemsFlags.AutoOscillation.HasEnabledSubsystems(current.EnabledSubsystems))
return "Бурение в слайде с осцилляцией";
var category = wellOperationCategories.FirstOrDefault(o => o.Id == current.IdCategory);

View File

@ -45,17 +45,13 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
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);
if (well?.IdTelemetry is null || well.Timezone is null)
return null;
if (well?.IdTelemetry is null)
return Enumerable.Empty<DetectedOperationDto>();
var query = BuildQuery(well, request)
?.AsNoTracking();
if (query is null)
return null;
var query = BuildQuery(well, request).AsNoTracking();
var data = await query.ToListAsync(token);
@ -65,61 +61,6 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
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)
{
var groups = operations.GroupBy(o => o.Driller);
@ -240,23 +181,26 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
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));
if (request.GtDate is not null)
query = query.Where(o => o.DateStart >= request.GtDate.Value.ToUtcDateTimeOffset(well.Timezone.Hours));
if (request.GeDateStart is not null)
query = query.Where(o => o.DateStart >= request.GeDateStart.Value.Date.ToUtcDateTimeOffset(well.Timezone.Hours));
if (request.LtDate is not null)
query = query.Where(o => o.DateEnd <= request.LtDate.Value.ToUtcDateTimeOffset(well.Timezone.Hours));
if (request.LeDateEnd is not null)
query = query.Where(o => o.DateEnd <= request.LeDateEnd.Value.Date.ToUtcDateTimeOffset(well.Timezone.Hours));
if (request.GtDepth is not null)
query = query.Where(o => o.DepthStart >= request.GtDepth);
if (request.GeDepth is not null)
query = query.Where(o => o.DepthStart >= request.GeDepth);
if (request.LtDepth is not null)
query = query.Where(o => o.DepthEnd <= request.LtDepth);
if (request.LeDepth is not null)
query = query.Where(o => o.DepthEnd <= request.LeDepth);
if (request.EqIdTelemetryUser is not null)
query = query.Where(o => o.IdUsersAtStart == request.EqIdTelemetryUser);
if (request.IdTelemetryUser is not null)
query = query.Where(o => o.IdUsersAtStart == request.IdTelemetryUser);
}
return query;

View File

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

View File

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using AsbCloudDb.Model;
@ -65,15 +64,15 @@ public class DetectorDrilling : DetectorAbstract
{
[ExtraDataKeyAvgRotorSpeed] = avgRotorSpeed,
[ExtraDataKeyDispersionOfNormalizedRotorSpeed] = dispersionOfNormalizedRotorSpeed,
[ExtraDataKeyHasOscillation] = dispersionOfNormalizedRotorSpeed > dispersionOfNormalizedRotorSpeedThreshold
[ExtraDataKeyHasOscillation] = avgRotorSpeed > 1 && dispersionOfNormalizedRotorSpeed > dispersionOfNormalizedRotorSpeedThreshold
};
return (idCategory, extraData);
}
private static (double avgRotorSpeed, double dispersionOfNormalizedRotorSpeed) CalcCriteries(DetectableTelemetry[] telemetry, int begin, int end)
{
var telemetryRange = telemetry[begin..end];
var avgRotorSpeed = telemetryRange.Average(t => t.RotorSpeed);
var telemetryRange = telemetry[begin..end];
var avgRotorSpeed = telemetryRange.Average(t => t.RotorSpeed);
var dispersion = telemetryRange.Average(t => Math.Pow(t.RotorSpeed / avgRotorSpeed - 1, 2));
return (avgRotorSpeed, dispersion);
}
@ -85,10 +84,10 @@ public class DetectorDrilling : DetectorAbstract
if (avgRotorSpeed < 5)
return WellOperationCategory.IdSlide;
if (dispersionOfNormalizedRotorSpeed < dispersionOfNormalizedRotorSpeedThreshold)
if(dispersionOfNormalizedRotorSpeed < dispersionOfNormalizedRotorSpeedThreshold)
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), то мы подтвердили что бурение в роторе.
2. Если полученное значение меньше константы, то это бурение в слайде с осцилляцией.
2. Если полученное значение меньше константы, и средние обороты ротора больше 1, то это бурение ~~~~в слайде с осцилляцией.

View File

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

View File

@ -1,62 +1,34 @@
using System;
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.Data;
using AsbCloudApp.Services.Notifications;
using AsbCloudInfrastructure.Background;
using DocumentFormat.OpenXml.Presentation;
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
{
public class EmailNotificationTransportService : INotificationTransportService
{
private readonly IConfiguration configuration;
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)
{
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;
this.configuration = configuration;
this.backgroundWorker = backgroundWorker;
}
public int IdTransportType => 1;
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);
}
@ -70,56 +42,7 @@ namespace AsbCloudInfrastructure.Services.Email
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} не найдена");
if (!well.IdTelemetry.HasValue)
throw new ArgumentInvalidException(nameof(idWell), $"Скважина с Id: {idWell} не имеет телеметрии");
return Enumerable.Empty<ProcessMapReportWellDrillingDto>();
var processMapPlanWellDrillings = await processMapPlanWellDrillingRepository.GetByIdWellAsync(idWell, token);

View File

@ -1,11 +1,12 @@
using AsbCloudApp.Data;
using AsbCloudApp.Data.Progress;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Background;
using AsbSaubReport;
using Mapster;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.IO;
@ -23,8 +24,6 @@ public class ReportService : IReportService
private readonly IWellService wellService;
private readonly BackgroundWorker backgroundWorkerService;
public int ReportCategoryId { get; private set; }
public ReportService(IAsbCloudDbContext db,
ITelemetryService telemetryService,
IWellService wellService,
@ -36,68 +35,14 @@ public class ReportService : IReportService
this.backgroundWorkerService = backgroundWorkerService;
this.telemetryService = telemetryService;
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,
DateTime end, Action<object, string> progressHandler)
public string EnqueueCreateReportWork(int idWell, int idUser, ReportParametersRequest request, Action<object, string> progressHandler)
{
var timezoneOffset = wellService.GetTimezone(idWell).Hours;
var beginUtc = begin.ToUtcDateTimeOffset(timezoneOffset);
var endUtc = end.ToUtcDateTimeOffset(timezoneOffset);
var beginRemote = begin.ToTimeZoneOffsetHours(timezoneOffset);
var endRemote = end.ToTimeZoneOffsetHours(timezoneOffset);
var work = new WorkToCreateReport(idWell, idUser, request, progressHandler);
var workId = $"create report by wellid:{idWell} for userid:{idUser} requested at {DateTime.Now}";
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
work.OnErrorAsync = (message, exception, token) => Task.Run(() => {
var state = new ProgressExceptionDto
{
Operation = "error",
Progress = 100f,
@ -105,8 +50,9 @@ public class ReportService : IReportService
? exception.Message
: message,
Exception = exception,
}, workId)
, token);
};
progressHandler.Invoke(state, work.Id);
}, token);
backgroundWorkerService.Enqueue(work);
@ -114,8 +60,8 @@ public class ReportService : IReportService
{
Operation = "Ожидает начала в очереди.",
Progress = 0f,
}, workId);
return workId;
}, work.Id);
return work.Id;
}
public int GetReportPagesCount(int idWell, DateTime begin, DateTime end, int stepSeconds, int format)
@ -172,6 +118,62 @@ public class ReportService : IReportService
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,
DateTime end, int stepSeconds, int format, IAsbCloudDbContext context)
{
@ -184,7 +186,7 @@ public class ReportService : IReportService
_ => new AsbSaubReportPdf.ReprotGeneratorPdf(dataSource),
};
if(begin == default || end == default)
if (begin == default || end == default)
{
var analyzeResult = dataSource.Analyze();
begin = begin == default ? analyzeResult.MinDate : begin;

View File

@ -39,6 +39,8 @@ namespace AsbCloudInfrastructure.Services.SAUB
var timezoneOffset = TimeSpan.FromHours(timezone.Hours);
int[] modes = new int[] { 0, 1, 3 };
db.Database.SetCommandTimeout(TimeSpan.FromMinutes(1.5));
var query = db.Set<TelemetryDataSaub>()
.Where(t => t.IdTelemetry == idTelemetry)
.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.Tasks;
using AsbCloudApp.Services.Notifications;
using AsbCloudDb.Model;
namespace AsbCloudInfrastructure.Services
{
@ -136,7 +137,6 @@ namespace AsbCloudInfrastructure.Services
private async Task SendMessageAsync(WellDto well, UserDto user, string documentCategory, string message,
CancellationToken cancellationToken)
{
const int idNotificationCategory = 20000;
const int idTransportType = 1;
var factory = new WellFinalDocumentMailBodyFactory(configuration);
@ -151,7 +151,7 @@ namespace AsbCloudInfrastructure.Services
await notificationService.NotifyAsync(new NotifyRequest
{
IdUser = user.Id,
IdNotificationCategory = idNotificationCategory,
IdNotificationCategory = NotificationCategory.IdSystemNotificationCategory,
Title = subject,
Message = body,
IdTransportType = idTransportType

View File

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

View File

@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@ -36,17 +35,15 @@ public class WellOperationExportService : IWellOperationExportService
IdWell = idWell
}, cancellationToken);
var timezone = wellService.GetTimezone(idWell);
return MakeExcelFileStream(operations, timezone.Hours);
return MakeExcelFileStream(operations);
}
private Stream MakeExcelFileStream(IEnumerable<WellOperationDto> operations, double timezoneOffset)
private Stream MakeExcelFileStream(IEnumerable<WellOperationDto> operations)
{
using Stream ecxelTemplateStream = wellOperationImportTemplateService.GetExcelTemplateStream();
using var workbook = new XLWorkbook(ecxelTemplateStream, XLEventTracking.Disabled);
AddOperationsToWorkbook(workbook, operations, timezoneOffset);
AddOperationsToWorkbook(workbook, operations);
var memoryStream = new MemoryStream();
workbook.SaveAs(memoryStream, new SaveOptions { });
@ -54,14 +51,14 @@ public class WellOperationExportService : IWellOperationExportService
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);
if (planOperations.Any())
{
var sheetPlan = workbook.Worksheets.FirstOrDefault(ws => ws.Name == DefaultTemplateInfo.SheetNamePlan);
if (sheetPlan is not null)
AddOperationsToSheet(sheetPlan, planOperations, timezoneOffset);
AddOperationsToSheet(sheetPlan, planOperations);
}
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);
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();
@ -83,19 +80,19 @@ public class WellOperationExportService : IWellOperationExportService
for (int i = 0; i < operationsToArray.Length; i++)
{
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,
IEnumerable<WellOperationCategoryDto> categories, double timezoneOffset)
IEnumerable<WellOperationCategoryDto> categories)
{
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.ColumnCategoryInfo).Value = operation.CategoryInfo;
row.Cell(DefaultTemplateInfo.ColumnDepthStart).Value = operation.DepthStart;
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.ColumnComment).Value = operation.Comment;
}

View File

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

View File

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

View File

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

View File

@ -32,8 +32,6 @@ namespace AsbCloudInfrastructure
backgroundWorker.Add<WorkToDeleteOldReports>(TimeSpan.FromDays(1));
backgroundWorker.Add<WellInfoService.WorkWellInfoUpdate>(TimeSpan.FromMinutes(30));
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(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.Services;
using AsbCloudApp.Services.ProcessMaps.WellDrilling;
using AsbCloudApp.Services.Subsystems;
using AsbCloudInfrastructure.Services.DailyReport;
using NSubstitute;
using Xunit;
@ -29,9 +28,7 @@ public class DailyReportServiceTest
private const int idDailyReport = 1;
private const int idUser = 3;
private const int idWell = 2;
private readonly DateTime dateDailyReport = new DateOnly(2023, 10, 26).ToDateTime(TimeOnly.MinValue);
private readonly SubsystemBlockDto fakeSubsystemBlock = new()
{
IdUser = idUser,
@ -191,7 +188,7 @@ public class DailyReportServiceTest
}
};
private readonly SubsystemStatDto fakeStatSubsystemOperationTime = new()
private readonly SubsystemStatDto fakeSubsystemsStat = new()
{
SubsystemName = "АПД",
SumDepthInterval = 250,
@ -199,12 +196,17 @@ public class DailyReportServiceTest
KUsage = 30
};
private readonly SimpleTimezoneDto fakeWellTimezone = new()
{
Hours = 5,
};
private readonly IWellService wellServiceMock = Substitute.For<IWellService>();
private readonly ITrajectoryNnbRepository trajectoryFactNnbRepositoryMock = Substitute.For<ITrajectoryNnbRepository>();
private readonly IDailyReportRepository dailyReportRepositoryMock = Substitute.For<IDailyReportRepository>();
private readonly IScheduleRepository scheduleRepositoryMock = Substitute.For<IScheduleRepository>();
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 IDetectedOperationService detectedOperationServiceMock = Substitute.For<IDetectedOperationService>();
@ -220,7 +222,7 @@ public class DailyReportServiceTest
{
Id = idDailyReport,
IdWell = idWell,
Date = dateDailyReport,
Date = new(2023, 10, 26),
DateLastUpdate = null
};
@ -245,14 +247,14 @@ public class DailyReportServiceTest
dailyReportRepositoryMock,
scheduleRepositoryMock,
wellOperationRepositoryMock,
subsystemOperationTimeServiceMock,
subsystemServiceMock,
processMapReportWellDrillingServiceMock,
detectedOperationServiceMock);
dailyReportRepositoryMock.InsertAsync(Arg.Any<DailyReportDto>(), Arg.Any<CancellationToken>())
.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);
dailyReportRepositoryMock.UpdateAsync(Arg.Any<DailyReportDto>(), Arg.Any<CancellationToken>())
@ -276,21 +278,24 @@ public class DailyReportServiceTest
detectedOperationServiceMock.GetAsync(Arg.Any<DetectedOperationRequest>(), Arg.Any<CancellationToken>())
.ReturnsForAnyArgs(fakeWellOperationSlipsTime);
subsystemOperationTimeServiceMock.GetStatAsync(Arg.Any<SubsystemOperationTimeRequest>(), Arg.Any<CancellationToken>())
.ReturnsForAnyArgs(new[] { fakeStatSubsystemOperationTime });
subsystemServiceMock.GetStatAsync(Arg.Any<SubsystemRequest>(), Arg.Any<CancellationToken>())
.ReturnsForAnyArgs(new[] { fakeSubsystemsStat });
scheduleRepositoryMock.GetAsync(idWell, dateDailyReport, Arg.Any<CancellationToken>())
scheduleRepositoryMock.GetAsync(Arg.Any<int>(), Arg.Any<DateTime>(), Arg.Any<CancellationToken>())
.ReturnsForAnyArgs(new[] { fakeShedule });
processMapReportWellDrillingServiceMock.GetAsync(idWell, Arg.Any<CancellationToken>())
processMapReportWellDrillingServiceMock.GetAsync(Arg.Any<int>(), Arg.Any<CancellationToken>())
.ReturnsForAnyArgs(new[] { fakeProcessMapReportWellDrilling });
wellServiceMock.GetTimezone(Arg.Any<int>())
.ReturnsForAnyArgs(fakeWellTimezone);
}
[Fact]
public async Task UpdateOrInsertAsync_ShouldReturn_UpdatedSubsystemBlock()
{
//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.NotNull(fakeSubsystemBlock.LastUpdateDate);
@ -301,14 +306,13 @@ public class DailyReportServiceTest
}
[Theory]
[InlineData("2090.01.01")]
[InlineData("2000.01.01")]
public async Task UpdateOrInsertAsync_ShouldReturn_UnableToUpdateDailyReport(DateTime dateDaileReport)
[MemberData(nameof(DateDailyReport))]
public async Task UpdateOrInsertAsync_ShouldReturn_UnableToUpdateDailyReport(DateOnly dateDailyReport)
{
//act
var result = await Assert.ThrowsAsync<ArgumentInvalidException>(() => dailyReportService.UpdateOrInsertAsync(
idWell,
dateDaileReport,
dateDailyReport,
idUser,
fakeSignBlock,
CancellationToken.None));
@ -321,7 +325,7 @@ public class DailyReportServiceTest
public async Task UpdateOrInsertAsync_ShouldReturn_UpdatedSignBlock()
{
//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.NotNull(fakeSignBlock.LastUpdateDate);
@ -335,7 +339,7 @@ public class DailyReportServiceTest
public async Task UpdateOrInsertAsync_ShouldReturn_UpdatedTimeBalanceBlock()
{
//act
var result = await dailyReportService.UpdateOrInsertAsync(idWell, dateDailyReport, idUser, fakeTimeBalanceBlock,
var result = await dailyReportService.UpdateOrInsertAsync(idWell, fakeDailyReport.Date, idUser, fakeTimeBalanceBlock,
CancellationToken.None);
//assert
@ -347,13 +351,12 @@ public class DailyReportServiceTest
}
[Theory]
[InlineData("2090.01.01")]
[InlineData("2000.01.01")]
public async Task GetAsync_ShouldReturn_UnableToGetDailyReport(DateTime dateDaileReport)
[MemberData(nameof(DateDailyReport))]
public async Task GetAsync_ShouldReturn_UnableToGetDailyReport(DateOnly dateDailyReport)
{
//act
var result = await Assert.ThrowsAsync<ArgumentInvalidException>(() => dailyReportService.GetAsync(idWell,
dateDaileReport,
dateDailyReport,
CancellationToken.None));
//assert
@ -364,7 +367,7 @@ public class DailyReportServiceTest
public async Task GetAsync_ShouldReturn_AddedWellInfo()
{
//act
var result = await dailyReportService.GetAsync(idWell, dateDailyReport, CancellationToken.None);
var result = await dailyReportService.GetAsync(idWell, fakeDailyReport.Date, CancellationToken.None);
//assert
Assert.Equal(result.IdWell, fakeWell.Id);
@ -382,7 +385,7 @@ public class DailyReportServiceTest
public async Task GetAsync_ShouldReturn_AddedTrajectoryBlock()
{
//act
var result = await dailyReportService.GetAsync(idWell, dateDailyReport, CancellationToken.None);
var result = await dailyReportService.GetAsync(idWell, fakeDailyReport.Date, CancellationToken.None);
//assert
Assert.Equal(fakeLastFactTrajectory.WellboreDepth, result.TrajectoryBlock.WellboreDepth);
@ -395,7 +398,7 @@ public class DailyReportServiceTest
public async Task GetAsync_ShouldReturn_AddedFactWellOperationBlock()
{
//act
var result = await dailyReportService.GetAsync(idWell, dateDailyReport, CancellationToken.None);
var result = await dailyReportService.GetAsync(idWell, fakeDailyReport.Date, CancellationToken.None);
//assert
Assert.Equal(16, result.FactWellOperationBlock.SectionDrillingHours);
@ -411,7 +414,7 @@ public class DailyReportServiceTest
public async Task GetAsync_ShouldReturn_AddedScheduleBlock()
{
//act
var result = await dailyReportService.GetAsync(idWell, dateDailyReport, CancellationToken.None);
var result = await dailyReportService.GetAsync(idWell, fakeDailyReport.Date, CancellationToken.None);
//assert
Assert.Single(result.ScheduleBlock);
@ -432,7 +435,7 @@ public class DailyReportServiceTest
fakeDailyReport.TimeBalanceBlock = fakeTimeBalanceBlock;
//act
var result = await dailyReportService.GetAsync(idWell, dateDailyReport, CancellationToken.None);
var result = await dailyReportService.GetAsync(idWell, fakeDailyReport.Date, CancellationToken.None);
//assert
Assert.NotNull(result.TimeBalanceBlock);
@ -447,7 +450,7 @@ public class DailyReportServiceTest
public async Task GetAsync_ShouldReturn_AddedProcessMapWellDrillingBlock()
{
//act
var result = await dailyReportService.GetAsync(idWell, dateDailyReport, CancellationToken.None);
var result = await dailyReportService.GetAsync(idWell, fakeDailyReport.Date, CancellationToken.None);
//assert
Assert.Single(result.ProcessMapWellDrillingBlock);
@ -468,7 +471,7 @@ public class DailyReportServiceTest
fakeDailyReport.SubsystemBlock = fakeSubsystemBlock;
//act
var result = await dailyReportService.GetAsync(idDailyReport, dateDailyReport, CancellationToken.None);
var result = await dailyReportService.GetAsync(idDailyReport, fakeDailyReport.Date, CancellationToken.None);
//assert
Assert.NotNull(result.SubsystemBlock);
@ -527,6 +530,18 @@ public class DailyReportServiceTest
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()
{
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);
var dailyReport = await dailyReportExportService.ExportAsync(idWell,
dateDailyReport.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc), cancellationToken);
var dailyReport = await dailyReportExportService.ExportAsync(idWell, dateDailyReport, cancellationToken);
return File(dailyReport.File, "application/octet-stream", dailyReport.FileName);
}
@ -187,8 +186,7 @@ public class DailyReportController : ControllerBase
{
await AssertUserAccessToWell(idWell, cancellationToken);
var id = await dailyReportService.UpdateOrInsertAsync(idWell, dateDailyReport.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc), IdUser,
block, cancellationToken);
var id = await dailyReportService.UpdateOrInsertAsync(idWell, dateDailyReport, IdUser, block, cancellationToken);
return Ok(id);
}

View File

@ -152,10 +152,10 @@ public abstract class ProcessMapBaseController<T> : ControllerBase
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpGet]
[ProducesResponseType(typeof(ValidationResultDto<>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[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);

View File

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

View File

@ -45,7 +45,6 @@ namespace AsbCloudWebApi.Controllers.SAUB
/// <param name = "token" >Токен завершения задачи</param>
/// <returns></returns>
[HttpGet("{idWell}")]
[Permission]
public async Task<ActionResult<IEnumerable<WitsRecordDto>>> GetDataAsync([Required] int idWell,
[FromQuery] GtrWithGetDataRequest request,
CancellationToken token)
@ -75,7 +74,6 @@ namespace AsbCloudWebApi.Controllers.SAUB
/// <param name="token"></param>
/// <returns></returns>
[HttpGet("{idWell}/{idRecord}")]
[Permission]
public async Task<ActionResult<IEnumerable<WitsItemRecordDto>>> GetLastDataByRecordIdAsync(int idWell, int idRecord, CancellationToken token)
{
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>
/// Импорт операций из excel (xlsx) файла. ГПНХ (Хантос)
/// </summary>
/// <param name="idWell">id скважины</param>
/// <param name="idWell">Id скважины</param>
/// <param name="options">Параметры парсинга</param>
/// <param name="files">Коллекция из одного файла xlsx</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
@ -412,14 +413,12 @@ namespace AsbCloudWebApi.Controllers
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
[Permission]
public Task<IActionResult> ImportPlanGazpromKhantosExcelFileAsync(int idWell,
[FromQuery] WellOperationImportGazpromKhantosOptionsDto options,
[FromForm] IFormFileCollection files,
CancellationToken cancellationToken)
{
var options = new WellOperationImportGazpromKhantosOptionsDto
{
IdType = WellOperation.IdOperationTypePlan
};
options.IdType = WellOperation.IdOperationTypePlan;
return ImportExcelFileAsync(idWell, files, options,
(stream, _) => wellOperationGazpromKhantosExcelParser.Parse(stream, options),
null,

View File

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