Merge pull request '#25858582 Изменение алгоритма расчёта наработки АПД' (#167) from feature/subsystems into dev

Reviewed-on: http://test.digitaldrilling.ru:8080/DDrilling/AsbCloudServer/pulls/167
This commit is contained in:
Никита Фролов 2023-12-19 16:02:26 +05:00
commit 5af0a0990b
43 changed files with 18898 additions and 1665 deletions

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

@ -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,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace AsbCloudApp.Requests
{
@ -14,31 +15,37 @@ namespace AsbCloudApp.Requests
/// </summary>
[Required]
public int IdWell { get; set; }
/// <summary>
/// Список id телеметрий
/// пустой список - нет фильтрации
/// </summary>
public IEnumerable<int> IdsTelemetries { get; set; } = Enumerable.Empty<int>();
/// <summary>
/// категории операций
/// </summary>
public IEnumerable<int>? IdsCategories { get; set; }
public IEnumerable<int> IdsCategories { get; set; } = Enumerable.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>
/// Фильтр по пользователю панели

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

@ -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

@ -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: "АКБ");
}
}
}

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
@ -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

@ -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

@ -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

@ -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

@ -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>();

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,7 +46,7 @@ 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;
}
@ -264,8 +263,8 @@ public class DailyReportService : IDailyReportService
{
IdsCategories = new[] { idWellOperationSlipsTime },
IdWell = dailyReport.IdWell,
GtDate = dailyReport.Date,
LtDate = dailyReport.Date.AddHours(24)
GeDateStart = dailyReport.Date,
LeDateEnd = dailyReport.Date.AddHours(24)
}, cancellationToken))?.Stats.Sum(s => s.Count);
dailyReport.TimeBalanceBlock.WellDepth.Fact = factWellOperations
@ -311,24 +310,24 @@ 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 subsystemsStatPerDay = await subsystemService.GetStatAsync(new SubsystemRequest
{
IdWell = dailyReport.IdWell,
GtDate = dailyReport.Date,
LtDate = dailyReport.Date.AddHours(24)
GeDate = dailyReport.Date,
LeDate = dailyReport.Date.AddHours(24)
}, 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())

View File

@ -8,6 +8,7 @@ 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;
@ -157,9 +158,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,20 +181,23 @@ 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);

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;
for (var i = begin; i < end; i += 2)
{
var mode = telemetry[i].Mode;
if (extraData.TryGetValue(DetectorDrilling.ExtraDataKeyHasOscillation, out var hasOscillation)
&& hasOscillation is true)
enabledSubsystems |= (int)EnabledSubsystemsFlags.AutoOscillation;
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

@ -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 readonly ITelemetryDataSaubService telemetryDataSaubService;
private IDictionary<int, SubsystemDto> subsystems = new Dictionary<int, SubsystemDto>();
public SubsystemService(ICrudRepository<SubsystemDto> subsystemRepository,
IWellService wellService,
IDetectedOperationService detectedOperationService,
ITelemetryDataSaubService telemetryDataSaubService)
{
this.wellService = wellService;
this.subsystemRepository = subsystemRepository;
this.detectedOperationService = detectedOperationService;
this.telemetryDataSaubService = telemetryDataSaubService;
}
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");
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

@ -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

@ -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

@ -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;
@ -191,7 +190,7 @@ public class DailyReportServiceTest
}
};
private readonly SubsystemStatDto fakeStatSubsystemOperationTime = new()
private readonly SubsystemStatDto fakeSubsystemsStat = new()
{
SubsystemName = "АПД",
SumDepthInterval = 250,
@ -204,7 +203,7 @@ public class DailyReportServiceTest
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>();
@ -245,7 +244,7 @@ public class DailyReportServiceTest
dailyReportRepositoryMock,
scheduleRepositoryMock,
wellOperationRepositoryMock,
subsystemOperationTimeServiceMock,
subsystemServiceMock,
processMapReportWellDrillingServiceMock,
detectedOperationServiceMock);
@ -276,8 +275,8 @@ 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>())
.ReturnsForAnyArgs(new[] { fakeShedule });

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;
}
}
}