Merge branch 'dev' into feature/sections

This commit is contained in:
Степанов Дмитрий 2023-12-06 17:55:43 +05:00
commit 521e36834e
52 changed files with 19435 additions and 612 deletions

View File

@ -6,7 +6,7 @@ namespace System.Collections.Generic
/// Цикличный массив /// Цикличный массив
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
public class CyclycArray<T> : IEnumerable<T> public class CyclicArray<T> : IEnumerable<T>
{ {
readonly T[] array; readonly T[] array;
int used, current = -1; int used, current = -1;
@ -15,7 +15,7 @@ namespace System.Collections.Generic
/// constructor /// constructor
/// </summary> /// </summary>
/// <param name="capacity"></param> /// <param name="capacity"></param>
public CyclycArray(int capacity) public CyclicArray(int capacity)
{ {
array = new T[capacity]; array = new T[capacity];
} }

View File

@ -94,5 +94,5 @@ public class ProcessMapReportWellDrillingDto
/// <summary> /// <summary>
/// Механическая скорость, м/ч /// Механическая скорость, м/ч
/// </summary> /// </summary>
public PlanFactDto<double?> Rop { get; set; } public PlanFactDto<double?> Rop { get; set; } = new();
} }

View File

@ -35,14 +35,18 @@ public class SectionByOperationsDto
public DateTimeOffset DateStart { get; set; } public DateTimeOffset DateStart { get; set; }
/// <summary> /// <summary>
/// Глубина после завершения последней операции операции в секции, м /// Глубина после завершения последней операции в секции, м
/// </summary> /// </summary>
[Range(0, 50_000)] [Range(0, 50_000)]
public double DepthEnd { get; set; } public double DepthEnd { get; set; }
/// <summary> /// <summary>
/// Дата после завершения последней операции операции в секции /// Дата после завершения последней операции в секции
/// </summary> /// </summary>
public DateTimeOffset DateEnd { get; set; } public DateTimeOffset DateEnd { get; set; }
public string Caption { get; set; }
/// <summary>
/// Название
/// </summary>
public string Caption { get; set; } = string.Empty;
} }

View File

@ -20,7 +20,7 @@ namespace AsbCloudApp.Repositories
void AddRange(int idTelemetry, IEnumerable<TDto> range); void AddRange(int idTelemetry, IEnumerable<TDto> range);
/// <summary> /// <summary>
/// вернуть последнюю записть /// вернуть последнюю запись
/// </summary> /// </summary>
/// <param name="idTelemetry"></param> /// <param name="idTelemetry"></param>
/// <returns></returns> /// <returns></returns>
@ -49,7 +49,7 @@ namespace AsbCloudApp.Repositories
/// </summary> /// </summary>
/// <param name="idTelemetry"></param> /// <param name="idTelemetry"></param>
/// <returns></returns> /// <returns></returns>
DatesRangeDto? GetOrDefaultCachedaDateRange(int idTelemetry); DatesRangeDto? GetOrDefaultCachedDateRange(int idTelemetry);
/// <summary> /// <summary>
/// Получить диапазон дат телеметрии. /// Получить диапазон дат телеметрии.

View File

@ -18,7 +18,7 @@ namespace AsbCloudApp.Services
/// <param name="dtos"></param> /// <param name="dtos"></param>
/// <param name="token"></param> /// <param name="token"></param>
/// <returns></returns> /// <returns></returns>
Task<int> UpdateRangeAsync(int idWell, IEnumerable<WellFinalDocumentInputDto>? dtos, CancellationToken token); Task<int> UpdateRangeAsync(int idWell, IEnumerable<WellFinalDocumentInputDto> dtos, CancellationToken token);
/// <summary> /// <summary>
/// Получение истории файлов /// Получение истории файлов

View File

@ -12,14 +12,13 @@ using System.Threading.Tasks;
namespace AsbCloudDb namespace AsbCloudDb
{ {
public static class EFExtentions public static class EFExtensions
{ {
private static readonly System.Text.Json.JsonSerializerOptions jsonSerializerOptions = new() private static readonly JsonSerializerOptions jsonSerializerOptions = new()
{ {
AllowTrailingCommas = true, AllowTrailingCommas = true,
WriteIndented = true, WriteIndented = true,
NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString | NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.AllowNamedFloatingPointLiterals,
System.Text.Json.Serialization.JsonNumberHandling.AllowNamedFloatingPointLiterals,
}; };
public static Microsoft.EntityFrameworkCore.Metadata.Builders.PropertyBuilder<TProperty> HasJsonConversion<TProperty>( public static Microsoft.EntityFrameworkCore.Metadata.Builders.PropertyBuilder<TProperty> HasJsonConversion<TProperty>(
@ -28,11 +27,11 @@ namespace AsbCloudDb
public static Microsoft.EntityFrameworkCore.Metadata.Builders.PropertyBuilder<TProperty> HasJsonConversion<TProperty>( public static Microsoft.EntityFrameworkCore.Metadata.Builders.PropertyBuilder<TProperty> HasJsonConversion<TProperty>(
this Microsoft.EntityFrameworkCore.Metadata.Builders.PropertyBuilder<TProperty> builder, this Microsoft.EntityFrameworkCore.Metadata.Builders.PropertyBuilder<TProperty> builder,
System.Text.Json.JsonSerializerOptions jsonSerializerOptions) JsonSerializerOptions jsonSerializerOptions)
{ {
builder.HasConversion( builder.HasConversion(
s => System.Text.Json.JsonSerializer.Serialize(s, jsonSerializerOptions), s => JsonSerializer.Serialize(s, jsonSerializerOptions),
s => System.Text.Json.JsonSerializer.Deserialize<TProperty>(s, jsonSerializerOptions)!); s => JsonSerializer.Deserialize<TProperty>(s, jsonSerializerOptions)!);
ValueComparer<TProperty> valueComparer = new ( ValueComparer<TProperty> valueComparer = new (
(a,b) => (a,b) =>
@ -102,7 +101,7 @@ namespace AsbCloudDb
return factory.Columns; return factory.Columns;
} }
public static Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry<T> Upsert<T>(this DbSet<T> dbSet, T value) public static EntityEntry<T> Upsert<T>(this DbSet<T> dbSet, T value)
where T : class where T : class
{ {
return dbSet.Contains(value) return dbSet.Contains(value)

View File

@ -5,9 +5,9 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
namespace AsbCloudDb namespace AsbCloudDb
{ {
public static class EFExtentionsInnitialization public static class EFExtensionsInitialization
{ {
public static void EnshureCreatedAndMigrated(this DatabaseFacade db) public static void EnsureCreatedAndMigrated(this DatabaseFacade db)
{ {
db.SetCommandTimeout(TimeSpan.FromMinutes(5)); db.SetCommandTimeout(TimeSpan.FromMinutes(5));
if (db.EnsureCreated()) if (db.EnsureCreated())

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,35 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class Daily_report_lost_migration : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DeleteData(
table: "t_relation_user_role_permission",
keyColumns: new[] { "id_permission", "id_user_role" },
keyValues: new object[] { 529, 1 });
migrationBuilder.DeleteData(
table: "t_permission",
keyColumn: "id",
keyValue: 529);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.InsertData(
table: "t_permission",
columns: new[] { "id", "description", "name" },
values: new object[] { 529, "Разрешение редактировать фактические траектории", "FactTrajectory.edit" });
migrationBuilder.InsertData(
table: "t_relation_user_role_permission",
columns: new[] { "id_permission", "id_user_role" },
values: new object[] { 529, 1 });
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,72 @@
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class DetectedOperation_add_subsystems_and_extraData : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DeleteData(
table: "t_relation_user_role_permission",
keyColumns: new[] { "id_permission", "id_user_role" },
keyValues: new object[] { 529, 1 });
migrationBuilder.DeleteData(
table: "t_permission",
keyColumn: "id",
keyValue: 529);
migrationBuilder.DropColumn(
name: "id_reason_of_end",
table: "t_detected_operation");
migrationBuilder.AddColumn<int>(
name: "enabled_subsystems",
table: "t_detected_operation",
type: "integer",
nullable: false,
defaultValue: 0,
comment: "флаги аключенных подсистем");
migrationBuilder.AddColumn<IDictionary<string, object>>(
name: "extra_data",
table: "t_detected_operation",
type: "jsonb",
nullable: false,
defaultValue: new { },
comment: "доп. инфо по операции");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "enabled_subsystems",
table: "t_detected_operation");
migrationBuilder.DropColumn(
name: "extra_data",
table: "t_detected_operation");
migrationBuilder.AddColumn<int>(
name: "id_reason_of_end",
table: "t_detected_operation",
type: "integer",
nullable: false,
defaultValue: 0,
comment: "Код признака окончания операции");
migrationBuilder.InsertData(
table: "t_permission",
columns: new[] { "id", "description", "name" },
values: new object[] { 529, "Разрешение редактировать фактические траектории", "FactTrajectory.edit" });
migrationBuilder.InsertData(
table: "t_relation_user_role_permission",
columns: new[] { "id_permission", "id_user_role" },
values: new object[] { 529, 1 });
}
}
}

View File

@ -1,5 +1,6 @@
// <auto-generated /> // <auto-generated />
using System; using System;
using System.Collections.Generic;
using System.Text.Json; using System.Text.Json;
using AsbCloudDb.Model; using AsbCloudDb.Model;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -19,7 +20,7 @@ namespace AsbCloudDb.Migrations
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.UseCollation("Russian_Russia.1251") .UseCollation("Russian_Russia.1251")
.HasAnnotation("ProductVersion", "6.0.22") .HasAnnotation("ProductVersion", "6.0.7")
.HasAnnotation("Relational:MaxIdentifierLength", 63); .HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "adminpack"); NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "adminpack");
@ -136,21 +137,21 @@ namespace AsbCloudDb.Migrations
{ {
Id = 1, Id = 1,
Caption = "Недропользователь", Caption = "Недропользователь",
IsContact = false, IsContact = true,
Order = 3 Order = 3
}, },
new new
{ {
Id = 2, Id = 2,
Caption = "Буровой подрядчик", Caption = "Буровой подрядчик",
IsContact = false, IsContact = true,
Order = 2 Order = 2
}, },
new new
{ {
Id = 3, Id = 3,
Caption = "Сервис автоматизации бурения", Caption = "Сервис автоматизации бурения",
IsContact = false, IsContact = true,
Order = 0 Order = 0
}, },
new new
@ -178,7 +179,7 @@ namespace AsbCloudDb.Migrations
{ {
Id = 7, Id = 7,
Caption = "Служба супервайзинга", Caption = "Служба супервайзинга",
IsContact = true, IsContact = false,
Order = 1 Order = 1
}, },
new new
@ -189,11 +190,18 @@ namespace AsbCloudDb.Migrations
Order = 7 Order = 7
}, },
new new
{
Id = 11,
Caption = "Дизельный сервис",
IsContact = false,
Order = 9
},
new
{ {
Id = 12, Id = 12,
Caption = "Сервис по обслуживанию верхних силовых приводов", Caption = "Сервис по обслуживанию верхних силовых приводов",
IsContact = true, IsContact = true,
Order = 7 Order = 8
}); });
}); });
@ -264,24 +272,46 @@ namespace AsbCloudDb.Migrations
modelBuilder.Entity("AsbCloudDb.Model.DailyReports.DailyReport", b => modelBuilder.Entity("AsbCloudDb.Model.DailyReports.DailyReport", b =>
{ {
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTime>("Date")
.HasColumnType("timestamp with time zone")
.HasColumnName("date")
.HasComment("Дата формирования отчёта");
b.Property<DateTime?>("DateLastUpdate")
.HasColumnType("timestamp with time zone")
.HasColumnName("date_last_update")
.HasComment("Дата последнего обновления");
b.Property<int>("IdWell") b.Property<int>("IdWell")
.HasColumnType("integer") .HasColumnType("integer")
.HasColumnName("id_well") .HasColumnName("id_well")
.HasComment("ID скважины"); .HasComment("ID скважины");
b.Property<DateOnly>("StartDate") b.Property<string>("SignBlock")
.HasColumnType("date")
.HasColumnName("start_date")
.HasComment("Дата отчёта");
b.Property<string>("Info")
.IsRequired()
.HasColumnType("jsonb") .HasColumnType("jsonb")
.HasColumnName("info") .HasColumnName("sign_block")
.HasComment("Список параметров для отчёта"); .HasComment("Подпись");
b.HasKey("IdWell", "StartDate") b.Property<string>("SubsystemBlock")
.HasName("t_id_well_date_start_pk"); .HasColumnType("jsonb")
.HasColumnName("subsystem_block")
.HasComment("Наработкой подсистем");
b.Property<string>("TimeBalanceBlock")
.HasColumnType("jsonb")
.HasColumnName("time_balance_block")
.HasComment("Баланс времени");
b.HasKey("Id");
b.HasIndex("IdWell", "Date")
.IsUnique();
b.ToTable("t_daily_report"); b.ToTable("t_daily_report");
@ -353,16 +383,22 @@ namespace AsbCloudDb.Migrations
.HasColumnName("depth_start") .HasColumnName("depth_start")
.HasComment("Глубина на начало операции, м"); .HasComment("Глубина на начало операции, м");
b.Property<int>("EnabledSubsystems")
.HasColumnType("integer")
.HasColumnName("enabled_subsystems")
.HasComment("флаги аключенных подсистем");
b.Property<IDictionary<string, object>>("ExtraData")
.IsRequired()
.HasColumnType("jsonb")
.HasColumnName("extra_data")
.HasComment("доп. инфо по операции");
b.Property<int>("IdCategory") b.Property<int>("IdCategory")
.HasColumnType("integer") .HasColumnType("integer")
.HasColumnName("id_category") .HasColumnName("id_category")
.HasComment("Id категории операции"); .HasComment("Id категории операции");
b.Property<int>("IdReasonOfEnd")
.HasColumnType("integer")
.HasColumnName("id_reason_of_end")
.HasComment("Код признака окончания операции");
b.Property<int>("IdTelemetry") b.Property<int>("IdTelemetry")
.HasColumnType("integer") .HasColumnType("integer")
.HasColumnName("id_telemetry"); .HasColumnName("id_telemetry");
@ -8147,7 +8183,7 @@ namespace AsbCloudDb.Migrations
b.Navigation("Well"); b.Navigation("Well");
}); });
modelBuilder.Entity("AsbCloudDb.Model.DailyReport.DailyReport", b => modelBuilder.Entity("AsbCloudDb.Model.DailyReports.DailyReport", b =>
{ {
b.HasOne("AsbCloudDb.Model.Well", "Well") b.HasOne("AsbCloudDb.Model.Well", "Well")
.WithMany() .WithMany()

View File

@ -351,6 +351,10 @@ namespace AsbCloudDb.Model
.HasJsonConversion(); .HasJsonConversion();
}); });
modelBuilder.Entity<DetectedOperation>(entity => entity
.Property(p=>p.ExtraData)
.HasJsonConversion());
modelBuilder.Entity<TelemetryDataSaubStat>(entity => modelBuilder.Entity<TelemetryDataSaubStat>(entity =>
{ {
entity.HasNoKey() entity.HasNoKey()

View File

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

View File

@ -42,7 +42,7 @@ namespace AsbCloudDb.Model
/// <summary> /// <summary>
/// КНБК /// КНБК
/// </summary> /// </summary>
public const int IdKnbk = 4000; public const int IdBha = 4000;
/// <summary> /// <summary>
/// Механическое. бурение /// Механическое. бурение
@ -246,7 +246,7 @@ namespace AsbCloudDb.Model
/// Виды работ /// Виды работ
/// </summary> /// </summary>
public static WellOperationCategory[] WorkTypes { get; } = new WellOperationCategory[]{ public static WellOperationCategory[] WorkTypes { get; } = new WellOperationCategory[]{
new () {Id = IdKnbk, IdParent = 3000, Name = "КНБК", KeyValueName = "dT", KeyValueUnits = "мин" }, new () {Id = IdBha, IdParent = 3000, Name = "КНБК", KeyValueName = "dT", KeyValueUnits = "мин" },
new () {Id = IdMechanicalDrilling, IdParent = 3000, Name = "Механическое. бурение", KeyValueName = "dT", KeyValueUnits = "м/ч" }, new () {Id = IdMechanicalDrilling, IdParent = 3000, Name = "Механическое. бурение", KeyValueName = "dT", KeyValueUnits = "м/ч" },
new () {Id = IdMeasurementStat, IdParent = 3000, Name = "Статический замер", KeyValueName = "dT", KeyValueUnits = "мин" }, new () {Id = IdMeasurementStat, IdParent = 3000, Name = "Статический замер", KeyValueName = "dT", KeyValueUnits = "мин" },
new () {Id = IdNormalizedWellDiameter, IdParent = 3000, Name = "Нормализация диаметра скважины", KeyValueName = "dT", KeyValueUnits = "мин" }, new () {Id = IdNormalizedWellDiameter, IdParent = 3000, Name = "Нормализация диаметра скважины", KeyValueName = "dT", KeyValueUnits = "мин" },

View File

@ -36,12 +36,12 @@ public class BackgroundWorker : BackgroundService
/// <summary> /// <summary>
/// последние 16 завершившиеся с ошибкой /// последние 16 завершившиеся с ошибкой
/// </summary> /// </summary>
public CyclycArray<Work> Felled { get; } = new(16); public CyclicArray<Work> Felled { get; } = new(16);
/// <summary> /// <summary>
/// последние 16 успешно завершенных /// последние 16 успешно завершенных
/// </summary> /// </summary>
public CyclycArray<Work> Done { get; } = new(16); public CyclicArray<Work> Done { get; } = new(16);
/// <summary> /// <summary>
/// Ошибка в главном цикле, никогда не должна появляться /// Ошибка в главном цикле, никогда не должна появляться

View File

@ -167,7 +167,6 @@ public class WorkSubsystemOscillationOperationTimeCalc : WorkSubsystemOperationT
.Where(d => d.IdTelemetry == idTelemetry) .Where(d => d.IdTelemetry == idTelemetry)
.Where(d => d.DateTime >= dateBegin) .Where(d => d.DateTime >= dateBegin)
.Where(d => d.DateTime <= dateEnd) .Where(d => d.DateTime <= dateEnd)
.Where(d => d.WellDepth != null)
.Where(d => d.WellDepth > 0) .Where(d => d.WellDepth > 0)
.GroupBy(d => Math.Ceiling(d.WellDepth * 10)) .GroupBy(d => Math.Ceiling(d.WellDepth * 10))
.Select(g => new .Select(g => new

View File

@ -111,7 +111,8 @@ public class WellOperationRepository : IWellOperationRepository
.ConfigureAwait(false); .ConfigureAwait(false);
if (lastFactOperation is not null) if (lastFactOperation is not null)
return DateTime.SpecifyKind(lastFactOperation.OperationPlan.DateStart.UtcDateTime + timeZoneOffset, DateTimeKind.Unspecified); return DateTime.SpecifyKind(lastFactOperation.OperationPlan!.DateStart.UtcDateTime + timeZoneOffset, DateTimeKind.Unspecified);
return null; return null;
} }

View File

@ -95,10 +95,8 @@ public class DailyReportService : IDailyReportService
public async Task<DailyReportDto> GetAsync(int idWell, DateTime dateDailyReport, CancellationToken cancellationToken) public async Task<DailyReportDto> GetAsync(int idWell, DateTime dateDailyReport, CancellationToken cancellationToken)
{ {
var well = await wellService.GetOrDefaultAsync(idWell, cancellationToken); var well = await wellService.GetOrDefaultAsync(idWell, cancellationToken)
?? throw new ArgumentNullException(nameof(idWell), $"Скважина с Id: {idWell} не найдена");
if (well is null)
throw new ArgumentNullException(nameof(idWell), $"Скважина с Id: {idWell} не найдена");
if (!await IsDateDailyReportInRangeAsync(idWell, dateDailyReport, cancellationToken)) if (!await IsDateDailyReportInRangeAsync(idWell, dateDailyReport, cancellationToken))
throw new ArgumentInvalidException(nameof(dateDailyReport), "Невозможно получить суточный отчёт"); throw new ArgumentInvalidException(nameof(dateDailyReport), "Невозможно получить суточный отчёт");

View File

@ -1,18 +1,16 @@
using System; using System;
namespace AsbCloudInfrastructure.Services.DetectOperations namespace AsbCloudInfrastructure.Services.DetectOperations;
{
public class DetectableTelemetry public class DetectableTelemetry
{ {
public DateTimeOffset DateTime { get; set; } public DateTimeOffset DateTime { get; set; }
public int? IdUser { get; set; } public int? IdUser { get; set; }
public int Mode { get; set; }
public float WellDepth { get; set; } public float WellDepth { get; set; }
public float Pressure { get; set; } public float Pressure { get; set; }
public float HookWeight { get; set; } public float HookWeight { get; set; }
public float BlockPosition { get; set; } public float BlockPosition { get; set; }
public float BitDepth { get; set; } public float BitDepth { get; set; }
public float RotorSpeed { get; set; } public float RotorSpeed { get; set; }
}
} }

View File

@ -9,12 +9,17 @@ using System.Reflection;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsbCloudInfrastructure.Services.DetectOperations.Detectors; using AsbCloudInfrastructure.Services.DetectOperations.Detectors;
using AsbCloudApp.Repositories;
namespace AsbCloudInfrastructure.Services.DetectOperations; namespace AsbCloudInfrastructure.Services.DetectOperations;
public class DetectedOperationExportService public class DetectedOperationExportService
{ {
private readonly DetectorAbstract[] detectors = { new DetectorDrilling(), new DetectorSlipsTime() }; private readonly DetectorAbstract[] detectors =
{
new DetectorDrilling(),
new DetectorSlipsTime()
};
private readonly IDictionary<int, string> domains = new Dictionary<int, string> private readonly IDictionary<int, string> domains = new Dictionary<int, string>
{ {
@ -38,12 +43,15 @@ public class DetectedOperationExportService
private const int columnDeltaDepth = 7; private const int columnDeltaDepth = 7;
private const int columnDepth = 8; private const int columnDepth = 8;
private const int columnIdReasonOfEnd = 9; private const int columnIdReasonOfEnd = 9;
private const int columnComment = 10;
private readonly IAsbCloudDbContext dbContext; private readonly IAsbCloudDbContext dbContext;
private readonly IWellOperationRepository wellOperationRepository;
public DetectedOperationExportService(IAsbCloudDbContext dbContext) public DetectedOperationExportService(IAsbCloudDbContext dbContext, IWellOperationRepository wellOperationRepository)
{ {
this.dbContext = dbContext; this.dbContext = dbContext;
this.wellOperationRepository = wellOperationRepository;
} }
public async Task<Stream> ExportAsync(int idWell, int idDomain, CancellationToken cancellationToken) public async Task<Stream> ExportAsync(int idWell, int idDomain, CancellationToken cancellationToken)
@ -59,20 +67,19 @@ public class DetectedOperationExportService
if (!well.IdTelemetry.HasValue) if (!well.IdTelemetry.HasValue)
throw new ArgumentNullException(nameof(well)); throw new ArgumentNullException(nameof(well));
var operations = await DetectOperationsAsync(well.IdTelemetry.Value, new DateTime(2023, 10, 14) var operations = await DetectOperationsAsync(well.IdTelemetry.Value, DateTime.UnixEpoch, cancellationToken);
.ToUtcDateTimeOffset(well.Timezone.Hours), cancellationToken);
return await GenerateExcelFileStreamAsync(well, idDomain, operations, cancellationToken); return await GenerateExcelFileStreamAsync(well, idDomain, operations, cancellationToken);
} }
private async Task<Stream> GenerateExcelFileStreamAsync(Well well, int idDomain, IEnumerable<DetectedOperation> detectedOperations, private async Task<Stream> GenerateExcelFileStreamAsync(Well well, int idDomain, IEnumerable<OperationDetectorResult> operationDetectorResults,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
using var excelTemplateStream = await GetExcelTemplateStreamAsync(cancellationToken); using var excelTemplateStream = await GetExcelTemplateStreamAsync(cancellationToken);
using var workbook = new XLWorkbook(excelTemplateStream, XLEventTracking.Disabled); using var workbook = new XLWorkbook(excelTemplateStream, XLEventTracking.Disabled);
await AddToWorkbookAsync(workbook, well, idDomain, detectedOperations, cancellationToken); await AddToWorkbookAsync(workbook, well, idDomain, operationDetectorResults, cancellationToken);
MemoryStream memoryStream = new MemoryStream(); MemoryStream memoryStream = new MemoryStream();
workbook.SaveAs(memoryStream, new SaveOptions { }); workbook.SaveAs(memoryStream, new SaveOptions { });
@ -80,22 +87,23 @@ public class DetectedOperationExportService
return memoryStream; return memoryStream;
} }
private async Task AddToWorkbookAsync(XLWorkbook workbook, Well well, int idDomain, IEnumerable<DetectedOperation> detectedOperations, private async Task AddToWorkbookAsync(XLWorkbook workbook, Well well, int idDomain, IEnumerable<OperationDetectorResult> operationDetectorResults,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
const string sheetName = "Операции"; const string sheetName = "Операции";
if (!detectedOperations.Any()) if (!operationDetectorResults.Any())
return; return;
var sheet = workbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetName) var sheet = workbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetName)
?? throw new FileFormatException($"Книга excel не содержит листа {sheetName}."); ?? throw new FileFormatException($"Книга excel не содержит листа {sheetName}.");
await AddToSheetAsync(sheet, well, idDomain, detectedOperations.OrderBy(x => x.DateStart).ThenBy(x => x.DepthStart).ToArray(), await AddToSheetAsync(sheet, well, idDomain, operationDetectorResults
.OrderBy(x => x.Operation.DateStart).ThenBy(x => x.Operation.DepthStart).ToArray(),
cancellationToken); cancellationToken);
} }
private async Task AddToSheetAsync(IXLWorksheet sheet, Well well, int idDomain, IList<DetectedOperation> detectedOperations, private async Task AddToSheetAsync(IXLWorksheet sheet, Well well, int idDomain, IList<OperationDetectorResult> operationDetectorResults,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var wellOperationCategories = await dbContext.WellOperationCategories.ToListAsync(cancellationToken); var wellOperationCategories = await dbContext.WellOperationCategories.ToListAsync(cancellationToken);
@ -103,39 +111,69 @@ public class DetectedOperationExportService
sheet.Cell(cellDepositName).Value = well.Cluster.Deposit.Caption; sheet.Cell(cellDepositName).Value = well.Cluster.Deposit.Caption;
sheet.Cell(cellClusterName).Value = well.Cluster.Caption; sheet.Cell(cellClusterName).Value = well.Cluster.Caption;
sheet.Cell(cellWellName).Value = well.Caption; sheet.Cell(cellWellName).Value = well.Caption;
sheet.Cell(cellDeltaDate).Value = detectedOperations.Max(o => o.DateEnd) - detectedOperations.Min(o => o.DateStart); sheet.Cell(cellDeltaDate).Value = operationDetectorResults.Max(o => o.Operation.DateEnd) - operationDetectorResults.Min(o => o.Operation.DateStart);
var timeZoneWell = TimeSpan.FromHours(well.Timezone.Hours); var detectedOperations = operationDetectorResults.Select(o => o.Operation).ToArray();
for (int i = 0; i < detectedOperations.Count; i++) for (int i = 0; i < operationDetectorResults.Count; i++)
{ {
var dateStart = detectedOperations[i].DateStart.ToOffset(timeZoneWell); var current = detectedOperations[i];
var dateEnd = detectedOperations[i].DateEnd.ToOffset(timeZoneWell); var dateStart = current.DateStart.ToRemoteDateTime(well.Timezone.Hours);
var dateEnd = current.DateEnd.ToRemoteDateTime(well.Timezone.Hours);
var row = sheet.Row(5 + i + headerRowsCount); var row = sheet.Row(5 + i + headerRowsCount);
row.Cell(columnOperationName).Value = detectedOperations[i].IdCategory == 12000 row.Cell(columnOperationName).Value = GetCategoryName(wellOperationCategories, current);
? "Бурение в слайде с осцилляцией"
: wellOperationCategories.Single(o => o.Id == detectedOperations[i].IdCategory).Name;
row.Cell(columnDateEnd).Value = dateEnd; row.Cell(columnDateEnd).Value = dateEnd;
row.Cell(columnDuration).Value = (dateEnd - dateStart).TotalMinutes; row.Cell(columnDuration).Value = (dateEnd - dateStart).TotalMinutes;
row.Cell(columnDepthStart).Value = detectedOperations[i].DepthStart; row.Cell(columnDepthStart).Value = current.DepthStart;
row.Cell(columnDepthEnd).Value = detectedOperations[i].DepthEnd; row.Cell(columnDepthEnd).Value = current.DepthEnd;
row.Cell(columnDepth).Value = detectedOperations[i].DepthEnd - detectedOperations[i].DepthStart; row.Cell(columnDepth).Value = current.DepthEnd - current.DepthStart;
row.Cell(columnIdReasonOfEnd).Value = detectedOperations[i].IdReasonOfEnd;
if (current.ExtraData.TryGetValue("IdReasonOfEnd", out object? idReasonOfEndObject)
&& idReasonOfEndObject is int idReasonOfEnd)
row.Cell(columnIdReasonOfEnd).Value = GetIdReasonOfEnd(idReasonOfEnd);
var link = var link =
$"{domains[idDomain]}/well/{well.Id}/telemetry/monitoring?end={Uri.EscapeDataString(dateStart.AddSeconds(3544).ToString("yyyy-MM-ddTHH:mm:ss.fff"))}&range=3600"; $"{domains[idDomain]}/well/{well.Id}/telemetry/monitoring?end={Uri.EscapeDataString(dateStart.AddSeconds(1800 * 0.9).ToString("yyyy-MM-ddTHH:mm:ss.fff"))}&range=1800";
row.Cell(columnDateStart).Value = dateStart; row.Cell(columnDateStart).Value = dateStart;
row.Cell(columnDateStart).SetHyperlink(new XLHyperlink(link)); row.Cell(columnDateStart).SetHyperlink(new XLHyperlink(link));
row.Cell(columnDeltaDepth).Value = i > 0 && i + 1 < detectedOperations.Count row.Cell(columnDeltaDepth).Value = i > 0 && i + 1 < detectedOperations.Length
? detectedOperations[i].DepthStart - detectedOperations[i - 1].DepthEnd ? current.DepthStart - detectedOperations[i - 1].DepthEnd
: 0; : 0;
row.Cell(columnComment).Value = CreateComment(operationDetectorResults[i]);
} }
} }
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)
return "Бурение в слайде с осцилляцией";
var category = wellOperationCategories.FirstOrDefault(o => o.Id == current.IdCategory);
if(category is not null)
return category.Name;
return $"Операция №{idCategory}";
}
private static string GetIdReasonOfEnd(int idReasonOfEnd)
=> idReasonOfEnd switch {
0 => "Не определена",
1 => "Не определено начало операции",
101 => "Разница глубин забоя и положением долота",
300 => "Низкое давление",
_ => idReasonOfEnd.ToString($"Причина № {idReasonOfEnd}"),
};
private async Task<Stream> GetExcelTemplateStreamAsync(CancellationToken cancellationToken) private async Task<Stream> GetExcelTemplateStreamAsync(CancellationToken cancellationToken)
{ {
string resourceName = Assembly.GetExecutingAssembly() string resourceName = Assembly.GetExecutingAssembly()
@ -152,7 +190,28 @@ public class DetectedOperationExportService
return memoryStream; return memoryStream;
} }
private async Task<IEnumerable<DetectedOperation>> DetectOperationsAsync(int idTelemetry, DateTimeOffset begin, private static string CreateComment(OperationDetectorResult operationDetectorResult)
{
var operation = operationDetectorResult.Operation;
switch (operation.IdCategory)
{
case WellOperationCategory.IdRotor:
case WellOperationCategory.IdSlide:
var comment = "";
if (operation.ExtraData.TryGetValue(DetectorDrilling.ExtraDataKeyAvgRotorSpeed, out object? oAvgRotorSpeed))
comment += $"Средняя скорость оборотов ротора: {oAvgRotorSpeed}\r\n";
if (operation.ExtraData.TryGetValue(DetectorDrilling.ExtraDataKeyDispersionOfNormalizedRotorSpeed, out object? oDispersionOfNormalizedRotorSpeed))
comment += $"Дисперсия нормированных оборотов ротора: {oDispersionOfNormalizedRotorSpeed}";
return comment;
default:
return string.Empty;
}
}
private async Task<IEnumerable<OperationDetectorResult>> DetectOperationsAsync(int idTelemetry, DateTimeOffset begin,
CancellationToken token) CancellationToken token)
{ {
var query = dbContext.TelemetryDataSaub var query = dbContext.TelemetryDataSaub
@ -173,7 +232,7 @@ public class DetectedOperationExportService
.OrderBy(d => d.DateTime); .OrderBy(d => d.DateTime);
var startDate = begin; var startDate = begin;
var detectedOperations = new List<DetectedOperation>(8); var detectedOperationResults = new List<OperationDetectorResult>(8);
DetectedOperation? lastDetectedOperation = null; DetectedOperation? lastDetectedOperation = null;
const int minOperationLength = 5; const int minOperationLength = 5;
const int maxDetectorsInterpolationFrameLength = 30; const int maxDetectorsInterpolationFrameLength = 30;
@ -191,26 +250,21 @@ public class DetectedOperationExportService
var isDetected = false; var isDetected = false;
var positionBegin = 0; var positionBegin = 0;
var positionEnd = data.Length - gap; var positionEnd = data.Length - gap;
var step = 10;
while (positionEnd > positionBegin) while (positionEnd > positionBegin)
{ {
step++;
foreach (var detector in detectors) foreach (var detector in detectors)
{ {
if (!detector.TryDetect(idTelemetry, data, positionBegin, positionEnd, lastDetectedOperation, out var result)) if (!detector.TryDetect(idTelemetry, data, positionBegin, positionEnd, lastDetectedOperation, out var result))
continue; continue;
detectedOperations.Add(result!.Operation); detectedOperationResults.Add(result!);
lastDetectedOperation = result.Operation; lastDetectedOperation = result!.Operation;
isDetected = true; isDetected = true;
step = 1;
positionBegin = result.TelemetryEnd; positionBegin = result.TelemetryEnd;
break; break;
} }
if (step > 20) positionBegin += 1;
step = 10;
positionBegin += step;
} }
if (isDetected) if (isDetected)
@ -219,6 +273,6 @@ public class DetectedOperationExportService
startDate = data[positionEnd].DateTime; startDate = data[positionEnd].DateTime;
} }
return detectedOperations; return detectedOperationResults;
} }
} }

View File

@ -201,7 +201,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
if (well?.IdTelemetry is null || well.Timezone is null) if (well?.IdTelemetry is null || well.Timezone is null)
return 0; return 0;
var query = BuildQuery(well, request); var query = BuildQueryBase(well, request);
if (query is null) if (query is null)
return 0; return 0;
@ -232,13 +232,10 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
}; };
} }
private IQueryable<DetectedOperation>? BuildQuery(WellDto well, DetectedOperationRequest request)
{
if (well?.IdTelemetry is null || well.Timezone is null)
return null;
private IQueryable<DetectedOperation> BuildQueryBase(WellDto well, DetectedOperationRequest request)
{
var query = db.Set<DetectedOperation>() var query = db.Set<DetectedOperation>()
.Include(o => o.OperationCategory)
.Where(o => o.IdTelemetry == well.IdTelemetry); .Where(o => o.IdTelemetry == well.IdTelemetry);
if (request is not null) if (request is not null)
@ -262,6 +259,14 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
query = query.Where(o => o.IdUsersAtStart == request.EqIdTelemetryUser); query = query.Where(o => o.IdUsersAtStart == request.EqIdTelemetryUser);
} }
return query;
}
private IQueryable<DetectedOperation> BuildQuery(WellDto well, DetectedOperationRequest request)
{
IQueryable<DetectedOperation> query = BuildQueryBase(well, request)
.Include(o => o.OperationCategory);
if (request?.SortFields?.Any() == true) if (request?.SortFields?.Any() == true)
{ {
query = query.SortBy(request.SortFields); query = query.SortBy(request.SortFields);

View File

@ -1,11 +1,11 @@
using AsbCloudDb.Model; using AsbCloudDb.Model;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
{ {
public abstract class DetectorAbstract
internal abstract class DetectorAbstract
{ {
private readonly int stepLength = 3; private readonly int stepLength = 3;
@ -35,9 +35,8 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
protected const int IdReasonOfEnd_Custom1 = 10_000; protected const int IdReasonOfEnd_Custom1 = 10_000;
public abstract Func<DetectableTelemetry[], int, int, int> GetIdOperation { get; } public bool TryDetect(int idTelemetry, DetectableTelemetry[] telemetry, int begin, int end, DetectedOperation? previousOperation,
out OperationDetectorResult? result)
public bool TryDetect(int idTelemetry, DetectableTelemetry[] telemetry, int begin, int end, DetectedOperation? previousOperation, out OperationDetectorResult? result)
{ {
// Проверка соответствия критерию начала операции // Проверка соответствия критерию начала операции
if (DetectBegin(telemetry, begin, previousOperation)) if (DetectBegin(telemetry, begin, previousOperation))
@ -53,83 +52,141 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
idReasonOfEnd = DetectEnd(telemetry, positionEnd, previousOperation); idReasonOfEnd = DetectEnd(telemetry, positionEnd, previousOperation);
if(idReasonOfEnd is IdReasonOfEnd_DeltaDepthIsHi or IdReasonOfEnd_PressureIsLo &&
!IsValidByWellDepthDoesNotChange(telemetry, begin, positionEnd))
break;
if (idReasonOfEnd != IdReasonOfEnd_NotDetected) if (idReasonOfEnd != IdReasonOfEnd_NotDetected)
break; break;
} }
result = null; var (Begin, End) = RefineEdges(telemetry, begin, positionEnd);
result = MakeOperation(idTelemetry, telemetry, begin, positionEnd, idReasonOfEnd);
return true; if (!IsValidTelemetryRange(telemetry, Begin, End))
} {
result = null; result = null;
return false; return false;
} }
result = MakeOperationDetectorResult(idTelemetry, telemetry, Begin, End, idReasonOfEnd);
return IsValidOperationDetectorResult(result);
}
result = null;
return false;
}
protected virtual bool IsValidTelemetryRange(DetectableTelemetry[] telemetry, int begin, int end)
=> end - begin > 1;
protected virtual (int Begin, int End) RefineEdges(DetectableTelemetry[] telemetry, int begin, int end)
=> (begin, end);
protected virtual bool IsValidOperationDetectorResult(OperationDetectorResult operationDetectorResult)
=> operationDetectorResult.Operation.DateEnd - operationDetectorResult.Operation.DateStart > TimeSpan.FromSeconds(3);
protected abstract bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation); protected abstract bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation);
protected virtual int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) protected virtual int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
=> DetectBegin(telemetry, position, previousOperation) => DetectBegin(telemetry, position, previousOperation)
? IdReasonOfEnd_NotDetected ? IdReasonOfEnd_NotDetected
: IdReasonOfEnd_NotDetectBegin; : IdReasonOfEnd_NotDetectBegin;
private OperationDetectorResult MakeOperation(int idTelemetry, DetectableTelemetry[] telemetry, int begin, int end, int idReasonOfEnd) private OperationDetectorResult MakeOperationDetectorResult(
int idTelemetry,
DetectableTelemetry[] telemetry,
int begin,
int end,
int idReasonOfEnd)
{ {
var pBegin = telemetry[begin]; var operation = MakeDetectedOperation(idTelemetry, telemetry, begin, end);
var pEnd = telemetry[end];
operation.ExtraData["IdReasonOfEnd"] = idReasonOfEnd;
var result = new OperationDetectorResult var result = new OperationDetectorResult
{ {
TelemetryBegin = begin, TelemetryBegin = begin,
TelemetryEnd = end, TelemetryEnd = end,
Operation = new DetectedOperation Operation = operation,
};
return result;
}
private DetectedOperation MakeDetectedOperation(int idTelemetry, DetectableTelemetry[] telemetry, int begin, int end)
{ {
var pBegin = telemetry[begin];
var pEnd = telemetry[end];
var (IdCategory, ExtraData) = GetSpecificInformation(telemetry, begin, end);
var operation = new DetectedOperation
{
IdCategory = IdCategory,
IdTelemetry = idTelemetry, IdTelemetry = idTelemetry,
IdCategory = GetIdOperation.Invoke(telemetry, begin, end),
IdUsersAtStart = pBegin.IdUser ?? -1, IdUsersAtStart = pBegin.IdUser ?? -1,
DateStart = pBegin.DateTime, DateStart = pBegin.DateTime,
DateEnd = pEnd.DateTime, DateEnd = pEnd.DateTime,
DepthStart = (double)pBegin.WellDepth, DepthStart = (double)pBegin.WellDepth,
DepthEnd = (double)pEnd.WellDepth, DepthEnd = (double)pEnd.WellDepth,
ExtraData = ExtraData,
Value = CalcValue(telemetry, begin, end), Value = CalcValue(telemetry, begin, end),
IdReasonOfEnd = idReasonOfEnd, EnabledSubsystems = DetectEnabledSubsystems(telemetry, begin, end)
},
}; };
return result;
return operation;
} }
protected abstract bool IsValid(DetectableTelemetry[] telemetry, int begin, int end); /// <summary>
/// Получение информации специфичной для конкретного детектора
/// IdCategory - одна из констант WellOperationCategory
/// ExtraData - дополнительная информация для отладки алгоритмов авто определения
/// </summary>
/// <returns></returns>
protected abstract (int IdCategory, IDictionary<string, object> ExtraData) GetSpecificInformation(DetectableTelemetry[] telemetry, int begin, int end);
/// <summary>
/// Расчет ключевого параметра операции
/// </summary>
/// <param name="telemetry"></param>
/// <param name="begin"></param>
/// <param name="end"></param>
/// <returns></returns>
protected abstract double CalcValue(DetectableTelemetry[] telemetry, int begin, int end); protected abstract double CalcValue(DetectableTelemetry[] telemetry, int begin, int end);
/// <summary> /// <summary>
/// Среднее арифметическое /// Определение включенных подсистем во время выполнения операции
/// </summary> /// </summary>
/// <param name="yGetter"></param>
/// <param name="telemetry"></param> /// <param name="telemetry"></param>
/// <param name="begin"></param> /// <param name="begin"></param>
/// <param name="fragmentLength"></param> /// <param name="end"></param>
/// <returns></returns> /// <returns></returns>
public static double CalcAvgAppr( private static int DetectEnabledSubsystems(DetectableTelemetry[] telemetry, int begin, int end)
Func<DetectableTelemetry, double> yGetter,
DetectableTelemetry[] telemetry,
int begin,
int fragmentLength)
{ {
var end = begin + fragmentLength; var enabledSubsystems = 0;
end = end < telemetry.Length
? end for (var i = begin; i < end; i += 2)
: telemetry.Length;
var subData = telemetry[begin..end].Select(yGetter);
if (end - begin > 10)
{ {
var ratio = (end - begin) / 5; var mode = telemetry[i].Mode;
subData = subData.Where((_,i) => i % ratio > 0);
if(mode == 1)
enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoRotor;
if (mode == 3)
enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoSlide;
if (mode == 2)
enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoConditionig;
if (mode == 4)
enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoSinking;
if (mode == 5)
enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoLifting;
if (mode == 6)
enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoLiftingWithConditionig;
if (mode == 10)
enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoBlocknig;
} }
var avg = subData.Average(); return enabledSubsystems;
return avg;
} }
/// <summary> /// <summary>
@ -210,6 +267,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
max = itemValue; max = itemValue;
sum += itemValue; sum += itemValue;
} }
return (min, max, sum, end - begin); return (min, max, sum, end - begin);
} }
@ -256,7 +314,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
var end = begin + count; var end = begin + count;
end = end < telemetry.Length ? end : telemetry.Length; end = end < telemetry.Length ? end : telemetry.Length;
for (var i = begin; i < end; i ++) for (var i = begin; i < end; i++)
{ {
var item = telemetry[i]; var item = telemetry[i];
var itemValue = getter(item); var itemValue = getter(item);
@ -264,9 +322,10 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
min = itemValue; min = itemValue;
if (max < itemValue) if (max < itemValue)
max = itemValue; max = itemValue;
if(max - min > deviation) if (max - min > deviation)
return true; return true;
} }
return false; return false;
} }
@ -290,8 +349,8 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
var max = double.MinValue; var max = double.MinValue;
var end = begin + count; var end = begin + count;
end = end < telemetry.Length ? end : telemetry.Length; end = end < telemetry.Length ? end : telemetry.Length;
var step = count > 15 ? count / 5 : count > 3 ? 3: 1; var step = count > 15 ? count / 5 : count > 3 ? 3 : 1;
for (var i = begin; i < end; i+= step) for (var i = begin; i < end; i += step)
{ {
var item = telemetry[i]; var item = telemetry[i];
var itemValue = getter(item); var itemValue = getter(item);
@ -302,6 +361,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
if (max - min > deviation) if (max - min > deviation)
return true; return true;
} }
return false; return false;
} }
@ -323,6 +383,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
if (Math.Abs(beginPointValue - itemValue) > deviation) if (Math.Abs(beginPointValue - itemValue) > deviation)
return true; return true;
} }
return false; return false;
} }
@ -341,13 +402,12 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
{ {
var item = telemetry[i]; var item = telemetry[i];
var itemValue = getter(item); var itemValue = getter(item);
if ( itemValue - beginPointValue > deviation) if (itemValue - beginPointValue > deviation)
return true; return true;
} }
return false; return false;
} }
} }
} }

View File

@ -1,68 +1,66 @@
using System; // using System;
using AsbCloudDb.Model; // using AsbCloudDb.Model;
//
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors // namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
{ // {
//
/// <summary> // /// <summary>
/// Проработка перед наращиванием // /// Проработка перед наращиванием
/// </summary> // /// </summary>
internal class DetectorDevelopment : DetectorAbstract // internal class DetectorDevelopment : DetectorAbstract
{ // {
protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end) // protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end)
=> CalcDeltaMinutes(telemetry, begin, end); // => CalcDeltaMinutes(telemetry, begin, end);
//
public override Func<DetectableTelemetry[], int, int, int> GetIdOperation => (_, _, _) // public override Func<DetectableTelemetry[], int, int, int> GetIdOperation => (_, _, _)
=> WellOperationCategory.IdDevelopment; // => WellOperationCategory.IdDevelopment;
//
protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) // protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{ // {
if (previousOperation?.IdCategory == WellOperationCategory.IdSlipsTime) // if (previousOperation?.IdCategory == WellOperationCategory.IdSlipsTime)
return false; // return false;
//
var point0 = telemetry[position]; // var point0 = telemetry[position];
var delta = point0.WellDepth - point0.BitDepth; // var delta = point0.WellDepth - point0.BitDepth;
if (delta < 0.03d || delta > 30) // if (delta < 0.03d || delta > 30)
return false; // return false;
//
if (point0.Pressure < 15) // if (point0.Pressure < 15)
return false; // return false;
//
if (point0.BlockPosition > 2.5) // if (point0.BlockPosition > 2.5)
return false; // return false;
//
if (point0.RotorSpeed < 10) // if (point0.RotorSpeed < 10)
return false; // return false;
//
if (!ContainsDeviationApprox(telemetry, d => d.BlockPosition, position, 60, 0.03)) // if (!ContainsDeviationApprox(telemetry, d => d.BlockPosition, position, 60, 0.03))
return false; // return false;
//
return true; // return true;
} // }
//
protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) // protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{ // {
var point0 = telemetry[position]; // var point0 = telemetry[position];
var delta = point0.WellDepth - point0.BitDepth; // var delta = point0.WellDepth - point0.BitDepth;
if (delta < 0.03d || delta > 30) // if (delta < 0.03d || delta > 30)
return IdReasonOfEnd_DeltaDepthOutOfRange; // return IdReasonOfEnd_DeltaDepthOutOfRange;
//
if (point0.Pressure < 15) // if (point0.Pressure < 15)
return IdReasonOfEnd_PressureIsLo; // return IdReasonOfEnd_PressureIsLo;
//
if (point0.BlockPosition > 31) // if (point0.BlockPosition > 31)
return IdReasonOfEnd_BlockPositionIsHi; // return IdReasonOfEnd_BlockPositionIsHi;
//
if (point0.RotorSpeed < 10) // if (point0.RotorSpeed < 10)
return IdReasonOfEnd_RotorSpeedIsLo; // return IdReasonOfEnd_RotorSpeedIsLo;
//
return IdReasonOfEnd_NotDetected; // return IdReasonOfEnd_NotDetected;
} // }
//
protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end) // protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end)
=> IsValidByWellDepthDoesNotChange(telemetry, begin, end); // => IsValidByWellDepthDoesNotChange(telemetry, begin, end);
} // }
// }
//
}

View File

@ -1,12 +1,17 @@
using System; using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq; using System.Linq;
using AsbCloudDb.Model; using AsbCloudDb.Model;
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors; namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors;
internal class DetectorDrilling : DetectorAbstract public class DetectorDrilling : DetectorAbstract
{ {
public override Func<DetectableTelemetry[], int, int, int> GetIdOperation => DefineDrillingOperation; private const double dispersionOfNormalizedRotorSpeedThreshold = 0.2d;
public const string ExtraDataKeyHasOscillation = "hasOscillation";
public const string ExtraDataKeyDispersionOfNormalizedRotorSpeed = "dispersionOfNormalizedRotorSpeed";
public const string ExtraDataKeyAvgRotorSpeed = "avgRotorSpeed";
protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{ {
@ -15,10 +20,7 @@ internal class DetectorDrilling : DetectorAbstract
if (delta > 0.03d) if (delta > 0.03d)
return false; return false;
if (point0.Pressure < 25) if (point0.Pressure < 18)
return false;
if (point0.RotorSpeed < 5)
return false; return false;
return true; return true;
@ -32,32 +34,61 @@ internal class DetectorDrilling : DetectorAbstract
if (delta > 0.03d) if (delta > 0.03d)
return IdReasonOfEnd_DeltaDepthIsHi; return IdReasonOfEnd_DeltaDepthIsHi;
if (point0.Pressure < 25) if (point0.Pressure < 18)
return IdReasonOfEnd_PressureIsLo; return IdReasonOfEnd_PressureIsLo;
return IdReasonOfEnd_NotDetected; return IdReasonOfEnd_NotDetected;
} }
protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end)
=> IsValidByWellDepthIncreasing(telemetry, begin, end);
protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end) protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end)
=> CalcRop(telemetry, begin, end); => CalcRop(telemetry, begin, end);
private static int DefineDrillingOperation(DetectableTelemetry[] telemetry, int begin, int end) protected override bool IsValidOperationDetectorResult(OperationDetectorResult operationDetectorResult) =>
base.IsValidOperationDetectorResult(operationDetectorResult)
&& (operationDetectorResult.Operation.DepthEnd - operationDetectorResult.Operation.DepthStart) > 0.01;
protected override (int Begin, int End) RefineEdges(DetectableTelemetry[] telemetry, int begin, int end)
{ {
const int idSlideWithOscillation = 12000; var i = end;
for (; i > begin + 1; i--)
if (telemetry[i].WellDepth - telemetry[i - 1].WellDepth > 0.001d)
break;
var telemetryRange = telemetry[begin.. end]; return (begin, i);
}
protected override (int IdCategory, IDictionary<string, object> ExtraData) GetSpecificInformation(DetectableTelemetry[] telemetry, int begin, int end)
{
var (avgRotorSpeed, dispersionOfNormalizedRotorSpeed) = CalcCriteries(telemetry, begin, end);
var idCategory = GetIdOperation(avgRotorSpeed, dispersionOfNormalizedRotorSpeed);
var extraData = new Dictionary<string, object>
{
[ExtraDataKeyAvgRotorSpeed] = avgRotorSpeed,
[ExtraDataKeyDispersionOfNormalizedRotorSpeed] = dispersionOfNormalizedRotorSpeed,
[ExtraDataKeyHasOscillation] = 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 avgRotorSpeed = telemetryRange.Average(t => t.RotorSpeed);
var dispersion = telemetryRange.Average(t => Math.Pow(t.RotorSpeed / avgRotorSpeed - 1, 2));
return (avgRotorSpeed, dispersion);
}
if (avgRotorSpeed < 10) private static int GetIdOperation(double avgRotorSpeed, double dispersionOfNormalizedRotorSpeed)
{
const int idSlideWithOscillation = WellOperationCategory.IdSlide;
if (avgRotorSpeed < 5)
return WellOperationCategory.IdSlide; return WellOperationCategory.IdSlide;
var despersion = telemetryRange if (dispersionOfNormalizedRotorSpeed < dispersionOfNormalizedRotorSpeedThreshold)
.Average(t => Math.Pow((t.RotorSpeed - avgRotorSpeed) / avgRotorSpeed, 2)); return WellOperationCategory.IdRotor;
else
return despersion < 0.2d ? WellOperationCategory.IdRotor : idSlideWithOscillation; return idSlideWithOscillation;
} }
} }

View File

@ -1,16 +1,14 @@
using System; using System;
using System.Collections.Generic;
using AsbCloudDb.Model; using AsbCloudDb.Model;
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors;
{
internal class DetectorSlipsTime : DetectorAbstract public class DetectorSlipsTime : DetectorAbstract
{ {
protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end) protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end)
=> CalcDeltaMinutes(telemetry, begin, end); => CalcDeltaMinutes(telemetry, begin, end);
public override Func<DetectableTelemetry[], int, int, int> GetIdOperation => (_, _, _) => WellOperationCategory.IdSlipsTime;
protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{ {
var point0 = telemetry[position]; var point0 = telemetry[position];
@ -30,10 +28,12 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
return true; return true;
} }
protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end) protected override (int IdCategory, IDictionary<string, object> ExtraData) GetSpecificInformation(DetectableTelemetry[] telemetry, int begin, int end)
=> IsValidByWellDepthDoesNotChange(telemetry, begin, end); {
return (WellOperationCategory.IdSlipsTime, new Dictionary<string, object>());
} }
protected override bool IsValidOperationDetectorResult(OperationDetectorResult operationDetectorResult) =>
Math.Abs(operationDetectorResult.Operation.DepthStart - operationDetectorResult.Operation.DepthEnd) > 0.01;
} }

View File

@ -1,29 +1,27 @@
Метод определения бурения # Алгоритм определения бурения в роторе, слайде, слайде с осцилляцией
Признак начала операции = ## Метод определения операции бурения
расстояние от долота до забоя < 0.03м  И Признак начала операции =
(расстояние от долота до забоя < 0.03м) И
(давление > 18атм)
давление > 25атм Признак окончания операции =
(расстояние от долота до забоя > 0.03м) ИЛИ
(давление < 18атм)
Признак окончания операции = ## Валидация
Для точного определения операции бурения, необходимо убрать диапазон в которых сработал признак окончания операции и не менялась глубина:
Определили точку окончания операции исходя из Признак окончания операции.
Определяем временной интервал, когда не менялась глубина (т.е. время шло, а глубина была неизменна)
Определив начальную точку и точку окончания операции
Исключаем этот интервал из операции.
расстояние от долота до забоя > 0.03м  ИЛИ ## Метод определения бурения в слайде
Необходимо рассчитать средние обороты ротора за всю операцию бурения.
Если среднее арифметическое больше константы (5 об/мин), то это бурение в роторе, если меньше, то это бурение в слайде.
давление < 25атм ## Метод определения бурения в роторе, слайде с осцилляцией
Необходимо рассчитать десперсию нормированных оборотов ротора по(по среднему значению)
Находим границы 1. Если полученное значение больше константы(0,2), то мы подтвердили что бурение в роторе.
2. Если полученное значение меньше константы, то это бурение в слайде с осцилляцией.
После того когда мы нашли границы, мы должны определить операцию, тогда мы смотрим на забой точки окончания операций сравниваем с забоем точками начала операций:
Если они равны друг другу, то мы эту операцию дальше не обрабатываем, а выбрасываем.
Если они не равны, то у нас произошло увеличение забоя, значит эта операция бурения.
Дальше мы определяем как мы бурили в роторе или слайде, для этого нам необходимо рассчитать среднюю скорость(среднее арифметическое) за всю операцию бурения . Если среднее арифметическое больше константы (10 об/мин), то это бурение в роторе, если меньше, то это бурение в слайде.
Если бурение в роторе, то мы считаем только дисперсию нормированных оборотов ротора(по среднему значению). (Так как это может быть бурение в слайде с осцилляцией и выглядеть как бурение в роторе):
Если полученное значение меньше константы(0,2), то мы подтвердили что бурение в роторе.
Если полученное значение больше константы, то это бурение в слайде с осцилляцией.

View File

@ -16,11 +16,12 @@ public class WorkOperationDetection: Work
{ {
private static readonly DetectorAbstract[] detectors = new DetectorAbstract[] private static readonly DetectorAbstract[] detectors = new DetectorAbstract[]
{ {
new DetectorDrilling(),
new DetectorSlipsTime()
// new DetectorRotor(), // new DetectorRotor(),
// new DetectorSlide(), // new DetectorSlide(),
//new DetectorDevelopment(), //new DetectorDevelopment(),
//new DetectorTemplating(), //new DetectorTemplating(),
new DetectorSlipsTime(),
//new DetectorStaticSurveying(), //new DetectorStaticSurveying(),
//new DetectorFlashingBeforeConnection(), //new DetectorFlashingBeforeConnection(),
//new DetectorFlashing(), //new DetectorFlashing(),
@ -95,6 +96,7 @@ public class WorkOperationDetection: Work
{ {
DateTime = d.DateTime, DateTime = d.DateTime,
IdUser = d.IdUser, IdUser = d.IdUser,
Mode = d.Mode,
WellDepth = d.WellDepth, WellDepth = d.WellDepth,
Pressure = d.Pressure, Pressure = d.Pressure,
HookWeight = d.HookWeight, HookWeight = d.HookWeight,
@ -125,25 +127,21 @@ public class WorkOperationDetection: Work
var isDetected = false; var isDetected = false;
var positionBegin = 0; var positionBegin = 0;
var positionEnd = data.Length - gap; var positionEnd = data.Length - gap;
var step = 10;
while (positionEnd > positionBegin) while (positionEnd > positionBegin)
{ {
step ++; foreach (var detector in detectors)
for (int i = 0; i < detectors.Length; i++)
{
if (detectors[i].TryDetect(idTelemetry, data, positionBegin, positionEnd, lastDetectedOperation, out OperationDetectorResult? result))
{ {
if (!detector.TryDetect(idTelemetry, data, positionBegin, positionEnd, lastDetectedOperation, out var result))
continue;
detectedOperations.Add(result!.Operation); detectedOperations.Add(result!.Operation);
lastDetectedOperation = result.Operation; lastDetectedOperation = result.Operation;
isDetected = true; isDetected = true;
step = 1;
positionBegin = result.TelemetryEnd; positionBegin = result.TelemetryEnd;
break; break;
} }
}
if (step > 20) positionBegin += 1;
step = 10;
positionBegin += step;
} }
if (isDetected) if (isDetected)

View File

@ -13,11 +13,10 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services namespace AsbCloudInfrastructure.Services;
{
public class ReportService : IReportService public class ReportService : IReportService
{ {
private readonly IAsbCloudDbContext db; private readonly IAsbCloudDbContext db;
private readonly ITelemetryService telemetryService; private readonly ITelemetryService telemetryService;
private readonly FileService fileService; private readonly FileService fileService;
@ -98,6 +97,17 @@ namespace AsbCloudInfrastructure.Services
}; };
var work = Work.CreateByDelegate(workId, workAction); var work = Work.CreateByDelegate(workId, workAction);
work.OnErrorAsync = (message, exception, token) => Task.Run(() => progressHandler.Invoke(new
{
Operation = "error",
Progress = 100f,
Message = string.IsNullOrEmpty(message)
? exception.Message
: message,
Exception = exception,
}, workId)
, token);
backgroundWorkerService.Enqueue(work); backgroundWorkerService.Enqueue(work);
progressHandler.Invoke(new ReportProgressDto progressHandler.Invoke(new ReportProgressDto
@ -200,5 +210,4 @@ namespace AsbCloudInfrastructure.Services
return await fileService.DeleteAsync(fileIds, token); return await fileService.DeleteAsync(fileIds, token);
} }
}
} }

View File

@ -213,7 +213,7 @@ namespace AsbCloudInfrastructure.Services.SAUB
if ((DateTimeOffset.UtcNow - geDate) < TimeSpan.FromHours(12)) if ((DateTimeOffset.UtcNow - geDate) < TimeSpan.FromHours(12))
{ {
// пробуем обойтись кешем // пробуем обойтись кешем
var cechedRange = telemetryDataCache.GetOrDefaultCachedaDateRange(telemetry.Id); var cechedRange = telemetryDataCache.GetOrDefaultCachedDateRange(telemetry.Id);
if (cechedRange?.From <= geDate) if (cechedRange?.From <= geDate)
{ {
var datesRange = new DatesRangeDto var datesRange = new DatesRangeDto

View File

@ -20,7 +20,7 @@ namespace AsbCloudInfrastructure.Services.SAUB
class TelemetryDataCacheItem class TelemetryDataCacheItem
{ {
public TDto FirstByDate { get; init; } = default!; public TDto FirstByDate { get; init; } = default!;
public CyclycArray<TDto> LastData { get; init; } = null!; public CyclicArray<TDto> LastData { get; init; } = null!;
public double TimezoneHours { get; init; } = 5; public double TimezoneHours { get; init; } = 5;
} }
@ -85,7 +85,7 @@ namespace AsbCloudInfrastructure.Services.SAUB
cacheItem = caches.GetOrAdd(idTelemetry, _ => new TelemetryDataCacheItem() cacheItem = caches.GetOrAdd(idTelemetry, _ => new TelemetryDataCacheItem()
{ {
FirstByDate = range.ElementAt(0), FirstByDate = range.ElementAt(0),
LastData = new CyclycArray<TDto>(activeWellCapacity) LastData = new CyclicArray<TDto>(activeWellCapacity)
}); });
} }
@ -159,7 +159,7 @@ namespace AsbCloudInfrastructure.Services.SAUB
return new DatesRangeDto { From = from.Value, To = to }; return new DatesRangeDto { From = from.Value, To = to };
} }
public DatesRangeDto? GetOrDefaultCachedaDateRange(int idTelemetry) public DatesRangeDto? GetOrDefaultCachedDateRange(int idTelemetry)
{ {
if (!caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? cacheItem)) if (!caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? cacheItem))
return null; return null;
@ -260,7 +260,7 @@ namespace AsbCloudInfrastructure.Services.SAUB
return dto; return dto;
}); });
var cacheItem = new CyclycArray<TDto>(capacity); var cacheItem = new CyclicArray<TDto>(capacity);
cacheItem.AddRange(dtos); cacheItem.AddRange(dtos);
var item = new TelemetryDataCacheItem var item = new TelemetryDataCacheItem

View File

@ -43,7 +43,6 @@ namespace AsbCloudInfrastructure.Services.SAUB
.Where(t => t.IdTelemetry == idTelemetry) .Where(t => t.IdTelemetry == idTelemetry)
.Where(t => t.BlockPosition > 0.0001) .Where(t => t.BlockPosition > 0.0001)
.Where(t => t.WellDepth > 0.0001) .Where(t => t.WellDepth > 0.0001)
.Where(t => t.Mode != null)
.Where(t => modes.Contains(t.Mode)) .Where(t => modes.Contains(t.Mode))
.Where(t => t.WellDepth - t.BitDepth < 0.01) .Where(t => t.WellDepth - t.BitDepth < 0.01)
.GroupBy(t => new { .GroupBy(t => new {

View File

@ -13,7 +13,7 @@ abstract class TrajectoryBaseService<TGeo, TCartesian>
where TGeo : TrajectoryGeoDto where TGeo : TrajectoryGeoDto
where TCartesian : TrajectoryCartesianDto, new() where TCartesian : TrajectoryCartesianDto, new()
{ {
ITrajectoryRepository<TGeo> repository; private readonly ITrajectoryRepository<TGeo> repository;
public TrajectoryBaseService(ITrajectoryRepository<TGeo> repository) public TrajectoryBaseService(ITrajectoryRepository<TGeo> repository)
{ {
@ -31,16 +31,16 @@ abstract class TrajectoryBaseService<TGeo, TCartesian>
public async Task<IEnumerable<TCartesian>?> GetAsync(int idWell, CancellationToken token) public async Task<IEnumerable<TCartesian>?> GetAsync(int idWell, CancellationToken token)
{ {
var geoCoords = await repository.GetAsync(idWell, token); var geoCoords = await repository.GetAsync(idWell, token);
var locs = GetTrajectoryVisualisation(geoCoords); var locs = TrajectoryBaseService<TGeo, TCartesian>.GetTrajectoryVisualisation(geoCoords);
var dtos = locs.Select(l => Convert(l)); var dtos = locs.Select(l => Convert(l));
return dtos; return dtos;
} }
private IEnumerable<Location> GetTrajectoryVisualisation(IEnumerable<TrajectoryGeoDto> geoCoordinates) private static IEnumerable<Location> GetTrajectoryVisualisation(IEnumerable<TrajectoryGeoDto> geoCoordinates)
{ {
var geoCoordinatesLength = geoCoordinates.Count(); var geoCoordinatesLength = geoCoordinates.Count();
if (geoCoordinatesLength < 2) if (geoCoordinatesLength < 2)
return new Location[0]; return Enumerable.Empty<Location>();
var cartesianCoordinates = new Location[geoCoordinatesLength]; var cartesianCoordinates = new Location[geoCoordinatesLength];
cartesianCoordinates[0] = new(); cartesianCoordinates[0] = new();
@ -48,7 +48,7 @@ abstract class TrajectoryBaseService<TGeo, TCartesian>
var geoCoordinatesArray = geoCoordinates.OrderBy(c => c.WellboreDepth).ToArray(); var geoCoordinatesArray = geoCoordinates.OrderBy(c => c.WellboreDepth).ToArray();
for (var i = 1; i < geoCoordinatesLength; i++) for (var i = 1; i < geoCoordinatesLength; i++)
{ {
var coordinates = Calculate(cartesianCoordinates[i - 1], var coordinates = TrajectoryBaseService<TGeo, TCartesian>.Calculate(cartesianCoordinates[i - 1],
geoCoordinatesArray[i - 1], geoCoordinatesArray[i - 1],
geoCoordinatesArray[i]); geoCoordinatesArray[i]);
@ -58,7 +58,7 @@ abstract class TrajectoryBaseService<TGeo, TCartesian>
return cartesianCoordinates; return cartesianCoordinates;
} }
protected Location Calculate(Location prevlocation, TrajectoryGeoDto prev, TrajectoryGeoDto current) protected static Location Calculate(Location prevLocation, TrajectoryGeoDto prev, TrajectoryGeoDto current)
{ {
var intervalGeoParams = prev; var intervalGeoParams = prev;
var deltaWellLength = current.WellboreDepth - intervalGeoParams.WellboreDepth; var deltaWellLength = current.WellboreDepth - intervalGeoParams.WellboreDepth;
@ -70,9 +70,9 @@ abstract class TrajectoryBaseService<TGeo, TCartesian>
return new() return new()
{ {
North = prevlocation.North + dNorth, North = prevLocation.North + dNorth,
East = prevlocation.East + dEast, East = prevLocation.East + dEast,
Depth = prevlocation.Depth + dDepth, Depth = prevLocation.Depth + dDepth,
Trajectory = current, Trajectory = current,
}; };
} }
@ -125,9 +125,9 @@ class TrajectoryNnbService : TrajectoryBaseService<TrajectoryGeoFactDto, Traject
public class TrajectoryService public class TrajectoryService
{ {
private TrajectoryPlanService trajectoryPlanService; private readonly TrajectoryPlanService trajectoryPlanService;
private TrajectoryFactService trajectoryFactService; private readonly TrajectoryFactService trajectoryFactService;
private TrajectoryNnbService trajectoryNnbService; private readonly TrajectoryNnbService trajectoryNnbService;
public TrajectoryService( public TrajectoryService(
ITrajectoryEditableRepository<TrajectoryGeoPlanDto> planRepository, ITrajectoryEditableRepository<TrajectoryGeoPlanDto> planRepository,
@ -147,11 +147,12 @@ public class TrajectoryService
/// <returns></returns> /// <returns></returns>
public async Task<TrajectoryPlanFactDto<IEnumerable<TrajectoryCartesianPlanDto>, IEnumerable<TrajectoryCartesianFactDto>>> GetTrajectoryCartesianAsync(int idWell, CancellationToken token) public async Task<TrajectoryPlanFactDto<IEnumerable<TrajectoryCartesianPlanDto>, IEnumerable<TrajectoryCartesianFactDto>>> GetTrajectoryCartesianAsync(int idWell, CancellationToken token)
{ {
var result = new TrajectoryPlanFactDto<IEnumerable<TrajectoryCartesianPlanDto>, IEnumerable<TrajectoryCartesianFactDto>>(); var result = new TrajectoryPlanFactDto<IEnumerable<TrajectoryCartesianPlanDto>, IEnumerable<TrajectoryCartesianFactDto>>
{
result.Plan = await trajectoryPlanService.GetAsync(idWell, token); Plan = await trajectoryPlanService.GetAsync(idWell, token),
result.FactManual = await trajectoryFactService.GetAsync(idWell, token); FactManual = await trajectoryFactService.GetAsync(idWell, token),
result.FactNnb = await trajectoryNnbService.GetAsync(idWell, token); FactNnb = await trajectoryNnbService.GetAsync(idWell, token)
};
return result; return result;
} }

View File

@ -28,8 +28,6 @@ namespace AsbCloudInfrastructure.Services
private readonly IWellFinalDocumentsRepository wellFinalDocumentsRepository; private readonly IWellFinalDocumentsRepository wellFinalDocumentsRepository;
private readonly NotificationService notificationService; private readonly NotificationService notificationService;
private const int FileServiceThrewException = -1;
public WellFinalDocumentsService(FileService fileService, public WellFinalDocumentsService(FileService fileService,
IUserRepository userRepository, IUserRepository userRepository,
IWellService wellService, IWellService wellService,
@ -48,7 +46,7 @@ namespace AsbCloudInfrastructure.Services
} }
///<inheritdoc/> ///<inheritdoc/>
public async Task<int> UpdateRangeAsync(int idWell, IEnumerable<WellFinalDocumentInputDto>? dtos, CancellationToken token) public async Task<int> UpdateRangeAsync(int idWell, IEnumerable<WellFinalDocumentInputDto> dtos, CancellationToken token)
{ {
var data = await wellFinalDocumentsRepository.UpdateRangeAsync(idWell, dtos, token); var data = await wellFinalDocumentsRepository.UpdateRangeAsync(idWell, dtos, token);

View File

@ -38,16 +38,15 @@ public class WellOperationExportService : IWellOperationExportService
var timezone = wellService.GetTimezone(idWell); var timezone = wellService.GetTimezone(idWell);
return await MakeExcelFileStreamAsync(operations, timezone.Hours, cancellationToken); return MakeExcelFileStream(operations, timezone.Hours);
} }
private async Task<Stream> MakeExcelFileStreamAsync(IEnumerable<WellOperationDto> operations, double timezoneOffset, private Stream MakeExcelFileStream(IEnumerable<WellOperationDto> operations, double timezoneOffset)
CancellationToken cancellationToken)
{ {
using Stream ecxelTemplateStream = wellOperationImportTemplateService.GetExcelTemplateStream(); using Stream ecxelTemplateStream = wellOperationImportTemplateService.GetExcelTemplateStream();
using var workbook = new XLWorkbook(ecxelTemplateStream, XLEventTracking.Disabled); using var workbook = new XLWorkbook(ecxelTemplateStream, XLEventTracking.Disabled);
await AddOperationsToWorkbook(workbook, operations, timezoneOffset, cancellationToken); AddOperationsToWorkbook(workbook, operations, timezoneOffset);
var memoryStream = new MemoryStream(); var memoryStream = new MemoryStream();
workbook.SaveAs(memoryStream, new SaveOptions { }); workbook.SaveAs(memoryStream, new SaveOptions { });
@ -55,15 +54,14 @@ public class WellOperationExportService : IWellOperationExportService
return memoryStream; return memoryStream;
} }
private async Task AddOperationsToWorkbook(XLWorkbook workbook, IEnumerable<WellOperationDto> operations, double timezoneOffset, private void AddOperationsToWorkbook(XLWorkbook workbook, IEnumerable<WellOperationDto> operations, double timezoneOffset)
CancellationToken cancellationToken)
{ {
var planOperations = operations.Where(o => o.IdType == 0); var planOperations = operations.Where(o => o.IdType == 0);
if (planOperations.Any()) if (planOperations.Any())
{ {
var sheetPlan = workbook.Worksheets.FirstOrDefault(ws => ws.Name == DefaultTemplateInfo.SheetNamePlan); var sheetPlan = workbook.Worksheets.FirstOrDefault(ws => ws.Name == DefaultTemplateInfo.SheetNamePlan);
if (sheetPlan is not null) if (sheetPlan is not null)
await AddOperationsToSheetAsync(sheetPlan, planOperations, timezoneOffset, cancellationToken); AddOperationsToSheet(sheetPlan, planOperations, timezoneOffset);
} }
var factOperations = operations.Where(o => o.IdType == 1); var factOperations = operations.Where(o => o.IdType == 1);
@ -71,12 +69,11 @@ public class WellOperationExportService : IWellOperationExportService
{ {
var sheetFact = workbook.Worksheets.FirstOrDefault(ws => ws.Name == DefaultTemplateInfo.SheetNameFact); var sheetFact = workbook.Worksheets.FirstOrDefault(ws => ws.Name == DefaultTemplateInfo.SheetNameFact);
if (sheetFact is not null) if (sheetFact is not null)
await AddOperationsToSheetAsync(sheetFact, factOperations, timezoneOffset, cancellationToken); AddOperationsToSheet(sheetFact, factOperations, timezoneOffset);
} }
} }
private async Task AddOperationsToSheetAsync(IXLWorksheet sheet, IEnumerable<WellOperationDto> operations, double timezoneOffset, private void AddOperationsToSheet(IXLWorksheet sheet, IEnumerable<WellOperationDto> operations, double timezoneOffset)
CancellationToken cancellationToken)
{ {
var operationsToArray = operations.ToArray(); var operationsToArray = operations.ToArray();

View File

@ -22,7 +22,7 @@ namespace AsbCloudInfrastructure
var provider = scope.ServiceProvider; var provider = scope.ServiceProvider;
var context = provider.GetRequiredService<IAsbCloudDbContext>(); var context = provider.GetRequiredService<IAsbCloudDbContext>();
context.Database.EnshureCreatedAndMigrated(); context.Database.EnsureCreatedAndMigrated();
// TODO: Сделать инициализацию кеша телеметрии более явной. // TODO: Сделать инициализацию кеша телеметрии более явной.
_ = provider.GetRequiredService<ITelemetryDataCache<TelemetryDataSaubDto>>(); _ = provider.GetRequiredService<ITelemetryDataCache<TelemetryDataSaubDto>>();

View File

@ -3,7 +3,7 @@ using System.Linq;
namespace AsbCloudWebApi.Tests namespace AsbCloudWebApi.Tests
{ {
public static class AspExtentions public static class AspExtensions
{ {
public static IServiceCollection ReplaceService<T>(this IServiceCollection services, T instance) public static IServiceCollection ReplaceService<T>(this IServiceCollection services, T instance)
where T : notnull where T : notnull

View File

@ -0,0 +1,295 @@
using System.Collections.Generic;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Services.DetectOperations;
using AsbCloudInfrastructure.Services.DetectOperations.Detectors;
using Xunit;
namespace AsbCloudWebApi.Tests.Services.DetectedOperations.Detectors;
public class DetectorDrillingTests : DetectorDrilling
{
private const int idSlide = 5002;
private const int idRotor = 5003;
private const int idSlideWithOscillation = 12000;
[Theory]
[MemberData(nameof(TelemetryRangeDrillingRotor))]
public void DefineDrillingOperation_ShouldReturn_DrillingRotor(DetectableTelemetry[] telemetryRange)
{
//act
var result = GetSpecificInformation(telemetryRange, 0, telemetryRange.Length);
//assert
Assert.Equal(idRotor, result.IdCategory);
}
[Theory]
[MemberData(nameof(TelemetryRangeDrillingSlide))]
public void DefineDrillingOperation_ShouldReturn_DrillingSlide(DetectableTelemetry[] telemetryRange)
{
//act
var result = GetSpecificInformation(telemetryRange, 0, telemetryRange.Length);
//assert
Assert.Equal(idSlide, result.IdCategory);
}
[Theory]
[MemberData(nameof(TelemetryRangeDrillingSlideWithOscillation))]
public void DefineDrillingOperation_ShouldReturn_DrillingSlideWithOscillation(DetectableTelemetry[] telemetryRange)
{
//act
var result = GetSpecificInformation(telemetryRange, 0, telemetryRange.Length);
//assert
var oHasOscillation = result.ExtraData[ExtraDataKeyHasOscillation];
Assert.Equal(idSlide, result.IdCategory);
Assert.True(oHasOscillation is bool hasOscillation && hasOscillation);
}
[Fact]
public void IsValidOperationDetectorResult_ShouldReturn_True()
{
//arrange
var operationDetectorResult = new OperationDetectorResult
{
Operation = new DetectedOperation
{
DepthStart = 5000,
DepthEnd = 6000,
DateStart = System.DateTimeOffset.Now.AddMinutes(-1),
DateEnd = System.DateTimeOffset.Now,
}
};
//act
var result = IsValidOperationDetectorResult(operationDetectorResult);
//assert
Assert.True(result);
}
[Fact]
public void IsValidOperationDetectorResult_ShouldReturn_False()
{
//arrange
var operationDetectorResult = new OperationDetectorResult
{
Operation = new DetectedOperation
{
DepthStart = 5000,
DepthEnd = 5000,
DateStart = System.DateTimeOffset.Now.AddMinutes(-1),
DateEnd = System.DateTimeOffset.Now,
}
};
//act
var result = IsValidOperationDetectorResult(operationDetectorResult);
//assert
Assert.False(result);
}
public static IEnumerable<object[]> TelemetryRangeDrillingRotor()
{
yield return new object[]
{
new[]
{
new DetectableTelemetry
{
WellDepth = 4.187f,
Pressure = 27.815952f,
HookWeight = 34.221367f,
BlockPosition = 24.388f,
BitDepth = 4.187f,
RotorSpeed = 40.3f
},
new DetectableTelemetry
{
WellDepth = 4.232f,
Pressure = 28.080372f,
HookWeight = 34.162174f,
BlockPosition = 24.343f,
BitDepth = 4.232f,
RotorSpeed = 40.3f
},
new DetectableTelemetry
{
WellDepth = 4.277f,
Pressure = 29.047901f,
HookWeight = 33.688717f,
BlockPosition = 24.298f,
BitDepth = 24.298f,
RotorSpeed = 40.3f
},
new DetectableTelemetry
{
WellDepth = 4.309f,
Pressure = 29.574032f,
HookWeight = 33.692104f,
BlockPosition = 24.266f,
BitDepth = 4.309f,
RotorSpeed = 40.4f
},
new DetectableTelemetry
{
WellDepth = 4.324f,
Pressure = 24.007977f,
HookWeight = 34.838448f,
BlockPosition = 24.251f,
BitDepth = 4.324f,
RotorSpeed = 40.5f
},
new DetectableTelemetry
{
WellDepth = 4.324f,
Pressure = 24.04114f,
HookWeight = 34.423424f,
BlockPosition = 24.252f,
BitDepth = 4.323f,
RotorSpeed = 40.3f
}
}
};
}
public static IEnumerable<object[]> TelemetryRangeDrillingSlide()
{
yield return new object[]
{
new[]
{
new DetectableTelemetry
{
WellDepth = 447.276001f,
Pressure = 26.619421f,
HookWeight = 40.9143829f,
BlockPosition = 4.559f,
BitDepth = 477.265991f,
RotorSpeed = 0
},
new DetectableTelemetry
{
WellDepth = 477.289f,
Pressure = 28.716f,
HookWeight = 38.27f,
BlockPosition = 4.5f,
BitDepth = 477.289f,
RotorSpeed = 0.1f
},
new DetectableTelemetry
{
WellDepth = 477.30899f,
Pressure = 33.953495f,
HookWeight = 38.27f,
BlockPosition = 4.5359997f,
BitDepth = 477.289001f,
RotorSpeed = 0.1f
},
}
};
}
public static IEnumerable<object[]> TelemetryRangeDrillingSlideWithOscillation()
{
yield return new object[]
{
new[]
{
new DetectableTelemetry
{
IdUser = 1,
WellDepth = 415.306f,
Pressure = 53.731934f,
HookWeight = 41.049942f,
BlockPosition = 28.666f,
BitDepth = 415.293f,
RotorSpeed = 0.3f
},
new DetectableTelemetry
{
IdUser = 1,
WellDepth = 415.311f,
Pressure = 57.660595f,
HookWeight = 40.898712f,
BlockPosition = 28.648f,
BitDepth = 415.311f,
RotorSpeed = 0.2f
},
new DetectableTelemetry
{
IdUser = 1,
WellDepth = 415.326f,
Pressure = 59.211086f,
HookWeight = 40.882797f,
BlockPosition = 28.633f,
BitDepth = 415.326f,
RotorSpeed = 0.1f
},
new DetectableTelemetry
{
IdUser = 1,
WellDepth = 415.344f,
Pressure = 59.484406f,
HookWeight = 40.91972f,
BlockPosition = 28.615f,
BitDepth = 415.344f,
RotorSpeed = 0.2f
},
new DetectableTelemetry
{
IdUser = 1,
WellDepth = 415.364f,
Pressure = 60.739918f,
HookWeight = 40.795666f,
BlockPosition = 28.595f,
BitDepth = 415.364f,
RotorSpeed = 4.5f
},
new DetectableTelemetry
{
IdUser = 1,
WellDepth = 415.378f,
Pressure = 62.528984f,
HookWeight = 40.52114f,
BlockPosition = 28.581f,
BitDepth = 415.378f,
RotorSpeed = 22.6f
},
new DetectableTelemetry
{
IdUser = 1,
WellDepth = 415.392f,
Pressure = 67.0039f,
HookWeight = 38.878895f,
BlockPosition = 28.569f,
BitDepth = 415.39f,
RotorSpeed = 50f
},
new DetectableTelemetry
{
IdUser = 1,
WellDepth = 415.392f,
Pressure = 65.72418f,
HookWeight = 42.53173f,
BlockPosition = 28.622f,
BitDepth = 415.337f,
RotorSpeed = 93f
},
new DetectableTelemetry
{
IdUser = 1,
WellDepth = 415.392f,
Pressure = 56.82195f,
HookWeight = 43.15844f,
BlockPosition = 28.704f,
BitDepth = 415.255f,
RotorSpeed = 71.5f
}
}
};
}
}

View File

@ -86,7 +86,7 @@ public class PeriodicBackgroundWorkerTest
service.Add(badWork, TimeSpan.FromSeconds(2)); service.Add(badWork, TimeSpan.FromSeconds(2));
service.Add(goodWork, TimeSpan.FromSeconds(2)); service.Add(goodWork, TimeSpan.FromSeconds(2));
await Task.Delay(TimeSpan.FromMilliseconds(20)); await Task.Delay(TimeSpan.FromMilliseconds(128));
//assert //assert
Assert.Equal(expectadResult, result); Assert.Equal(expectadResult, result);

View File

@ -20,15 +20,16 @@ namespace AsbCloudWebApi.Controllers
public class ReportController : ControllerBase public class ReportController : ControllerBase
{ {
private readonly IReportService reportService; private readonly IReportService reportService;
private readonly FileService fileService;
private readonly IWellService wellService; private readonly IWellService wellService;
private readonly IHubContext<ReportsHub, IReportHubClient> reportsHubContext; private readonly IHubContext<ReportsHub, IReportHubClient> reportsHubContext;
public ReportController(IReportService reportService, IWellService wellService, public ReportController(
FileService fileService, IHubContext<ReportsHub, IReportHubClient> reportsHubContext) IReportService reportService,
IWellService wellService,
IHubContext<ReportsHub,
IReportHubClient> reportsHubContext)
{ {
this.reportService = reportService; this.reportService = reportService;
this.fileService = fileService;
this.wellService = wellService; this.wellService = wellService;
this.reportsHubContext = reportsHubContext; this.reportsHubContext = reportsHubContext;
} }
@ -150,6 +151,9 @@ namespace AsbCloudWebApi.Controllers
if (wellReportsDatesRange is null) if (wellReportsDatesRange is null)
return NoContent(); return NoContent();
if (wellReportsDatesRange.From == wellReportsDatesRange.To)
return NoContent();
return Ok(wellReportsDatesRange); return Ok(wellReportsDatesRange);
} }
} }

View File

@ -60,7 +60,7 @@ namespace AsbCloudWebApi.Controllers.SAUB
[FromQuery] DetectedOperationRequest request, [FromQuery] DetectedOperationRequest request,
CancellationToken token) CancellationToken token)
{ {
if (!await UserHasAccesToWellAsync(request.IdWell, token)) if (!await UserHasAccessToWellAsync(request.IdWell, token))
return Forbid(); return Forbid();
var result = await detectedOperationService.GetAsync(request, token); var result = await detectedOperationService.GetAsync(request, token);
@ -79,7 +79,7 @@ namespace AsbCloudWebApi.Controllers.SAUB
[FromQuery] DetectedOperationRequest request, [FromQuery] DetectedOperationRequest request,
CancellationToken token) CancellationToken token)
{ {
if (!await UserHasAccesToWellAsync(request.IdWell, token)) if (!await UserHasAccessToWellAsync(request.IdWell, token))
return Forbid(); return Forbid();
var result = await detectedOperationService.GetOperationsStatAsync(request, token); var result = await detectedOperationService.GetOperationsStatAsync(request, token);
@ -101,14 +101,14 @@ namespace AsbCloudWebApi.Controllers.SAUB
[FromQuery] DetectedOperationRequest request, [FromQuery] DetectedOperationRequest request,
CancellationToken token) CancellationToken token)
{ {
if (!await UserHasAccesToWellAsync(request.IdWell, token)) if (!await UserHasAccessToWellAsync(request.IdWell, token))
return Forbid(); return Forbid();
var result = await detectedOperationService.DeleteAsync(request, token); var result = await detectedOperationService.DeleteAsync(request, token);
return Ok(result); return Ok(result);
} }
protected async Task<bool> UserHasAccesToWellAsync(int idWell, CancellationToken token) protected async Task<bool> UserHasAccessToWellAsync(int idWell, CancellationToken token)
{ {
var idCompany = User.GetCompanyId(); var idCompany = User.GetCompanyId();
if (idCompany is not null && if (idCompany is not null &&

View File

@ -21,7 +21,7 @@ namespace AsbCloudWebApi.Controllers.Trajectory
public abstract class TrajectoryController<TDto> : ControllerBase public abstract class TrajectoryController<TDto> : ControllerBase
where TDto : TrajectoryGeoDto where TDto : TrajectoryGeoDto
{ {
protected abstract string fileName { get; set; } protected abstract string fileName { get; }
private readonly IWellService wellService; private readonly IWellService wellService;
private readonly TrajectoryExportService<TDto> trajectoryExportService; private readonly TrajectoryExportService<TDto> trajectoryExportService;

View File

@ -19,19 +19,17 @@ namespace AsbCloudWebApi.Controllers.Trajectory
/// </summary> /// </summary>
[ApiController] [ApiController]
[Authorize] [Authorize]
public abstract class TrajectoryEditableController<Tdto> : TrajectoryController<Tdto> public abstract class TrajectoryEditableController<TDto> : TrajectoryController<TDto>
where Tdto : TrajectoryGeoDto where TDto : TrajectoryGeoDto
{ {
protected override string fileName { get; set; } private readonly TrajectoryParserService<TDto> trajectoryImportService;
private readonly TrajectoryExportService<TDto> trajectoryExportService;
private readonly TrajectoryParserService<Tdto> trajectoryImportService; private readonly ITrajectoryEditableRepository<TDto> trajectoryRepository;
private readonly TrajectoryExportService<Tdto> trajectoryExportService;
private readonly ITrajectoryEditableRepository<Tdto> trajectoryRepository;
public TrajectoryEditableController(IWellService wellService, public TrajectoryEditableController(IWellService wellService,
TrajectoryParserService<Tdto> trajectoryImportService, TrajectoryParserService<TDto> trajectoryImportService,
TrajectoryExportService<Tdto> trajectoryExportService, TrajectoryExportService<TDto> trajectoryExportService,
ITrajectoryEditableRepository<Tdto> trajectoryRepository) ITrajectoryEditableRepository<TDto> trajectoryRepository)
: base( : base(
wellService, wellService,
trajectoryExportService, trajectoryExportService,
@ -118,7 +116,7 @@ namespace AsbCloudWebApi.Controllers.Trajectory
/// <returns>количество успешно записанных строк в БД</returns> /// <returns>количество успешно записанных строк в БД</returns>
[HttpPost] [HttpPost]
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)] [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> AddAsync(int idWell, [FromBody] Tdto row, public async Task<IActionResult> AddAsync(int idWell, [FromBody] TDto row,
CancellationToken token) CancellationToken token)
{ {
if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false)) if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false))
@ -141,7 +139,7 @@ namespace AsbCloudWebApi.Controllers.Trajectory
/// <returns>количество успешно записанных строк в БД</returns> /// <returns>количество успешно записанных строк в БД</returns>
[HttpPost("range")] [HttpPost("range")]
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)] [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> AddRangeAsync(int idWell, [FromBody] IEnumerable<Tdto> rows, public async Task<IActionResult> AddRangeAsync(int idWell, [FromBody] IEnumerable<TDto> rows,
CancellationToken token) CancellationToken token)
{ {
if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false)) if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false))
@ -169,7 +167,7 @@ namespace AsbCloudWebApi.Controllers.Trajectory
[HttpPut("{idRow}")] [HttpPut("{idRow}")]
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)] [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> UpdateAsync(int idWell, int idRow, public async Task<IActionResult> UpdateAsync(int idWell, int idRow,
[FromBody] Tdto row, CancellationToken token) [FromBody] TDto row, CancellationToken token)
{ {
if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false)) if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false))
return Forbid(); return Forbid();

View File

@ -14,7 +14,7 @@ namespace AsbCloudWebApi.Controllers.Trajectory;
[Route("api/well/{idWell}/[controller]")] [Route("api/well/{idWell}/[controller]")]
public class TrajectoryFactManualController : TrajectoryEditableController<TrajectoryGeoFactDto> public class TrajectoryFactManualController : TrajectoryEditableController<TrajectoryGeoFactDto>
{ {
protected override string fileName { get; set; } protected override string fileName => "ЕЦП_шаблон_файлаактическая_траектория.xlsx";
public TrajectoryFactManualController(IWellService wellService, public TrajectoryFactManualController(IWellService wellService,
TrajectoryFactManualParserService factTrajectoryImportService, TrajectoryFactManualParserService factTrajectoryImportService,
TrajectoryFactManualExportService factTrajectoryExportService, TrajectoryFactManualExportService factTrajectoryExportService,
@ -24,7 +24,5 @@ public class TrajectoryFactManualController : TrajectoryEditableController<Traje
factTrajectoryImportService, factTrajectoryImportService,
factTrajectoryExportService, factTrajectoryExportService,
trajectoryFactRepository) trajectoryFactRepository)
{ { }
fileName = "ЕЦП_шаблон_файлаактическая_траектория.xlsx";
}
} }

View File

@ -15,7 +15,7 @@ namespace AsbCloudWebApi.Controllers.Trajectory;
[Route("api/well/{idWell}/[controller]")] [Route("api/well/{idWell}/[controller]")]
public class TrajectoryFactNnbController : TrajectoryController<TrajectoryGeoFactDto> public class TrajectoryFactNnbController : TrajectoryController<TrajectoryGeoFactDto>
{ {
protected override string fileName { get; set; } protected override string fileName => "ЕЦП_шаблон_файлаактическая_ннбраектория.xlsx";
public TrajectoryFactNnbController( public TrajectoryFactNnbController(
ITrajectoryNnbRepository trajectoryNnbRepository, ITrajectoryNnbRepository trajectoryNnbRepository,
TrajectoryFactNnbExportService trajectoryExportService, TrajectoryFactNnbExportService trajectoryExportService,
@ -24,7 +24,5 @@ public class TrajectoryFactNnbController : TrajectoryController<TrajectoryGeoFac
wellService, wellService,
trajectoryExportService, trajectoryExportService,
trajectoryNnbRepository) trajectoryNnbRepository)
{ { }
fileName = "ЕЦП_шаблон_файлаактическая_ннбраектория.xlsx";
}
} }

View File

@ -21,6 +21,8 @@ namespace AsbCloudWebApi.Controllers.Trajectory
{ {
private readonly TrajectoryService trajectoryVisualizationService; private readonly TrajectoryService trajectoryVisualizationService;
protected override string fileName => "ЕЦП_шаблон_файла_плановая_траектория.xlsx";
public TrajectoryPlanController(IWellService wellService, public TrajectoryPlanController(IWellService wellService,
TrajectoryPlanParserService trajectoryPlanImportService, TrajectoryPlanParserService trajectoryPlanImportService,
TrajectoryPlanExportService trajectoryPlanExportService, TrajectoryPlanExportService trajectoryPlanExportService,
@ -32,7 +34,6 @@ namespace AsbCloudWebApi.Controllers.Trajectory
trajectoryPlanExportService, trajectoryPlanExportService,
trajectoryPlanRepository) trajectoryPlanRepository)
{ {
fileName = "ЕЦП_шаблон_файла_плановая_траектория.xlsx";
this.trajectoryVisualizationService = trajectoryVisualizationService; this.trajectoryVisualizationService = trajectoryVisualizationService;
} }

View File

@ -7,6 +7,7 @@ namespace AsbCloudWebApi
{ {
// Uncomment next line to find wired exceptions by tracing. // Uncomment next line to find wired exceptions by tracing.
//static TraceListenerView trace4debug = new TraceListenerView(); //static TraceListenerView trace4debug = new TraceListenerView();
enum A { a = 1 << 2 }
public static void Main(string[] args) public static void Main(string[] args)
{ {

View File

@ -1,8 +1,9 @@
using AsbCloudApp.Data; using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudInfrastructure.Background; using AsbCloudInfrastructure.Background;
using AsbCloudWebApi.SignalR.Clients; using AsbCloudWebApi.SignalR.Clients;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using System.Linq; using Microsoft.AspNetCore.Mvc;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -17,39 +18,44 @@ namespace AsbCloudWebApi.SignalR
[Authorize] [Authorize]
public class ReportsHub : BaseHub<IReportHubClient> public class ReportsHub : BaseHub<IReportHubClient>
{ {
private readonly BackgroundWorker backgroundWorker; private readonly IWellService wellService;
private readonly IReportService reportService;
public ReportsHub(BackgroundWorker backgroundWorker) public ReportsHub(IWellService wellService, IReportService reportService)
{ {
this.backgroundWorker = backgroundWorker; this.wellService = wellService;
this.reportService = reportService;
} }
/// <summary> /// <summary>
/// Добавление в группу, отправка данных о формировании отчета /// Добавление в группу, отправка данных о формировании отчета
/// </summary> /// </summary>
/// <param name="groupName"></param> /// <param name="groupName">группа</param>
/// <param name="idWell">ключ скважины</param>
/// <param name="request">параметры запроса</param>
/// <returns></returns> /// <returns></returns>
public override async Task AddToGroup(string groupName) public async Task CreateReport(string groupName, int idWell, ReportParametersRequest request)
{ {
var idUser = Context.User?.GetUserId();
var idCompany = Context.User?.GetCompanyId();
if ((idCompany is null) || (idUser is null))
return;
if (!await wellService.IsCompanyInvolvedInWellAsync((int)idCompany, idWell, CancellationToken.None))
return;
await base.AddToGroup(groupName); await base.AddToGroup(groupName);
var workId = groupName.Replace("Report_", ""); void HandleReportProgressAsync(object progress, string id) =>
var work = backgroundWorker.Works.FirstOrDefault(work => work.Id == workId); Task.Run(async () =>
var progress = new ReportProgressDto()
{ {
Operation = "Ожидает начала в очереди.", await Clients.Group(groupName)
Progress = 0f, .GetReportProgress(progress, CancellationToken.None);
}; }, CancellationToken.None);
var state = work?.CurrentState; var id = reportService.EnqueueCreateReportWork(idWell, (int)idUser,
if (state is not null) request.StepSeconds, request.Format, request.Begin, request.End, HandleReportProgressAsync);
{
progress.Operation = state.State;
progress.Progress = (float)state.Progress;
}
await Clients.Group(groupName).GetReportProgress(progress, CancellationToken.None);
} }
} }
} }

View File

@ -6,7 +6,7 @@ namespace SignalRTestClient;
internal class Program internal class Program
{ {
static void Main(string[] args) static void Main()
{ {
var connectionBuilder = new HubConnectionBuilder(); var connectionBuilder = new HubConnectionBuilder();
var connection = connectionBuilder var connection = connectionBuilder
@ -47,7 +47,7 @@ internal class Program
builder.AddProvider(provider); builder.AddProvider(provider);
} }
private static Task<string?> AccessTokenProvider() private static Task<string> AccessTokenProvider()
{ {
return Task.FromResult("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImN0eSI6IkpXVCJ9.eyJpZCI6IjEiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiZGV2IiwiaWRDb21wYW55IjoiMSIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6InJvb3QiLCJuYmYiOjE2NjI1NDgxNjIsImV4cCI6MTY5NDEwNTc2MiwiaXNzIjoiYSIsImF1ZCI6ImEifQ.OEAlNzxi7Jat6pzDBTAjTbChskc-tdJthJexyWwwUKE"); return Task.FromResult("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImN0eSI6IkpXVCJ9.eyJpZCI6IjEiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiZGV2IiwiaWRDb21wYW55IjoiMSIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6InJvb3QiLCJuYmYiOjE2NjI1NDgxNjIsImV4cCI6MTY5NDEwNTc2MiwiaXNzIjoiYSIsImF1ZCI6ImEifQ.OEAlNzxi7Jat6pzDBTAjTbChskc-tdJthJexyWwwUKE");
} }