This commit is contained in:
ngfrolov 2022-08-03 10:57:49 +05:00
commit 5de4e43ad9
132 changed files with 45184 additions and 487 deletions

View File

@ -1,5 +1,5 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace AsbCloudApp.Requests
{
@ -9,9 +9,16 @@ namespace AsbCloudApp.Requests
public class DetectedOperationRequest : RequestBase
{
/// <summary>
/// категории операций
/// категория операций
/// </summary>
public IEnumerable<int> CategoryIds { get; set; }
[Required]
public int IdWell { get; set; }
/// <summary>
/// категория операций
/// </summary>
[Required]
public int IdCategory { get; set; }
/// <summary>
/// Больше или равно дате

View File

@ -31,14 +31,14 @@ namespace AsbCloudApp.Services
/// <param name="id"></param>
/// <param name="token"></param>
/// <returns>null if not found</returns>
Task<TDto?> GetAsync(int id, CancellationToken token);
Task<TDto?> GetOrDefaultAsync(int id, CancellationToken token);
/// <summary>
/// Получить запись по id
/// </summary>
/// <param name="id"></param>
/// <returns>null if not found</returns>
TDto? Get(int id);
TDto? GetOrDefault(int id);
/// <summary>
/// Добавление новой записи

View File

@ -8,8 +8,8 @@ namespace AsbCloudApp.Services
{
public interface IDetectedOperationService
{
Task<IEnumerable<WellOperationCategoryDto>> GetCategoriesAsync(CancellationToken token);
Task<DetectedOperationListDto> GetAsync(int idWell, Requests.DetectedOperationRequest request, CancellationToken token);
Task<int> DeleteAsync(int idWell, DetectedOperationRequest request, CancellationToken token);
Task<IEnumerable<WellOperationCategoryDto>> GetCategoriesAsync(int? idWell, CancellationToken token);
Task<DetectedOperationListDto> GetAsync(DetectedOperationRequest request, CancellationToken token);
Task<int> DeleteAsync(DetectedOperationRequest request, CancellationToken token);
}
}

View File

@ -11,7 +11,7 @@ namespace AsbCloudApp.Services
Task<IEnumerable<SetpointsRequestDto>> GetAsync(int idWell, CancellationToken token);
Task<IEnumerable<SetpointsRequestDto>> GetForPanelAsync(string uid, CancellationToken token);
Task<int> TryDelete(int id, CancellationToken token);
Task<int> UpdateStateAsync(int id, SetpointsRequestDto setpointsRequestDto, CancellationToken token);
Task<int> UpdateStateAsync(SetpointsRequestDto setpointsRequestDto, CancellationToken token);
IEnumerable<SetpointInfoDto> GetSetpointsNames();
}
}

View File

@ -0,0 +1,15 @@
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudApp.Services
{
public interface IUserSettingsRepository
{
public const int ErrorKeyNotFound = -1;
public const int ErrorKeyIsUsed = -2;
Task<object> GetOrDefaultAsync(int userId, string key, CancellationToken token);
Task<int> InsertAsync(int userId, string key, object value, CancellationToken token);
Task<int> UpdateAsync(int userId, string key, object value, CancellationToken token);
Task<int> DeleteAsync(int userId, string key, CancellationToken token);
}
}

View File

@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Metadata;
using System;
using System.Collections.Generic;
@ -11,6 +12,38 @@ namespace AsbCloudDb
{
public static class EFExtentions
{
private static readonly System.Text.Json.JsonSerializerOptions jsonSerializerOptions = new()
{
AllowTrailingCommas = true,
WriteIndented = true,
NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString |
System.Text.Json.Serialization.JsonNumberHandling.AllowNamedFloatingPointLiterals,
};
public static Microsoft.EntityFrameworkCore.Metadata.Builders.PropertyBuilder<TProperty> HasJsonConversion<TProperty>(
this Microsoft.EntityFrameworkCore.Metadata.Builders.PropertyBuilder<TProperty> builder)
=> HasJsonConversion(builder, jsonSerializerOptions);
public static Microsoft.EntityFrameworkCore.Metadata.Builders.PropertyBuilder<TProperty> HasJsonConversion<TProperty>(
this Microsoft.EntityFrameworkCore.Metadata.Builders.PropertyBuilder<TProperty> builder,
System.Text.Json.JsonSerializerOptions jsonSerializerOptions)
{
builder.HasConversion(
s => System.Text.Json.JsonSerializer.Serialize(s, jsonSerializerOptions),
s => System.Text.Json.JsonSerializer.Deserialize<TProperty>(s, jsonSerializerOptions)!);
ValueComparer<TProperty> valueComparer = new (
(a,b) =>
(a!=null) && (b != null)
? a.GetHashCode() == b.GetHashCode()
: (a == null) && (b == null),
i => (i == null) ?-1 : i.GetHashCode(),
i => i);
builder.Metadata.SetValueComparer(valueComparer);
return builder;
}
static Dictionary<Type, IQueryStringFactory> QueryFactories { get; set; } = new();
static QueryStringFactory<T> GetQueryStringFactory<T>(DbSet<T> dbSet)
@ -32,7 +65,7 @@ namespace AsbCloudDb
{
var factory = GetQueryStringFactory(dbSet);
var query = factory.MakeInsertOrUpdateSql(items);
return database.ExecuteSqlRawAsync(query, token);
}
@ -97,7 +130,10 @@ namespace AsbCloudDb
pk = pkColsNames is null ? string.Empty : $"({string.Join(", ", pkColsNames)})";
TableName = dbset.EntityType.GetTableName()!;
getters = properties.Select(p => p.GetGetter());
getters = properties
.Where(p => !p.IsShadowProperty())
.Select(p => p.GetGetter()).ToList();
Columns = properties.Select(p => $"\"{p.GetColumnBaseName()}\"");
var colunmsString = $"({string.Join(", ", Columns)})";
@ -142,13 +178,21 @@ namespace AsbCloudDb
private static string FormatValue(object? v)
=> v switch
{
string vStr => $"'{vStr}'",
string vStr => $"'{EscapeCurlyBraces(vStr)}'",
DateTime vDate => $"'{FormatDateValue(vDate)}'",
DateTimeOffset vDate => $"'{FormatDateValue(vDate.UtcDateTime)}'",
IFormattable vFormattable => FormatFormattableValue(vFormattable),
_ => System.Text.Json.JsonSerializer.Serialize(v),
};
private static string EscapeCurlyBraces(string vStr)
{
var result = vStr
.Replace("{", "{{")
.Replace("}", "}}");
return result;
}
private static string FormatFormattableValue(IFormattable v)
=> v switch
{

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,34 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class Add_new_well_operations_17_18 : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.InsertData(
table: "t_well_operation_category",
columns: new[] { "id", "code", "name" },
values: new object[,]
{
{ 18, 0, "Проработка перед наращиванием" },
{ 19, 0, "Шаблонировка перед наращиванием" }
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DeleteData(
table: "t_well_operation_category",
keyColumn: "id",
keyValue: 18);
migrationBuilder.DeleteData(
table: "t_well_operation_category",
keyColumn: "id",
keyValue: 19);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,185 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class Rename_WITS_base_table : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_t_telemetry_wits_1_RecordBase_id_telemetry_date",
table: "t_telemetry_wits_1");
migrationBuilder.DropForeignKey(
name: "FK_t_telemetry_wits_50_RecordBase_id_telemetry_date",
table: "t_telemetry_wits_50");
migrationBuilder.DropForeignKey(
name: "FK_t_telemetry_wits_60_RecordBase_id_telemetry_date",
table: "t_telemetry_wits_60");
migrationBuilder.DropForeignKey(
name: "FK_t_telemetry_wits_61_RecordBase_id_telemetry_date",
table: "t_telemetry_wits_61");
migrationBuilder.DropForeignKey(
name: "FK_t_telemetry_wits_7_RecordBase_id_telemetry_date",
table: "t_telemetry_wits_7");
migrationBuilder.DropForeignKey(
name: "FK_t_telemetry_wits_8_RecordBase_id_telemetry_date",
table: "t_telemetry_wits_8");
migrationBuilder.DropPrimaryKey(
name: "PK_RecordBase",
table: "RecordBase");
migrationBuilder.RenameTable(
name: "RecordBase",
newName: "t_telemetry_wits_base");
migrationBuilder.AddPrimaryKey(
name: "PK_t_telemetry_wits_base",
table: "t_telemetry_wits_base",
columns: new[] { "id_telemetry", "date" });
migrationBuilder.AddForeignKey(
name: "FK_t_telemetry_wits_1_t_telemetry_wits_base_id_telemetry_date",
table: "t_telemetry_wits_1",
columns: new[] { "id_telemetry", "date" },
principalTable: "t_telemetry_wits_base",
principalColumns: new[] { "id_telemetry", "date" },
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_t_telemetry_wits_50_t_telemetry_wits_base_id_telemetry_date",
table: "t_telemetry_wits_50",
columns: new[] { "id_telemetry", "date" },
principalTable: "t_telemetry_wits_base",
principalColumns: new[] { "id_telemetry", "date" },
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_t_telemetry_wits_60_t_telemetry_wits_base_id_telemetry_date",
table: "t_telemetry_wits_60",
columns: new[] { "id_telemetry", "date" },
principalTable: "t_telemetry_wits_base",
principalColumns: new[] { "id_telemetry", "date" },
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_t_telemetry_wits_61_t_telemetry_wits_base_id_telemetry_date",
table: "t_telemetry_wits_61",
columns: new[] { "id_telemetry", "date" },
principalTable: "t_telemetry_wits_base",
principalColumns: new[] { "id_telemetry", "date" },
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_t_telemetry_wits_7_t_telemetry_wits_base_id_telemetry_date",
table: "t_telemetry_wits_7",
columns: new[] { "id_telemetry", "date" },
principalTable: "t_telemetry_wits_base",
principalColumns: new[] { "id_telemetry", "date" },
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_t_telemetry_wits_8_t_telemetry_wits_base_id_telemetry_date",
table: "t_telemetry_wits_8",
columns: new[] { "id_telemetry", "date" },
principalTable: "t_telemetry_wits_base",
principalColumns: new[] { "id_telemetry", "date" },
onDelete: ReferentialAction.Cascade);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_t_telemetry_wits_1_t_telemetry_wits_base_id_telemetry_date",
table: "t_telemetry_wits_1");
migrationBuilder.DropForeignKey(
name: "FK_t_telemetry_wits_50_t_telemetry_wits_base_id_telemetry_date",
table: "t_telemetry_wits_50");
migrationBuilder.DropForeignKey(
name: "FK_t_telemetry_wits_60_t_telemetry_wits_base_id_telemetry_date",
table: "t_telemetry_wits_60");
migrationBuilder.DropForeignKey(
name: "FK_t_telemetry_wits_61_t_telemetry_wits_base_id_telemetry_date",
table: "t_telemetry_wits_61");
migrationBuilder.DropForeignKey(
name: "FK_t_telemetry_wits_7_t_telemetry_wits_base_id_telemetry_date",
table: "t_telemetry_wits_7");
migrationBuilder.DropForeignKey(
name: "FK_t_telemetry_wits_8_t_telemetry_wits_base_id_telemetry_date",
table: "t_telemetry_wits_8");
migrationBuilder.DropPrimaryKey(
name: "PK_t_telemetry_wits_base",
table: "t_telemetry_wits_base");
migrationBuilder.RenameTable(
name: "t_telemetry_wits_base",
newName: "RecordBase");
migrationBuilder.AddPrimaryKey(
name: "PK_RecordBase",
table: "RecordBase",
columns: new[] { "id_telemetry", "date" });
migrationBuilder.AddForeignKey(
name: "FK_t_telemetry_wits_1_RecordBase_id_telemetry_date",
table: "t_telemetry_wits_1",
columns: new[] { "id_telemetry", "date" },
principalTable: "RecordBase",
principalColumns: new[] { "id_telemetry", "date" },
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_t_telemetry_wits_50_RecordBase_id_telemetry_date",
table: "t_telemetry_wits_50",
columns: new[] { "id_telemetry", "date" },
principalTable: "RecordBase",
principalColumns: new[] { "id_telemetry", "date" },
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_t_telemetry_wits_60_RecordBase_id_telemetry_date",
table: "t_telemetry_wits_60",
columns: new[] { "id_telemetry", "date" },
principalTable: "RecordBase",
principalColumns: new[] { "id_telemetry", "date" },
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_t_telemetry_wits_61_RecordBase_id_telemetry_date",
table: "t_telemetry_wits_61",
columns: new[] { "id_telemetry", "date" },
principalTable: "RecordBase",
principalColumns: new[] { "id_telemetry", "date" },
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_t_telemetry_wits_7_RecordBase_id_telemetry_date",
table: "t_telemetry_wits_7",
columns: new[] { "id_telemetry", "date" },
principalTable: "RecordBase",
principalColumns: new[] { "id_telemetry", "date" },
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_t_telemetry_wits_8_RecordBase_id_telemetry_date",
table: "t_telemetry_wits_8",
columns: new[] { "id_telemetry", "date" },
principalTable: "RecordBase",
principalColumns: new[] { "id_telemetry", "date" },
onDelete: ReferentialAction.Cascade);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,25 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class Add_Well_operation_Flashing : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.InsertData(
table: "t_well_operation_category",
columns: new[] { "id", "code", "name" },
values: new object[] { 20, 0, "Промывка перед наращиванием" });
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DeleteData(
table: "t_well_operation_category",
keyColumn: "id",
keyValue: 20);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,39 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class Add_Well_operation_StaticSurveying : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.UpdateData(
table: "t_well_operation_category",
keyColumn: "id",
keyValue: 20,
column: "name",
value: "Промывка перед наращиванием");
migrationBuilder.InsertData(
table: "t_well_operation_category",
columns: new[] { "id", "code", "name" },
values: new object[] { 21, 0, "Статический замер телесистемы" });
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DeleteData(
table: "t_well_operation_category",
keyColumn: "id",
keyValue: 21);
migrationBuilder.UpdateData(
table: "t_well_operation_category",
keyColumn: "id",
keyValue: 20,
column: "name",
value: "Шаблонировка перед наращиванием");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,38 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class Add_user_settings : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "t_user_settings",
columns: table => new
{
idUser = table.Column<int>(type: "integer", nullable: false),
key = table.Column<string>(type: "text", nullable: false, comment: "Ключ настроек пользователя"),
setting_value = table.Column<object>(type: "jsonb", nullable: true, comment: "Значение настроек пользователя")
},
constraints: table =>
{
table.PrimaryKey("PK_t_user_settings", x => new { x.idUser, x.key });
table.ForeignKey(
name: "FK_t_user_settings_t_user_idUser",
column: x => x.idUser,
principalTable: "t_user",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
},
comment: "настройки интерфейса пользователя");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "t_user_settings");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,37 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class Add_user_settings_key_size_limit : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "key",
table: "t_user_settings",
type: "character varying(255)",
maxLength: 255,
nullable: false,
comment: "Ключ настроек пользователя",
oldClrType: typeof(string),
oldType: "text",
oldComment: "Ключ настроек пользователя");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "key",
table: "t_user_settings",
type: "text",
nullable: false,
comment: "Ключ настроек пользователя",
oldClrType: typeof(string),
oldType: "character varying(255)",
oldMaxLength: 255,
oldComment: "Ключ настроек пользователя");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,49 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class UserSettings_rename_idUser : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_t_user_settings_t_user_idUser",
table: "t_user_settings");
migrationBuilder.RenameColumn(
name: "idUser",
table: "t_user_settings",
newName: "id_user");
migrationBuilder.AddForeignKey(
name: "FK_t_user_settings_t_user_id_user",
table: "t_user_settings",
column: "id_user",
principalTable: "t_user",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_t_user_settings_t_user_id_user",
table: "t_user_settings");
migrationBuilder.RenameColumn(
name: "id_user",
table: "t_user_settings",
newName: "idUser");
migrationBuilder.AddForeignKey(
name: "FK_t_user_settings_t_user_idUser",
table: "t_user_settings",
column: "idUser",
principalTable: "t_user",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
}
}
}

View File

@ -1,6 +1,5 @@
// <auto-generated />
using System;
using System.Collections.Generic;
using AsbCloudDb.Model;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
@ -52,7 +51,7 @@ namespace AsbCloudDb.Migrations
.HasColumnType("double precision")
.HasColumnName("longitude");
b.Property<SimpleTimezone>("Timezone")
b.Property<string>("Timezone")
.HasColumnType("jsonb")
.HasColumnName("timezone")
.HasComment("Смещение часового пояса от UTC");
@ -149,7 +148,7 @@ namespace AsbCloudDb.Migrations
.HasColumnName("start_date")
.HasComment("Дата отчёта");
b.Property<DailyReportInfo>("Info")
b.Property<string>("Info")
.HasColumnType("jsonb")
.HasColumnName("info")
.HasComment("Список параметров для отчёта");
@ -184,7 +183,7 @@ namespace AsbCloudDb.Migrations
.HasColumnType("double precision")
.HasColumnName("longitude");
b.Property<SimpleTimezone>("Timezone")
b.Property<string>("Timezone")
.HasColumnType("jsonb")
.HasColumnName("timezone")
.HasComment("Смещение часового пояса от UTC");
@ -704,7 +703,7 @@ namespace AsbCloudDb.Migrations
.HasColumnName("name")
.HasComment("Название файла");
b.Property<FilePublishInfo>("PublishInfo")
b.Property<string>("PublishInfo")
.HasColumnType("jsonb")
.HasColumnName("publish_info")
.HasComment("Информация о файле в облаке");
@ -791,7 +790,7 @@ namespace AsbCloudDb.Migrations
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<RawData>("Data")
b.Property<string>("Data")
.HasColumnType("jsonb")
.HasColumnName("data")
.HasComment("Данные таблицы последних данных");
@ -2727,7 +2726,7 @@ namespace AsbCloudDb.Migrations
.HasColumnName("obsolescence")
.HasComment("сек. до устаревания");
b.Property<Dictionary<string, double>>("Setpoints")
b.Property<string>("Setpoints")
.HasColumnType("jsonb")
.HasColumnName("setpoint_set")
.HasComment("Набор уставок");
@ -2756,7 +2755,7 @@ namespace AsbCloudDb.Migrations
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<TelemetryInfo>("Info")
b.Property<string>("Info")
.HasColumnType("jsonb")
.HasColumnName("info")
.HasComment("Информация с панели о скважине");
@ -2766,7 +2765,7 @@ namespace AsbCloudDb.Migrations
.HasColumnName("remote_uid")
.HasComment("Идентификатор передающего устройства. Может повторяться в списке, так как комплекты оборудования переезжают от скв. к скв.");
b.Property<SimpleTimezone>("TimeZone")
b.Property<string>("TimeZone")
.HasColumnType("jsonb")
.HasColumnName("timezone")
.HasComment("Смещение часового пояса от UTC");
@ -3873,6 +3872,30 @@ namespace AsbCloudDb.Migrations
});
});
modelBuilder.Entity("AsbCloudDb.Model.UserSetting", b =>
{
b.Property<int>("IdUser")
.HasColumnType("integer")
.HasColumnName("id_user");
b.Property<string>("Key")
.HasMaxLength(255)
.HasColumnType("character varying(255)")
.HasColumnName("key")
.HasComment("Ключ настроек пользователя");
b.Property<object>("Value")
.HasColumnType("jsonb")
.HasColumnName("setting_value")
.HasComment("Значение настроек пользователя");
b.HasKey("IdUser", "Key");
b.ToTable("t_user_settings");
b.HasComment("настройки интерфейса пользователя");
});
modelBuilder.Entity("AsbCloudDb.Model.Well", b =>
{
b.Property<int>("Id")
@ -3912,7 +3935,7 @@ namespace AsbCloudDb.Migrations
.HasColumnType("double precision")
.HasColumnName("longitude");
b.Property<SimpleTimezone>("Timezone")
b.Property<string>("Timezone")
.HasColumnType("jsonb")
.HasColumnName("timezone")
.HasComment("Смещение часового пояса от UTC");
@ -4164,6 +4187,30 @@ namespace AsbCloudDb.Migrations
Name = "На поверхности"
},
new
{
Id = 18,
Code = 0,
Name = "Проработка перед наращиванием"
},
new
{
Id = 19,
Code = 0,
Name = "Шаблонировка перед наращиванием"
},
new
{
Id = 20,
Code = 0,
Name = "Промывка перед наращиванием"
},
new
{
Id = 21,
Code = 0,
Name = "Статический замер телесистемы"
},
new
{
Id = 1001,
Code = 0,
@ -4721,7 +4768,7 @@ namespace AsbCloudDb.Migrations
b.HasKey("IdTelemetry", "DateTime");
b.ToTable("RecordBase", (string)null);
b.ToTable("t_telemetry_wits_base");
});
modelBuilder.Entity("AsbCloudDb.Model.WITS.Record1", b =>
@ -5787,6 +5834,17 @@ namespace AsbCloudDb.Migrations
b.Navigation("Company");
});
modelBuilder.Entity("AsbCloudDb.Model.UserSetting", b =>
{
b.HasOne("AsbCloudDb.Model.User", "User")
.WithMany()
.HasForeignKey("IdUser")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("AsbCloudDb.Model.Well", b =>
{
b.HasOne("AsbCloudDb.Model.Cluster", "Cluster")

View File

@ -38,6 +38,7 @@ namespace AsbCloudDb.Model
public virtual DbSet<TelemetryUser> TelemetryUsers => Set<TelemetryUser>();
public virtual DbSet<User> Users => Set<User>();
public virtual DbSet<UserRole> UserRoles => Set<UserRole>();
public virtual DbSet<UserSetting> UserSettings => Set<UserSetting>();
public virtual DbSet<Well> Wells => Set<Well>();
public virtual DbSet<WellComposite> WellComposites => Set<WellComposite>();
public virtual DbSet<WellOperation> WellOperations => Set<WellOperation>();
@ -56,6 +57,14 @@ namespace AsbCloudDb.Model
public DbSet<WITS.Record60> Record60 => Set<WITS.Record60>();
public DbSet<WITS.Record61> Record61 => Set<WITS.Record61>();
private System.Text.Json.JsonSerializerOptions jsonSerializerOptions = new()
{
AllowTrailingCommas = true,
WriteIndented = true,
NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString |
System.Text.Json.Serialization.JsonNumberHandling.AllowNamedFloatingPointLiterals,
};
public AsbCloudDbContext() : base()
{
}
@ -76,12 +85,47 @@ namespace AsbCloudDb.Model
modelBuilder.HasPostgresExtension("adminpack")
.HasAnnotation("Relational:Collation", "Russian_Russia.1251");
modelBuilder.Entity<Deposit>(entity =>
{
entity.Property(e => e.Timezone)
.HasJsonConversion();
});
modelBuilder.Entity<Cluster>(entity =>
{
entity.HasOne(d => d.Deposit)
.WithMany(p => p.Clusters)
.HasForeignKey(d => d.IdDeposit)
.HasConstraintName("t_cluster_t_deposit_id_fk");
entity.Property(e => e.Timezone)
.HasJsonConversion();
});
modelBuilder.Entity<Well>(entity =>
{
entity.HasOne(d => d.Cluster)
.WithMany(p => p.Wells)
.HasForeignKey(d => d.IdCluster)
.HasConstraintName("t_well_t_cluster_id_fk");
entity.HasOne(d => d.Telemetry)
.WithOne(p => p.Well)
.HasForeignKey<Well>(d => d.IdTelemetry)
.OnDelete(DeleteBehavior.SetNull)
.HasConstraintName("t_well_t_telemetry_id_fk");
entity.Property(e => e.Timezone)
.HasJsonConversion();
});
modelBuilder.Entity<Telemetry>(entity =>
{
entity.Property(e => e.Info)
.HasJsonConversion();
entity.Property(e => e.TimeZone)
.HasJsonConversion();
});
modelBuilder.Entity<WellComposite>(entity =>
@ -175,20 +219,6 @@ namespace AsbCloudDb.Model
.IsUnique();
});
modelBuilder.Entity<Well>(entity =>
{
entity.HasOne(d => d.Cluster)
.WithMany(p => p.Wells)
.HasForeignKey(d => d.IdCluster)
.HasConstraintName("t_well_t_cluster_id_fk");
entity.HasOne(d => d.Telemetry)
.WithOne(p => p.Well)
.HasForeignKey<Well>(d => d.IdTelemetry)
.OnDelete(DeleteBehavior.SetNull)
.HasConstraintName("t_well_t_telemetry_id_fk");
});
modelBuilder.Entity<RelationCompanyWell>(entity =>
{
entity.HasKey(nameof(RelationCompanyWell.IdCompany), nameof(RelationCompanyWell.IdWell));
@ -241,6 +271,18 @@ namespace AsbCloudDb.Model
.IsUnique();
});
modelBuilder.Entity<Measure>(entity =>
{
entity.Property(e => e.Data)
.HasJsonConversion();
});
modelBuilder.Entity<FileInfo>(entity =>
{
entity.Property(e => e.PublishInfo)
.HasJsonConversion();
});
modelBuilder.Entity<FileMark>(entity =>
{
entity.HasOne(d => d.User)
@ -278,6 +320,8 @@ namespace AsbCloudDb.Model
{
entity.HasKey(e => new { e.IdWell, e.StartDate })
.HasName("t_id_well_date_start_pk");
entity.Property(e => e.Info)
.HasJsonConversion();
});
modelBuilder.Entity<TelemetryDataSaubStat>(entity =>
@ -294,17 +338,23 @@ namespace AsbCloudDb.Model
.HasConstraintName("t_schedule_t_driller_id_driller");
});
modelBuilder.Entity<SetpointsRequest>(entity => {
entity.Property(e => e.Setpoints)
.HasJsonConversion();
});
FillData(modelBuilder);
}
public Task<int> RefreshMaterializedViewAsync<TEntity>(CancellationToken token = default)
public Task<int> RefreshMaterializedViewAsync<TEntity>(CancellationToken token)
where TEntity : class
{
var materializedViewName = Set<TEntity>().EntityType.GetViewName();
return RefreshMaterializedViewAsync(materializedViewName!, token);
var materializedViewName = Set<TEntity>().EntityType.GetViewName()
?? throw new System.Exception($"RefreshMaterializedViewAsync<{typeof(TEntity).Name}>(..) db table for this type does not found.");
return RefreshMaterializedViewAsync(materializedViewName, token);
}
public Task<int> RefreshMaterializedViewAsync(string materializedViewName, CancellationToken token = default)
public Task<int> RefreshMaterializedViewAsync(string materializedViewName, CancellationToken token)
{
var sql = $"REFRESH MATERIALIZED VIEW {materializedViewName};";
return Database.ExecuteSqlRawAsync(sql, token);
@ -632,6 +682,11 @@ namespace AsbCloudDb.Model
new WellOperationCategory {Id = 15, Name = "Неподвижное состояние", Code = 0 },
new WellOperationCategory {Id = 16, Name = "Вращение без циркуляции", Code = 0 },
new WellOperationCategory {Id = 17, Name = "На поверхности", Code = 0 },
new WellOperationCategory {Id = 18, Name = "Проработка перед наращиванием", Code = 0 },
new WellOperationCategory {Id = 19, Name = "Шаблонировка перед наращиванием", Code = 0 },
new WellOperationCategory {Id = 20, Name = "Промывка перед наращиванием", Code = 0 },
new WellOperationCategory {Id = 21, Name = "Статический замер телесистемы", Code = 0 },
// Операции ручного ввода
new WellOperationCategory {Id = 1001, Name = "Бурение", Code = 0 },
new WellOperationCategory {Id = 1002, Name = "ГИС", Code = 0 },
@ -783,6 +838,10 @@ namespace AsbCloudDb.Model
});
});
modelBuilder.Entity<UserSetting>(entity =>
{
entity.HasKey(nameof(UserSetting.IdUser), nameof(UserSetting.Key));
});
}
public Task<int> RefreshMaterializedViewAsync<TEntity>(string? mwName = null, CancellationToken token = default) where TEntity : class

View File

@ -56,8 +56,8 @@ namespace AsbCloudDb.Model
DatabaseFacade Database { get; }
Task<int> RefreshMaterializedViewAsync(string? mwName = null, CancellationToken token = default);
Task<int> RefreshMaterializedViewAsync<TEntity>(CancellationToken token = default) where TEntity : class;
Task<int> RefreshMaterializedViewAsync(string mwName, CancellationToken token);
Task<int> RefreshMaterializedViewAsync<TEntity>(CancellationToken token) where TEntity : class;
int SaveChanges();
int SaveChanges(bool acceptAllChangesOnSuccess);
Task<int> SaveChangesAsync(CancellationToken cancellationToken);

View File

@ -0,0 +1,24 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace AsbCloudDb.Model
{
[Table("t_user_settings"), Comment("настройки интерфейса пользователя")]
public class UserSetting
{
[Column("id_user")]
public int IdUser { get; set; }
[Column("key"), Comment("Ключ настроек пользователя"), StringLength(255)]
public string Key { get; set; } = null!;
[Column("setting_value", TypeName = "jsonb"), Comment("Значение настроек пользователя")]
public object? Value { get; set; }
[ForeignKey(nameof(IdUser))]
public User User { get; set; } = null!;
}
}

View File

@ -6,6 +6,7 @@ namespace AsbCloudDb.Model.WITS
/// <summary>
/// This is base class for all WITS-0 records
/// </summary>
[Table("t_telemetry_wits_base")]
public abstract class RecordBase : ITelemetryData
{
[Column("id_telemetry")]

View File

@ -2,6 +2,7 @@
using AsbCloudApp.Data.SAUB;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Repository;
using AsbCloudInfrastructure.Services;
using AsbCloudInfrastructure.Services.Cache;
using AsbCloudInfrastructure.Services.DailyReport;
@ -66,8 +67,6 @@ namespace AsbCloudInfrastructure
.ForType<ClusterDto, Cluster>()
.Ignore(dst => dst.Deposit,
dst => dst.Wells);
}
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
@ -118,6 +117,7 @@ namespace AsbCloudInfrastructure
services.AddTransient<IDrillerService, DrillerService>();
services.AddTransient<IScheduleService, ScheduleService>();
services.AddTransient<IOperationValueService, OperationValueService>();
services.AddTransient<IUserSettingsRepository, UserSettingsRepository>();
// admin crud services:
services.AddTransient<ICrudService<TelemetryDto>, CrudServiceBase<TelemetryDto, Telemetry>>(s =>

View File

@ -7,7 +7,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services
namespace AsbCloudInfrastructure.Repository
{
#nullable enable
/// <summary>
@ -17,7 +17,7 @@ namespace AsbCloudInfrastructure.Services
/// <typeparam name="TEntity"></typeparam>
public class CrudCacheServiceBase<TDto, TEntity> : CrudServiceBase<TDto, TEntity>
where TDto : AsbCloudApp.Data.IId
where TEntity : class, AsbCloudDb.Model.IId
where TEntity : class, IId
{
protected string CacheTag = typeof(TDto).Name;
protected TimeSpan CacheOlescence = TimeSpan.FromMinutes(5);
@ -53,9 +53,8 @@ namespace AsbCloudInfrastructure.Services
/// <inheritdoc/>
public override async Task<IEnumerable<TDto>> GetAllAsync(CancellationToken token)
{
var result = await GetQuery()
.FromCacheDictionaryAsync(CacheTag, CacheOlescence, KeySelector, Convert, token);
return result.Values;
var cache = await GetCacheAsync(token);
return cache.Values;
}
/// <summary>
@ -63,19 +62,17 @@ namespace AsbCloudInfrastructure.Services
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public override TDto? Get(int id)
public override TDto? GetOrDefault(int id)
{
var result = GetQuery()
.FromCacheDictionary(CacheTag, CacheOlescence, KeySelector, Convert);
return result.GetValueOrDefault(id);
var cache = GetCache();
return cache.GetValueOrDefault(id);
}
/// <inheritdoc/>
public override async Task<TDto?> GetAsync(int id, CancellationToken token)
public override async Task<TDto?> GetOrDefaultAsync(int id, CancellationToken token)
{
var result = await GetQuery()
.FromCacheDictionaryAsync(CacheTag, CacheOlescence, KeySelector, Convert, token);
return result.GetValueOrDefault(id);
var cache = await GetCacheAsync(token);
return cache.GetValueOrDefault(id);
}
/// <inheritdoc/>

View File

@ -8,9 +8,9 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services
namespace AsbCloudInfrastructure.Repository
{
#nullable enable
/// <summary>
/// CRUD сервис для работы с БД
/// </summary>
@ -18,7 +18,7 @@ namespace AsbCloudInfrastructure.Services
/// <typeparam name="TEntity"></typeparam>
public class CrudServiceBase<TDto, TEntity> : ICrudService<TDto>
where TDto : AsbCloudApp.Data.IId
where TEntity : class, AsbCloudDb.Model.IId
where TEntity : class, IId
{
protected readonly IAsbCloudDbContext dbContext;
protected readonly DbSet<TEntity> dbSet;
@ -26,7 +26,7 @@ namespace AsbCloudInfrastructure.Services
public CrudServiceBase(IAsbCloudDbContext context)
{
this.dbContext = context;
dbContext = context;
dbSet = context.Set<TEntity>();
GetQuery = () => dbSet;
}
@ -47,7 +47,7 @@ namespace AsbCloudInfrastructure.Services
public CrudServiceBase(IAsbCloudDbContext context, Func<DbSet<TEntity>, IQueryable<TEntity>> makeQuery)
{
this.dbContext = context;
dbContext = context;
dbSet = context.Set<TEntity>();
GetQuery = () => makeQuery(dbSet);
}
@ -65,7 +65,7 @@ namespace AsbCloudInfrastructure.Services
}
/// <inheritdoc/>
public virtual async Task<TDto?> GetAsync(int id, CancellationToken token = default)
public virtual async Task<TDto?> GetOrDefaultAsync(int id, CancellationToken token = default)
{
var entity = await GetQuery()
.AsNoTracking()
@ -78,7 +78,7 @@ namespace AsbCloudInfrastructure.Services
}
/// <inheritdoc/>
public virtual TDto? Get(int id)
public virtual TDto? GetOrDefault(int id)
{
var entity = GetQuery()
.AsNoTracking()

View File

@ -0,0 +1,51 @@
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Repository
{
#nullable enable
public class CrudWellRelatedCacheServiceBase<TDto, TEntity> : CrudCacheServiceBase<TDto, TEntity>, ICrudWellRelatedService<TDto>
where TDto : AsbCloudApp.Data.IId, AsbCloudApp.Data.IWellRelated
where TEntity : class, IId, IWellRelated
{
public CrudWellRelatedCacheServiceBase(IAsbCloudDbContext context)
: base(context) { }
public CrudWellRelatedCacheServiceBase(IAsbCloudDbContext dbContext, ISet<string> includes)
: base(dbContext, includes) { }
public CrudWellRelatedCacheServiceBase(IAsbCloudDbContext context, Func<DbSet<TEntity>, IQueryable<TEntity>> makeQuery)
: base(context, makeQuery) { }
public async Task<IEnumerable<TDto>> GetByIdWellAsync(int idWell, CancellationToken token)
{
var cache = await GetCacheAsync(token);
var dtos = cache.Values
.Where(e => e.IdWell == idWell)
.ToList();
return dtos;
}
public async Task<IEnumerable<TDto>> GetByIdWellAsync(IEnumerable<int> idsWells, CancellationToken token)
{
if (!idsWells.Any())
return Enumerable.Empty<TDto>();
var cache = await GetCacheAsync(token);
var dtos = cache.Values
.Where(e => idsWells.Contains(e.IdWell))
.ToList();
return dtos;
}
}
#nullable disable
}

View File

@ -7,12 +7,12 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services
namespace AsbCloudInfrastructure.Repository
{
#nullable enable
public class CrudWellRelatedServiceBase<TDto, TEntity> : CrudServiceBase<TDto, TEntity>, ICrudWellRelatedService<TDto>
where TDto : AsbCloudApp.Data.IId, AsbCloudApp.Data.IWellRelated
where TEntity : class, AsbCloudDb.Model.IId, AsbCloudDb.Model.IWellRelated
where TEntity : class, IId, IWellRelated
{
public CrudWellRelatedServiceBase(IAsbCloudDbContext context)
: base(context) { }

View File

@ -0,0 +1,36 @@
using AsbCloudApp.Data.SAUB;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using Microsoft.EntityFrameworkCore;
using System;
namespace AsbCloudInfrastructure.Repository
{
public class SetpointsRequestRepository : CrudWellRelatedCacheServiceBase<SetpointsRequestDto, SetpointsRequest>
{
private readonly IWellService wellService;
public SetpointsRequestRepository(IAsbCloudDbContext dbContext, IWellService wellService)
: base(dbContext, q => q.Include(s => s.Author)
.Include(s => s.Well))
{
this.wellService = wellService;
}
protected override SetpointsRequestDto Convert(SetpointsRequest src)
{
var result = base.Convert(src);
var timezoneOffsetHours = wellService.GetTimezone(src.IdWell).Hours;
result.UploadDate = src.UploadDate.ToRemoteDateTime(timezoneOffsetHours);
return result;
}
protected override SetpointsRequest Convert(SetpointsRequestDto src)
{
var result = base.Convert(src);
var timezoneOffsetHours = wellService.GetTimezone(src.IdWell).Hours;
result.UploadDate = src.UploadDate.ToUtcDateTimeOffset(timezoneOffsetHours);
return result;
}
}
}

View File

@ -0,0 +1,9 @@
# Repository
`Repository` - CRUD сервис для сущности в проекте. Не содержит бизнес логику.
Вся логика такого сервиса - преобразование данных полученых из БД в Data Transfer Object (DTO) и обратно.
Преобразования осуществляются методами `Convert` с базовым маппингом:
protected virtual TDto Convert(TEntity src) => src.Adapt<TDto>();
protected virtual TEntity Convert(TDto src) => src.Adapt<TEntity>();

View File

@ -51,7 +51,7 @@ namespace AsbCloudInfrastructure.Services
if (identity == default || user.IdState == 0)
return null;
var userDto = await userService.GetAsync(user.Id, token)
var userDto = await userService.GetOrDefaultAsync(user.Id, token)
.ConfigureAwait(false);
var userTokenDto = userDto.Adapt<UserTokenDto>();
@ -195,10 +195,13 @@ namespace AsbCloudInfrastructure.Services
private bool CheckPassword(string passwordHash, string password)
{
if (passwordHash.Length == 0 && password.Length == 0)
if (passwordHash?.Length == 0 && password.Length == 0)
return true;
if (passwordHash.Length < PasswordSaltLength)
if (passwordHash?.Length < PasswordSaltLength)
return false;
if (passwordHash is null)
return false;
var salt = passwordHash[0..PasswordSaltLength];

View File

@ -6,11 +6,11 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
{
public DateTimeOffset DateTime { get; set; }
public int? IdUser { get; set; }
public float? WellDepth { get; set; }
public float? Pressure { get; set; }
public float? HookWeight { get; set; }
public float? BlockPosition { get; set; }
public float? BitDepth { get; set; }
public float? RotorSpeed { get; set; }
public float WellDepth { get; set; }
public float Pressure { get; set; }
public float HookWeight { get; set; }
public float BlockPosition { get; set; }
public float BitDepth { get; set; }
public float RotorSpeed { get; set; }
}
}

View File

@ -33,9 +33,9 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
this.scheduleService = scheduleService;
}
public async Task<DetectedOperationListDto> GetAsync(int idWell, DetectedOperationRequest request, CancellationToken token)
public async Task<DetectedOperationListDto> GetAsync(DetectedOperationRequest request, CancellationToken token)
{
var well = await wellService.GetAsync(idWell, token);
var well = await wellService.GetOrDefaultAsync(request.IdWell, token);
if (well?.IdTelemetry is null || well.Timezone is null)
return null;
@ -44,8 +44,8 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
var data = await query.ToListAsync(token);
var operationValues = await operationValueService.GetByIdWellAsync(idWell, token);
var schedules = await scheduleService.GetByIdWellAsync(idWell, token);
var operationValues = await operationValueService.GetByIdWellAsync(request.IdWell, token);
var schedules = await scheduleService.GetByIdWellAsync(request.IdWell, token);
var dtos = data.Select(o => Convert(o, well, operationValues, schedules));
var groups = dtos.GroupBy(o => o.Driller);
@ -78,9 +78,9 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
return result;
}
public async Task<int> DeleteAsync(int idWell, DetectedOperationRequest request, CancellationToken token)
public async Task<int> DeleteAsync(DetectedOperationRequest request, CancellationToken token)
{
var well = await wellService.GetAsync(idWell, token);
var well = await wellService.GetOrDefaultAsync(request.IdWell, token);
if (well?.IdTelemetry is null || well.Timezone is null)
return 0;
@ -122,8 +122,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
if (request is not null)
{
if (request.CategoryIds is not null)
query = query.Where(o => request.CategoryIds.Contains(o.IdCategory));
query = query.Where(o => request.IdCategory == o.IdCategory);
if (request.GtDate is not null)
query = query.Where(o => o.DateStart >= request.GtDate.Value.ToUtcDateTimeOffset(well.Timezone.Hours));
@ -166,8 +165,9 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
var dateStart = operation.DateStart.ToRemoteDateTime(well.Timezone.Hours);
dto.DateStart = dateStart;
dto.DateEnd = operation.DateEnd.ToRemoteDateTime(well.Timezone.Hours);
dto.OperationValue = operationValues.FirstOrDefault(e => e.IdOperationCategory == dto.IdCategory
&& e.DepthStart <= dto.DepthStart);
dto.OperationValue = operationValues.FirstOrDefault(v => v.IdOperationCategory == dto.IdCategory
&& v.DepthStart <= dto.DepthStart
&& v.DepthEnd > dto.DepthStart);
var timeStart = new TimeDto(dateStart);
var driller = schedules.FirstOrDefault(s =>
@ -182,15 +182,32 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
return dto;
}
public async Task<IEnumerable<WellOperationCategoryDto>> GetCategoriesAsync(CancellationToken token)
public async Task<IEnumerable<WellOperationCategoryDto>> GetCategoriesAsync(int? idWell, CancellationToken token)
{
var result = await db.WellOperationCategories
IQueryable<WellOperationCategory> query = null;
if(idWell is null)
{
query = db.WellOperationCategories;
}
else
{
var well = await wellService.GetOrDefaultAsync((int )idWell, token);
if (well?.IdTelemetry is null)
return null;
var idTelemetry = (int)well.IdTelemetry;
query = db.DetectedOperations
.Include(o => o.OperationCategory)
.Where(o => o.IdTelemetry == idTelemetry)
.Select(o => o.OperationCategory)
.Distinct();
}
var result = await query
.Where(c => c.Id < 1000)
.AsNoTracking()
.Select(c => c.Adapt<WellOperationCategoryDto>())
.ToArrayAsync(token);
return result;
}
}
}

View File

@ -1,71 +1,307 @@
using AsbCloudDb.Model;
using System;
using System.Collections.Generic;
using System.Linq;
namespace AsbCloudInfrastructure.Services.DetectOperations
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
{
#nullable enable
abstract class DetectorAbstract
internal abstract class DetectorAbstract
{
public int StepLength { get; set; } = 3;
public int FragmentLength { get; set; } = 6;
public int IdCategory { get; }
private readonly int idOperation;
private readonly int stepLength = 3;
// TODO: assert MaxDurationSeconds and MinDurationSeconds
public double MaxDurationSeconds { get; } = 31 * 24 * 60 * 60;
public double MinDurationSeconds { get; } = 3;
/// <summary>
/// Конструктор детектора.
/// Словарь с IdCategory в дефолтных данных AsbCloudDbContext для таблицы WellOperationCategory
/// </summary>
/// <param name="IdCategory">ключ названия/описания операции из таблицы WellOperationCategory</param>
public DetectorAbstract(int IdCategory)
protected DetectorAbstract(int idOperation)
{
this.IdCategory = IdCategory;
this.idOperation = idOperation;
}
public virtual DetectedOperation? DetectOrDefault(DetectableTelemetry[] telemetry, ref int position)
public bool TryDetect(int idTelemetry, DetectableTelemetry[] telemetry, int begin, int end, DetectedOperation? previousOperation, out OperationDetectorResult? result)
{
if ((telemetry.Length > position + FragmentLength + StepLength) && DetectStart(telemetry, position))
// Проверка соответствия критерию начала операции
if (DetectBegin(telemetry, begin, previousOperation))
{
var skip = position + StepLength;
while (telemetry.Length > skip + FragmentLength)
// Поиск окончания соответствия критерию
var positionEnd = begin;
while (positionEnd < end)
{
if (DetectEnd(telemetry, skip))
positionEnd += stepLength;
if ((positionEnd > end))
break;
if (DetectEnd(telemetry, positionEnd, previousOperation))
{
var dateStart = telemetry[position].DateTime;
var dateEnd = telemetry[skip].DateTime;
var durationSec = (dateEnd - dateStart).TotalSeconds;
if (durationSec < MinDurationSeconds || durationSec > MaxDurationSeconds)
return null;
var result = new DetectedOperation
{
IdCategory = IdCategory,
IdUsersAtStart = telemetry[position].IdUser ?? -1,
DateStart = dateStart,
DateEnd = dateEnd,
DepthStart = telemetry[position].WellDepth ?? -1d,
DepthEnd = telemetry[skip].WellDepth ?? -1d,
};
CalcValue(ref result);
position = skip + FragmentLength;
return result;
result = MakeOperation(idTelemetry, telemetry, begin, positionEnd);
return true;
}
skip = skip + StepLength;
}
}
return null;
result = null;
return false;
}
protected abstract void CalcValue(ref DetectedOperation result);
protected abstract bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation);
protected virtual bool DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
=> !DetectBegin(telemetry, position, previousOperation);
protected abstract bool DetectStart(DetectableTelemetry[] telemetry, int position);
private OperationDetectorResult MakeOperation(int idTelemetry, DetectableTelemetry[] telemetry, int begin, int end)
{
var pBegin = telemetry[begin];
var pEnd = telemetry[end];
var result = new OperationDetectorResult
{
TelemetryBegin = begin,
TelemetryEnd = end,
Operation = new DetectedOperation
{
IdTelemetry = idTelemetry,
IdCategory = idOperation,
IdUsersAtStart = pBegin.IdUser ?? -1,
DateStart = pBegin.DateTime,
DateEnd = pEnd.DateTime,
DepthStart = (double)pBegin.WellDepth,
DepthEnd = (double)pEnd.WellDepth,
Value = CalcValue(telemetry, begin, end),
},
};
return result;
}
protected abstract bool DetectEnd(DetectableTelemetry[] telemetry, int position);
protected abstract bool IsValid(DetectableTelemetry[] telemetry, int begin, int end);
protected abstract double CalcValue(DetectableTelemetry[] telemetry, int begin, int end);
public static InterpolationLine MakeInterpolationLine(
Func<DetectableTelemetry, double> yGetter,
DetectableTelemetry[] telemetry,
int begin,
int fragmentLength)
{
DetectableTelemetry[] data;
if (fragmentLength > 0 && (begin + fragmentLength) < telemetry.Length)
data = telemetry[begin..(begin + fragmentLength)];
else
data = telemetry[begin..];
var line = new InterpolationLine(data.Select(d => (yGetter(d), (d.DateTime - telemetry[0].DateTime).TotalHours)));
return line;
}
/// <summary>
/// расчет продолжительности операции
/// </summary>
/// <param name="telemetry"></param>
/// <param name="begin"></param>
/// <param name="end"></param>
/// <returns></returns>
protected static double CalcDeltaMinutes(DetectableTelemetry[] telemetry, int begin, int end)
{
var pBegin = telemetry[begin];
var pEnd = telemetry[end];
var result = (pEnd.DateTime - pBegin.DateTime).TotalMinutes;
return result;
}
/// <summary>
/// часто используемый предикат для определения отсутствия изменения глубины ствола скважины
/// </summary>
/// <param name="telemetry"></param>
/// <param name="begin"></param>
/// <param name="end"></param>
/// <returns></returns>
protected static bool IsValidByWellDepthDoesNotChange(DetectableTelemetry[] telemetry, int begin, int end)
{
var pBegin = telemetry[begin];
var pEnd = telemetry[end];
if (Math.Abs((double)(pBegin.WellDepth - pEnd.WellDepth)) > 0.01)
return false;
return true;
}
protected static bool IsValidByWellDepthIncreasing(DetectableTelemetry[] telemetry, int begin, int end)
{
var pBegin = telemetry[begin];
var pEnd = telemetry[end];
if (pBegin.WellDepth >= pEnd.WellDepth)
return false;
return true;
}
protected static double CalcRop(DetectableTelemetry[] telemetry, int begin, int end)
{
var pBegin = telemetry[begin];
var pEnd = telemetry[end];
var result = (double)(pEnd.WellDepth - pBegin.WellDepth) / (pEnd.DateTime - pBegin.DateTime).TotalHours;
return result;
}
/// <summary>
/// Расчет статистики по массиву данных за интервал
/// </summary>
/// <param name="telemetry"></param>
/// <param name="getter"></param>
/// <param name="begin"></param>
/// <param name="count"></param>
/// <returns></returns>
protected static (double min, double max, double sum, int count) CalcStat(
DetectableTelemetry[] telemetry,
Func<DetectableTelemetry, double> getter,
int begin,
int count)
{
var sum = 0d;
var min = double.MaxValue;
var max = double.MinValue;
var end = begin + count;
end = end < telemetry.Length ? end : telemetry.Length;
for (var i = begin; i < end; i++)
{
var item = telemetry[i];
var itemValue = getter(item);
if (min > itemValue)
min = itemValue;
if (max < itemValue)
max = itemValue;
sum += itemValue;
}
return (min, max, sum, end - begin);
}
/// <summary>
/// Максимальное отклонение от среднего за интервал
/// </summary>
/// <param name="telemetry"></param>
/// <param name="getter"></param>
/// <param name="begin"></param>
/// <param name="count"></param>
/// <returns></returns>
protected static double CalcMaxDeviation(
DetectableTelemetry[] telemetry,
Func<DetectableTelemetry, double> getter,
int begin,
int count)
{
var stat = CalcStat(telemetry, getter, begin, count);
var avg = stat.sum / stat.count;
var dev1 = avg - stat.min;
var dev2 = stat.max - avg;
var dev = dev1 > dev2 ? dev1 : dev2;
return dev;
}
/// <summary>
/// Определяет наличие разброса значений в интервале большего указанного значения.
/// </summary>
/// <param name="telemetry"></param>
/// <param name="getter"></param>
/// <param name="begin"></param>
/// <param name="count"></param>
/// <param name="deviation"></param>
/// <returns></returns>
protected static bool ContainsDeviation(
DetectableTelemetry[] telemetry,
Func<DetectableTelemetry, double> getter,
int begin,
int count,
double deviation)
{
var min = double.MaxValue;
var max = double.MinValue;
var end = begin + count;
end = end < telemetry.Length ? end : telemetry.Length;
for (var i = begin; i < end; i ++)
{
var item = telemetry[i];
var itemValue = getter(item);
if (min > itemValue)
min = itemValue;
if (max < itemValue)
max = itemValue;
if(max - min > deviation)
return true;
}
return false;
}
/// <summary>
/// Определяет наличие разброса значений в интервале большего указанного значения. По нескольким значениям из интервала.
/// </summary>
/// <param name="telemetry"></param>
/// <param name="getter"></param>
/// <param name="begin"></param>
/// <param name="count"></param>
/// <param name="deviation"></param>
/// <returns></returns>
protected static bool ContainsDeviationApprox(
DetectableTelemetry[] telemetry,
Func<DetectableTelemetry, double> getter,
int begin,
int count,
double deviation)
{
var min = double.MaxValue;
var max = double.MinValue;
var end = begin + count;
end = end < telemetry.Length ? end : telemetry.Length;
var step = count > 15 ? count / 5 : count > 3 ? 3: 1;
for (var i = begin; i < end; i+= step)
{
var item = telemetry[i];
var itemValue = getter(item);
if (min > itemValue)
min = itemValue;
if (max < itemValue)
max = itemValue;
if (max - min > deviation)
return true;
}
return false;
}
protected static bool DeviatesFromBegin(
DetectableTelemetry[] telemetry,
Func<DetectableTelemetry, double> getter,
int begin,
int count,
double deviation)
{
var beginPointValue = getter(telemetry[begin]);
var end = begin + count;
end = end < telemetry.Length ? end : telemetry.Length;
var step = count > 15 ? count / 5 : count > 3 ? 3 : 1;
for (var i = begin; i < end; i += step)
{
var item = telemetry[i];
var itemValue = getter(item);
if (Math.Abs(beginPointValue - itemValue) > deviation)
return true;
}
return false;
}
protected static bool RisesFromBegin(
DetectableTelemetry[] telemetry,
Func<DetectableTelemetry, double> getter,
int begin,
int count,
double deviation)
{
var beginPointValue = getter(telemetry[begin]);
var end = begin + count;
end = end < telemetry.Length ? end : telemetry.Length;
var step = count > 15 ? count / 5 : count > 3 ? 3 : 1;
for (var i = begin; i < end; i += step)
{
var item = telemetry[i];
var itemValue = getter(item);
if ( itemValue - beginPointValue > deviation)
return true;
}
return false;
}
}
#nullable disable
}

View File

@ -0,0 +1,64 @@
using AsbCloudDb.Model;
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
{
#nullable enable
internal class DetectorDevelopment : DetectorAbstract
{
public DetectorDevelopment()
: base(18) { }
protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end)
=> CalcDeltaMinutes(telemetry, begin, end);
protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{
if (previousOperation?.IdCategory == 14)
return false;
var point0 = telemetry[position];
var delta = point0.WellDepth - point0.BitDepth;
if (delta < 0.03d || delta > 30)
return false;
if (point0.Pressure < 15)
return false;
if (point0.BlockPosition > 2.5)
return false;
if (point0.RotorSpeed < 10)
return false;
if (!ContainsDeviationApprox(telemetry, d => d.BlockPosition, position, 60, 0.03))
return false;
return true;
}
protected override bool DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{
var point0 = telemetry[position];
var delta = point0.WellDepth - point0.BitDepth;
if (delta < 0.03d || delta > 30)
return true;
if (point0.Pressure < 15)
return true;
if (point0.BlockPosition > 31)
return true;
if (point0.RotorSpeed < 10)
return true;
return false;
}
protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end)
=> IsValidByWellDepthDoesNotChange(telemetry, begin, end);
}
#nullable disable
}

View File

@ -1,53 +0,0 @@
using AsbCloudDb.Model;
using System.Linq;
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
{
#nullable enable
class DetectorDrillingRotor : DetectorAbstract
{
public DetectorDrillingRotor() : base(1)
{
FragmentLength = 15;
StepLength = 10;
}
protected override bool DetectStart(DetectableTelemetry[] telemetry, int position)
{
var firstItem = telemetry[position];
var delta = firstItem.WellDepth - firstItem.BitDepth;
if (delta is not null &&
System.Math.Abs((float)delta) > 1d)
return false;
var fragment = telemetry[position..(position + FragmentLength)];
const double minRop = 5; //м/час
const double minRotorSpeed = 5; //об/мин
const double ticksPerHour = 60 * 60 * 10_000_000d;
var lineBlockPosition = new InterpolationLine(fragment.Select(d => (d.BlockPosition ?? 0d, d.DateTime.Ticks / ticksPerHour)));
if (!lineBlockPosition.IsYDecreases(minRop))
return false;
var lineWellDepth = new InterpolationLine(fragment.Select(d => (d.WellDepth ?? 0d, d.DateTime.Ticks / ticksPerHour)));
if (!lineWellDepth.IsYIncreases(minRop))
return false;
var lineRotorSpeed = new InterpolationLine(fragment.Select(d => (d.RotorSpeed ?? 0d, d.DateTime.Ticks / ticksPerHour)));
if (!lineRotorSpeed.IsAverageYLessThanBound(minRotorSpeed))
return false;
return true;
}
protected override bool DetectEnd(DetectableTelemetry[] telemetry, int position)
=> !DetectStart(telemetry, position);
protected override void CalcValue(ref DetectedOperation result)
{
throw new System.NotImplementedException();
}
}
#nullable disable
}

View File

@ -1,52 +0,0 @@
using AsbCloudDb.Model;
using System.Linq;
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
{
#nullable enable
class DetectorDrillingSlide : DetectorAbstract
{
public DetectorDrillingSlide() : base(3)
{
FragmentLength = 10;
}
protected override bool DetectStart(DetectableTelemetry[] telemetry, int position)
{
var firstItem = telemetry[position];
var delta = firstItem.WellDepth - firstItem.BitDepth;
if (delta is not null &&
System.Math.Abs((float)delta) > 1d)
return false;
var fragment = telemetry[position..(position + FragmentLength)];
const double minRop = 5; //м/час
const double minRotorSpeed = 5; //об/мин
const double ticksPerHour = 60 * 60 * 10_000_000d;
var lineBlockPosition = new InterpolationLine(fragment.Select(d => (d.BlockPosition ?? 0d, d.DateTime.Ticks / ticksPerHour)));
if (!lineBlockPosition.IsYDecreases(minRop))
return false;
var lineWellDepth = new InterpolationLine(fragment.Select(d => (d.WellDepth ?? 0d, d.DateTime.Ticks / ticksPerHour)));
if (!lineWellDepth.IsYIncreases(minRop))
return false;
var lineRotorSpeed = new InterpolationLine(fragment.Select(d => (d.RotorSpeed ?? 0d, d.DateTime.Ticks / ticksPerHour)));
if (!lineRotorSpeed.IsAverageYMoreThanBound(minRotorSpeed))
return false;
return true;
}
protected override bool DetectEnd(DetectableTelemetry[] telemetry, int position)
=> !DetectStart(telemetry, position);
protected override void CalcValue(ref DetectedOperation result)
{
throw new System.NotImplementedException();
}
}
#nullable disable
}

View File

@ -0,0 +1,55 @@
using AsbCloudDb.Model;
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
{
/// <summary>
/// Промывка перед наращиванием
/// </summary>
internal class DetectorFlashing : DetectorAbstract
{
public DetectorFlashing()
: base(20) { }
protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end)
=> CalcDeltaMinutes(telemetry, begin, end);
protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{
if (!((previousOperation?.IdCategory == 2) ||
(previousOperation?.IdCategory == 3)))
return false;
var point0 = telemetry[position];
var delta = point0.WellDepth - point0.BitDepth;
if (delta > 0.05d)
return false;
if (point0.Pressure < 15)
return false;
if (point0.BlockPosition > 3)
return false;
return true;
}
protected override bool DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{
var point0 = telemetry[position];
var delta = point0.WellDepth - point0.BitDepth;
if ((delta > 0.03d )
&& (point0.Pressure > 15)
&& ContainsDeviationApprox(telemetry, t=>t.BlockPosition, position, 60, 0.03))
return true;
return false;
}
protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end)
=> IsValidByWellDepthDoesNotChange(telemetry, begin, end);
}
#nullable disable
}

View File

@ -0,0 +1,61 @@
using AsbCloudDb.Model;
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
{
#nullable enable
internal class DetectorRotor : DetectorAbstract
{
public DetectorRotor()
: base(2) { }
protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{
var point0 = telemetry[position];
var delta = point0.WellDepth - point0.BitDepth;
if (delta > 0.03d)
return false;
if (point0.Pressure < 25)
return false;
if (point0.RotorSpeed < 5)
return false;
var point1 = telemetry[position + 1];
if (point1.WellDepth - point0.WellDepth <= 0.003)
return false;
return true;
}
protected override bool DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{
var point0 = telemetry[position];
var delta = point0.WellDepth - point0.BitDepth;
if (delta > 0.03d)
return true;
if (point0.Pressure < 25)
return true;
var lineRotorSpeed = MakeInterpolationLine(d => d.RotorSpeed, telemetry, position, 10);
if (lineRotorSpeed.IsAverageYLessThan(5))
return true;
if (!DeviatesFromBegin(telemetry, t => t.WellDepth, position, 60, 0.003))
return true;
return false;
}
protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end)
=> IsValidByWellDepthIncreasing(telemetry, begin, end);
protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end)
=> CalcRop(telemetry, begin, end);
}
#nullable disable
}

View File

@ -0,0 +1,61 @@
using AsbCloudDb.Model;
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
{
#nullable enable
internal class DetectorSlide : DetectorAbstract
{
public DetectorSlide()
: base(3) { }
protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{
var point0 = telemetry[position];
var delta = point0.WellDepth - point0.BitDepth;
if (delta > 0.03d)
return false;
if (point0.Pressure < 25)
return false;
if (point0.RotorSpeed > 5)
return false;
var point1 = telemetry[position + 1];
if (point1.WellDepth - point0.WellDepth <= 0.003)
return false;
return true;
}
protected override bool DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{
var point0 = telemetry[position];
var delta = point0.WellDepth - point0.BitDepth;
if (delta > 0.03d)
return true;
if (point0.Pressure < 25)
return true;
var lineRotorSpeed = MakeInterpolationLine(d => d.RotorSpeed, telemetry, position, 10);
if (lineRotorSpeed.IsAverageYGreaterThan(5))
return true;
if (!DeviatesFromBegin(telemetry, t => t.WellDepth, position, 60, 0.003))
return true;
return false;
}
protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end)
=> IsValidByWellDepthIncreasing(telemetry, begin, end);
protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end)
=> CalcRop(telemetry, begin, end);
}
#nullable disable
}

View File

@ -3,41 +3,37 @@
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
{
#nullable enable
class DetectorSlipsTime : DetectorAbstract
internal class DetectorSlipsTime : DetectorAbstract
{
public DetectorSlipsTime() : base(14) { }
public double HookWeightSP { get; set; } = 20;
public double PressureSP { get; set; } = 15;
public double PosisionSP { get; set; } = 8;
public double DeltaWellDepthMax { get; set; } = 2.5;
public DetectorSlipsTime()
: base(14) { }
protected override bool DetectStart(DetectableTelemetry[] telemetry, int position)
protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end)
=> CalcDeltaMinutes(telemetry, begin, end);
protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{
var item = telemetry[position];
var point0 = telemetry[position];
var delta = point0.WellDepth - point0.BitDepth;
if (delta > 2.5d)
return false;
var result =
item.HookWeight < HookWeightSP &&
item.Pressure < PressureSP &&
item.BlockPosition < PosisionSP &&
(item.WellDepth - item.BitDepth) < DeltaWellDepthMax;
if (point0.Pressure > 15)
return false;
return result;
if (point0.BlockPosition > 8)
return false;
if (point0.HookWeight > 20)
return false;
return true;
}
protected override bool DetectEnd(DetectableTelemetry[] telemetry, int position)
{
var item = telemetry[position];
var result = item.Pressure > PressureSP &&
item.BlockPosition > PosisionSP;
return result;
}
protected override void CalcValue(ref DetectedOperation result)
{
result.Value = result.DurationMinutes;
}
protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end)
=> IsValidByWellDepthDoesNotChange(telemetry, begin, end);
}
#nullable disable
}

View File

@ -0,0 +1,70 @@
using AsbCloudDb.Model;
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
{
#nullable enable
/// <summary>
/// Статический замер телесистемы
/// </summary>
internal class DetectorStaticSurveying: DetectorAbstract
{
public DetectorStaticSurveying()
: base(21) { }
protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{
var point0 = telemetry[position];
if (point0.Pressure < 15)
return false;
var delta = point0.WellDepth - point0.BitDepth;
if (delta > 2.5d)
return false;
if (point0.RotorSpeed > 15)
return false;
if (ContainsDeviation(telemetry, t => t.BlockPosition, position, 60, 0.03))
return false;
if (ContainsDeviation(telemetry, t => t.Pressure, position, 60, 10))
return false;
return true;
}
protected override bool DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{
var point0 = telemetry[position];
var delta = point0.WellDepth - point0.BitDepth;
if (delta > 2.5d)
return true;
if (point0.RotorSpeed > 15)
return true;
if (RisesFromBegin(telemetry, t => t.Pressure, position, 10, 10))
return true;
if (ContainsDeviation(telemetry, t => t.BlockPosition, position, 10, 0.03))
return false;
//if (DeviatesFromBegin(telemetry, t => t.Pressure, position, 60, 10))
// return false;
return false;
}
protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end)
=> IsValidByWellDepthDoesNotChange(telemetry, begin, end);
protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end)
=> CalcDeltaMinutes(telemetry, begin, end);
}
#nullable disable
}

View File

@ -0,0 +1,65 @@
using AsbCloudDb.Model;
using System.Collections.Generic;
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
{
#nullable enable
internal class DetectorTemplating : DetectorAbstract
{
public DetectorTemplating()
: base(19) { }
protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end)
=> CalcDeltaMinutes(telemetry, begin, end);
protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{
if(previousOperation?.IdCategory == 14)
return false;
var point0 = telemetry[position];
var delta = point0.WellDepth - point0.BitDepth;
if (delta < 0.03d || delta > 30)
return false;
if (point0.Pressure < 15)
return false;
if (point0.BlockPosition > 2.5)
return false;
if (point0.RotorSpeed > 10)
return false;
if (!ContainsDeviationApprox(telemetry, d => d.BlockPosition, position, 60, 0.03))
return false;
return true;
}
protected override bool DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{
var point0 = telemetry[position];
var delta = point0.WellDepth - point0.BitDepth;
if (delta < 0.03d || delta > 30)
return true;
if (point0.Pressure < 15)
return true;
if (point0.BlockPosition > 31)
return true;
if (point0.RotorSpeed > 10)
return true;
return false;
}
protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end)
=> IsValidByWellDepthDoesNotChange(telemetry, begin, end);
}
#nullable disable
}

View File

@ -0,0 +1,11 @@
using AsbCloudDb.Model;
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
{
class OperationDetectorResult
{
public int TelemetryBegin { get; set; }
public int TelemetryEnd { get; set; }
public DetectedOperation Operation { get; set; }
}
}

View File

@ -46,10 +46,10 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
public bool IsYDecreases(double bound = 0d) =>
A < bound;
public bool IsAverageYLessThanBound(double bound) =>
public bool IsAverageYLessThan(double bound) =>
(ySum / count) < bound;
public bool IsAverageYMoreThanBound(double bound) =>
public bool IsAverageYGreaterThan(double bound) =>
(ySum / count) >= bound;
}
}

View File

@ -8,31 +8,29 @@ using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudInfrastructure.Services.DetectOperations.Detectors;
namespace AsbCloudInfrastructure.Services.DetectOperations
{
#nullable enable
public class OperationDetectionBackgroundService : BackgroundService
{
private readonly IEnumerable<DetectorAbstract> detectors = new List<DetectorAbstract>
{
new Detectors.DetectorSlipsTime(),
// new Detectors.DetectorDrillingRotor(),
// new Detectors.DetectorDrillingSlide(),
};
private readonly int minStepLength;
private readonly int minFragmentLength;
private readonly string connectionString;
private readonly TimeSpan period = TimeSpan.FromHours(1);
private static readonly DetectorAbstract[] detectors = new DetectorAbstract[]
{
new DetectorRotor(),
new DetectorSlide(),
new DetectorDevelopment(),
new DetectorTemplating(),
new DetectorSlipsTime(),
//new DetectorStaticSurveying(),
new DetectorFlashing(),
};
public OperationDetectionBackgroundService(IConfiguration configuration)
{
minStepLength = detectors.Min(d => d.StepLength);
minStepLength = minStepLength > 0 ? minStepLength : 3;
minFragmentLength = detectors.Min(d => d.FragmentLength);
minFragmentLength = minFragmentLength > 0 ? minFragmentLength : 6;
connectionString = configuration.GetConnectionString("DefaultConnection");
}
@ -72,7 +70,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
await base.StopAsync(token).ConfigureAwait(false);
}
private async Task<int> DetectedAllTelemetriesAsync(IAsbCloudDbContext db, CancellationToken token)
private static async Task<int> DetectedAllTelemetriesAsync(IAsbCloudDbContext db, CancellationToken token)
{
var lastDetectedDates = await db.DetectedOperations
.GroupBy(o => o.IdTelemetry)
@ -95,12 +93,14 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
(outer, inner) => new
{
IdTelemetry = outer,
LastDate = inner.SingleOrDefault()?.LastDate,
inner.SingleOrDefault()?.LastDate,
});
var affected = 0;
foreach (var item in JounedlastDetectedDates)
{
var stopwatch = Stopwatch.StartNew();
var newOperations = await DetectOperationsAsync(item.IdTelemetry, item.LastDate ?? DateTimeOffset.MinValue, db, token);
stopwatch.Stop();
if (newOperations.Any())
{
db.DetectedOperations.AddRange(newOperations);
@ -110,7 +110,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
return affected;
}
private async Task<IEnumerable<DetectedOperation>> DetectOperationsAsync(int idTelemetry, DateTimeOffset begin, IAsbCloudDbContext db, CancellationToken token)
private static async Task<IEnumerable<DetectedOperation>> DetectOperationsAsync(int idTelemetry, DateTimeOffset begin, IAsbCloudDbContext db, CancellationToken token)
{
var query = db.TelemetryDataSaub
.AsNoTracking()
@ -119,83 +119,60 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
{
DateTime = d.DateTime,
IdUser = d.IdUser,
WellDepth = d.WellDepth,
Pressure = d.Pressure,
HookWeight = d.HookWeight,
BlockPosition = d.BlockPosition,
BitDepth = d.BitDepth,
RotorSpeed = d.RotorSpeed,
WellDepth = d.WellDepth ?? float.NaN,
Pressure = d.Pressure ?? float.NaN,
HookWeight = d.HookWeight ?? float.NaN,
BlockPosition = d.BlockPosition ?? float.NaN,
BitDepth = d.BitDepth ?? float.NaN,
RotorSpeed = d.RotorSpeed ?? float.NaN,
})
.OrderBy(d => d.DateTime);
var take = 4 * 86_400;
var take = 4 * 86_400; // 4 дня
var startDate = begin;
var detectedOperations = new List<DetectedOperation>(8);
var dbRequests_ = 0;
var dbTime_ = 0d;
var sw_ = new Stopwatch();
var otherTime_ = 0d;
DetectedOperation? lastDetectedOperation = null;
const int minOperationLength = 5;
const int maxDetectorsInterpolationFrameLength = 30;
const int gap = maxDetectorsInterpolationFrameLength + minOperationLength;
while (true)
{
sw_.Restart();
var data = await query
.Where(d => d.DateTime > startDate)
.Take(take)
.ToArrayAsync(token);
sw_.Stop();
dbTime_ += sw_.ElapsedMilliseconds;
dbRequests_++;
sw_.Restart();
if (data.Length < minFragmentLength)
if (data.Length < gap)
break;
var skip = 0;
var isDetected = false;
while (data.Length > skip + minFragmentLength)
var positionBegin = 0;
var positionEnd = data.Length - gap;
while (positionEnd > positionBegin)
{
var isDetected1 = false;
foreach (var detector in detectors)
for (int i = 0; i < detectors.Length; i++)
{
if (data.Length < skip + detector.StepLength + detector.FragmentLength)
continue;
var detectedOperation = detector.DetectOrDefault(data, ref skip);
if (detectedOperation is not null)
if (detectors[i].TryDetect(idTelemetry, data, positionBegin, positionEnd, lastDetectedOperation, out OperationDetectorResult? result))
{
isDetected1 = true;
detectedOperations.Add(result!.Operation);
lastDetectedOperation = result.Operation;
isDetected = true;
detectedOperation.IdTelemetry = idTelemetry;
detectedOperations.Add(detectedOperation);
startDate = detectedOperation.DateEnd;
positionBegin = result.TelemetryEnd;
break;
}
}
if (!isDetected1)
skip += minStepLength;
positionBegin++;
}
sw_.Stop();
otherTime_ += sw_.ElapsedMilliseconds;
if (!isDetected)
{
if (data.Length < take)
break;
var lastPartDate = data.Last().DateTime;
startDate = startDate + (0.75 * (lastPartDate - startDate));
}
if (isDetected)
startDate = lastDetectedOperation!.DateEnd;
else
startDate = data[positionEnd].DateTime;
}
return detectedOperations;
}
}
#nullable disable
}

View File

@ -0,0 +1,22 @@
# Алгоритм определения бурения в роторе
## Описание
## Метод определения бурения в роторе
Признак начала операции =
( расстояние от долота до забоя < 0.03м ) И
( давление > 25атм ) И
( глубина забоя за следующую секунду больше текущей на 0.003м ) И
( обороты ротора > 5 об/м );
Признак окончания операции =
( расстояние от долота до забоя > 0.03м ) ИЛИ
( давление < 25атм ) ИЛИ
( среднее арифметическое оборотов ротора за 10 сек < 5 об/м ) ИЛИ
( глубина забоя в течении следующих 60 сек не изменяется больше чем на 0.003 );
## Метод определения бурения в слайде
Повторяет метод определения бурения в роторе, за исключением условия с оборотами ротора. Это уловие нужно инвертировать.
## Ключевой параметр
МСП = разность глубины забоя на конец и начало операции / продолжительность операции.

View File

@ -1,44 +0,0 @@
> Из писма Гранова А.П. от 19.04.2022 13:29 "Алгоритм определения наращивания БИ.docx"
# Алгоритм определения времени в клиньях
## Описание:
Наращивание бурильного инструмента операция, во время которой после добуривания очередной трубы/ свечи
циркуляция выключается, инструмент разгружается в клиньях (остается только вес крюкоблока и ВСП),
происходит развинчивание трубы от верхнего силового привода, берется очередная труба/ свеча,
свинчивается с инструментом в клиньях, свинчивается с верхним силовым приводом, происходит подъем инструмента,
вес на крюке увеличивается. Далее включается циркуляция и происходит механическое бурение.
Наращиванию предшествует механическое бурение (наличие давления, увеличение глубины забоя) и
после наращивания механическое бурение возобновляется (наличие давления, Увеличение глубины забоя).
> Это не учитывать в методе, так как предыдущая и последующая операция могут быть определены не корректно.
Наращивается определяется как время между:
- разгрузкой инструмента на клинья (остается только вес крюкоблока и ВСП).
При этом давление менее 15 атм. В случае давления более 15 атм считать началом операции как
снижение давления менее 15 атм и началом движения талевого блока вверх.
- снятие инструмента с клиньев (вес увеличивается более, чем на 1т).
- При этом движение талевого блока происходит вверх.
## Метод определения:
> Исправлено на совещании от 19.04.2022 16:50
> Исправлено задачей в кайтен от 12.05.2022 16:49
считать время в клиньях только при соотношении глубина забоя - глубина долота меньше 2,5 метра
```
Признак начала операции =
(параметр «вес на крюке» < 22 тонн) И
(давление < 15 атм) И
(положение талевого блока < 8) И
(глубина забоя - глубина долота < 2,5)
Признак окончания операции =
(вес на крюке > 22 ) И
(давление > 15 атм)
```

View File

@ -0,0 +1,26 @@
# Алгоритм определения промывки перед проработкой/ шаблонировкой перед наращиванием
## Описание
Промывка перед проработкой/ шаблонировкой перед наращиванием операция, во время которой после добуривания очередной трубы происходит снижение осевой нагрузки и дифференциального давления, талевый блок остается условно неподвижным.
Проработка перед наращиванием определяется как время между:
- окончанием операции бурения (ротор/ слайд/ ручное бурение)
- началом операции проработки/ шаблонировки перед наращивании
## Метод определения
Признак начала операции =
( предыдущая операция == бурение в роторе или слайде)
( расстояние от долота до забоя < 0,05м ) И
( давление > 15 атм ) И
( положение блока < )
Признак окончания операции =
( расстояние от долота до забоя > 0.03м ) И
( давление > 15 атм ) И
( высота блока изменяется больше чем на 0.03м в течении 60 сек с начала операции);
## Ключевой параметр
Продолжительность операции.

View File

@ -0,0 +1,31 @@
# Алгоритм определения шаблонировки перед наращиванием
## Описание
Проработка перед наращиванием бурильного инструмента операция, во время которой после добуривания очередной трубы/ свечи начинается подъем и спуск бурильного инструмента с вращением. Следующей операцией после проработки будет либо шаблонировка (аналогично проработке, но без вращения), либо разгрузка инструмента в клинья (снижение веса на крюке) - наращивание
Проработка перед наращиванием определяется как время между:
- начало подъема/ спуска бурильного инструмента с вращением
- разгрузкой инструмента на клинья (остается только вес крюкоблока и ВСП). При этом давление менее 15 атм. ЛИБО
- начало подъема/ спуска бурильного инструмента БЕЗ вращения
- считать время на проработку только при соотношении глубина забоя - глубина долота не больше меньше 30 метров
## Метод определения
Признак начала операции =
( предыдущая операция НЕ удержание в клиньях) И
( расстояние от долота до забоя > 0.03м ) И
( расстояние от долота до забоя < 30м ) И
( давление > 15 атм ) И
( положение блока < 2.5м ) И
( обороты ротора > 10 об/м ) И
( высота блока изменяется больше чем на 0.03м в течении 60 сек с начала операции);
Признак окончания операции =
( расстояние от долота до забоя < 0.03м ) ИЛИ
( расстояние от долота до забоя < 30м ) ИЛИ
( давление < 15 атм ) ИЛИ
( положение блока > 31м ) ИЛИ
( обороты ротора < 10 об/м );
## Ключевой параметр
Продолжительность операции.

View File

@ -0,0 +1,21 @@
# Статический замер телесистемы
Статический замер телесистемы перед бурением - операция после удержания в клиньях и наращивания очередной трубы/ свечи.
Замер ТС (статический замер) при бурении свечами (промежуточный замер) - операция при добуривании одной трубки, без наращивании и удержания в клиньях.
## метод определения 1
Признак начала операции =
( давление > 15 атм ) И
( расстояние от долота до забоя < 2.5м ) И
( обороты ротора < 15 об/м ) И
( движение тал.блока в течении 60 сек изменяется менее чем на 3 см ) И
( давление за следующие 60 сек изменяется менее чем на 10 атм )
Признак окончания операции =
( расстояние от долота до забоя > 2.5м ) ИЛИ
( обороты ротора > 15 об/м ) ИЛИ
( давление за следующие 10 сек вырастет на 10 атм ) ИЛИ
( движение тал.блока в течении 60 сек изменяется более чем на 3 см )
## Ключевой параметр
Продолжительность операции.

View File

@ -0,0 +1,21 @@
# Алгоритм определения времени в клиньях
## Описание
Наращивание бурильного инструмента операция, во время которой после добуривания очередной трубы/ свечи циркуляция выключается, инструмент разгружается в клиньях (остается только вес крюкоблока и ВСП), происходит развинчивание трубы от верхнего силового привода, берется очередная труба/ свеча, свинчивается с инструментом в клиньях, свинчивается с верхним силовым приводом, происходит подъем инструмента, вес на крюке увеличивается. Далее включается циркуляция и происходит механическое бурение.
Наращивается определяется как время между:
- разгрузкой инструмента на клинья (остается только вес крюкоблока и ВСП). При этом давление менее 15 атм. В случае давления более 15 атм считать началом операции как снижение давления менее 15 атм и началом движения талевого блока вверх.
- снятие инструмента с клиньев (вес увеличивается более, чем на 1т). При этом движение талевого блока происходит вверх.
## Метод определения
Признак начала операции =
( расстояние от долота до забоя < 2.5м ) И
( положение талевого блока < 8 ) И
( вес на крюке < 20 тонн ) И
( давление < 15 атм );
Признак окончания операции = НЕ выполняется признак начала операции;
## Ключевой параметр
Продолжительность операции.

View File

@ -0,0 +1,31 @@
# Алгоритм определения шаблонировки перед наращиванием
## Описание
Шаблонировкаперед наращиванием бурильного инструмента операция, во время которой после добуривания очередной трубы/ свечи начинается подъем и спуск бурильного инструмента БЕЗ вращения. Следующей операцией после шаблонировки будет либо проработка (аналогично шаблонировке, но С вращением), либо разгрузка инструмента в клинья (снижение веса на крюке) - наращивание
Шаблонировка перед наращиванием определяется как время между:
- начало подъема/ спуска бурильного инструмента БЕЗ вращения
- разгрузкой инструмента на клинья (остается только вес крюкоблока и ВСП). При этом давление менее 15 атм. ЛИБО
- начало подъема/ спуска бурильного инструмента БЕЗ вращения
- считать время на шаблонировку только при соотношении глубина забоя - глубина долота не больше меньше 30 метров
## Метод определения
Признак начала операции =
( предыдущая операция НЕ удержание в клиньях) И
( расстояние от долота до забоя > 0.03м ) И
( расстояние от долота до забоя < 30м ) И
( давление > 15 атм ) И
( положение блока < 2.5м ) И
( обороты ротора < 10 об/м ) И
( высота блока изменяется больше чем на 0.03м в течении 60 сек с начала операции);
Признак окончания операции =
( расстояние от долота до забоя < 0.03м ) ИЛИ
( расстояние от долота до забоя < 30м ) ИЛИ
( давление < 15 атм ) ИЛИ
( положение блока > 31м ) ИЛИ
( обороты ротора > 10 об/м );
## Ключевой параметр
Продолжительность операции.

View File

@ -1,6 +1,7 @@
using AsbCloudApp.Data;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Repository;
using Mapster;
using Microsoft.EntityFrameworkCore;
using System;

View File

@ -1,6 +1,7 @@
using AsbCloudApp.Data;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Repository;
using Mapster;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;

View File

@ -1,6 +1,7 @@
using AsbCloudApp.Data;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Repository;
namespace AsbCloudInfrastructure.Services
{

View File

@ -0,0 +1,41 @@
using AsbCloudApp.Data;
using ClosedXML.Excel;
using System;
using System.Collections.Generic;
using System.Linq;
namespace AsbCloudInfrastructure.Services.DrillingProgram
{
#nullable enable
public class ContentListSheet
{
private readonly List<DrillingProgramPartDto> parts;
public ContentListSheet(IEnumerable<DrillingProgramPartDto> parts)
{
this.parts = parts.ToList();
}
public void Draw(IXLWorksheet sheet)
{
sheet.Style.Font.FontName = "Calibri";
sheet.Style.Font.FontSize = 12;
sheet.Cell(2, 2)
.SetValue("Содержание")
.Style
.Font.SetBold(true)
.Font.SetFontSize(14);
for (int i = 0; i < parts.Count; i++)
{
sheet.Cell(4 + i, 1)
.SetValue(i + 1);
sheet.Cell(4 + i, 2)
.SetValue(parts[i].Name);
}
}
}
#nullable disable
}

View File

@ -10,9 +10,18 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
{
private const int maxAllowedColumns = 256;
public static void UniteExcelFiles(IEnumerable<string> excelFilesNames, string resultExcelPath)
public static void UniteExcelFiles(IEnumerable<string> excelFilesNames, string resultExcelPath, IEnumerable<AsbCloudApp.Data.DrillingProgramPartDto> parts, AsbCloudApp.Data.WellDto well)
{
var resultExcelFile = new XLWorkbook(XLEventTracking.Disabled);
var titleSheet = resultExcelFile.AddWorksheet("Титульный лист");
var marks = parts.SelectMany(p => p.File.FileMarks);
var titleSheetMaker = new TitleListSheet(marks, well);
titleSheetMaker.Draw(titleSheet);
var contentSheet = resultExcelFile.AddWorksheet("Содержание");
var contentListSheetMaker = new ContentListSheet(parts);
contentListSheetMaker.Draw(contentSheet);
var filteredFileNames = excelFilesNames.Distinct();

View File

@ -109,6 +109,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
var partEntities = await context.DrillingProgramParts
.Include(p => p.RelatedUsers)
.ThenInclude(r => r.User)
.ThenInclude(u => u.Company)
.Where(p => p.IdWell == idWell)
.ToListAsync(token);
@ -224,7 +225,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
public async Task<int> AddUserAsync(int idWell, int idFileCategory, int idUser, int idUserRole, CancellationToken token = default)
{
var user = await userService.GetAsync(idUser, token);
var user = await userService.GetOrDefaultAsync(idUser, token);
if (user is null)
throw new ArgumentInvalidException($"User id == {idUser} does not exist", nameof(idUser));
@ -356,7 +357,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
private async Task NotifyPublisherOnFullAccepAsync(FileMarkDto fileMark, CancellationToken token)
{
var file = await fileService.GetInfoAsync(fileMark.IdFile, token);
var well = await wellService.GetAsync(file.IdWell, token);
var well = await wellService.GetOrDefaultAsync(file.IdWell, token);
var user = file.Author;
var factory = new MailBodyFactory(configuration);
var subject = MailBodyFactory.MakeSubject(well, "Загруженный вами документ полностью согласован");
@ -368,7 +369,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
private async Task NotifyPublisherOnRejectAsync(FileMarkDto fileMark, CancellationToken token)
{
var file = await fileService.GetInfoAsync(fileMark.IdFile, token);
var well = await wellService.GetAsync(file.IdWell, token);
var well = await wellService.GetOrDefaultAsync(file.IdWell, token);
var user = file.Author;
var factory = new MailBodyFactory(configuration);
var subject = MailBodyFactory.MakeSubject(well, "Загруженный вами документ отклонен");
@ -379,7 +380,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
private async Task NotifyApproversAsync(DrillingProgramPart part, int idFile, string fileName, CancellationToken token)
{
var well = await wellService.GetAsync(part.IdWell, token);
var well = await wellService.GetOrDefaultAsync(part.IdWell, token);
var factory = new MailBodyFactory(configuration);
var subject = MailBodyFactory.MakeSubject(well, "Загружен новый документ для согласования.");
var users = part.RelatedUsers
@ -395,7 +396,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
private async Task NotifyNewPublisherAsync(int idWell, UserDto user, string documentCategory, CancellationToken token)
{
var well = await wellService.GetAsync(idWell, token);
var well = await wellService.GetOrDefaultAsync(idWell, token);
var factory = new MailBodyFactory(configuration);
var subject = MailBodyFactory.MakeSubject(well, $"От вас ожидается загрузка на портал документа «{documentCategory}»");
var body = factory.MakeMailBodyForNewPublisher(well, user.Name, documentCategory);
@ -469,7 +470,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
var workId = MakeWorkId(idWell);
if (!backgroundWorker.Contains(workId))
{
var well = await wellService.GetAsync(idWell, token);
var well = await wellService.GetOrDefaultAsync(idWell, token);
var resultFileName = $"Программа бурения {well.Cluster} {well.Caption}.xlsx";
var tempResultFilePath = Path.Combine(Path.GetTempPath(), "drillingProgram", resultFileName);
var mailService = new EmailService(backgroundWorker, configuration);
@ -481,7 +482,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
using var context = new AsbCloudDbContext(contextOptions);
var fileService = new FileService(context);
var files = state.Parts.Select(p => fileService.GetUrl(p.File));
DrillingProgramMaker.UniteExcelFiles(files, tempResultFilePath);
DrillingProgramMaker.UniteExcelFiles(files, tempResultFilePath, state.Parts, well);
await fileService.MoveAsync(idWell, null, idFileCategoryDrillingProgram, resultFileName, tempResultFilePath, token);
}

View File

@ -0,0 +1,160 @@
using AsbCloudApp.Data;
using ClosedXML.Excel;
using System;
using System.Collections.Generic;
using System.Linq;
namespace AsbCloudInfrastructure.Services.DrillingProgram
{
#nullable enable
public class TitleListSheet
{
private const string directionDirectorPositionName = "Руководитель направления по ТСБ";
private readonly DateTime totalDate;
private readonly FileMarkDto? acceptDirectionDirector;
private readonly List<FileMarkDto> acceptsOthers;
private readonly WellDto well;
public TitleListSheet(IEnumerable<FileMarkDto> fileMarks, WellDto well)
{
totalDate = fileMarks.Max(f => f.DateCreated);
acceptDirectionDirector = fileMarks
.OrderByDescending(f => f.DateCreated)
.FirstOrDefault(f => f.User.Position == directionDirectorPositionName);
acceptsOthers = fileMarks
.Where(f => f.Id != acceptDirectionDirector?.Id)
.OrderBy(f => f.DateCreated)
.ToList();
this.well = well;
}
public void Draw(IXLWorksheet sheet)
{
const double santimetr = 0.393701;
sheet.Style.Font.FontName = "Calibri";
sheet.Style.Font.FontSize = 12;
sheet.PageSetup.PaperSize = XLPaperSize.A4Paper;
sheet.PageSetup.Margins
.SetTop(santimetr)
.SetLeft(2 * santimetr)
.SetBottom(santimetr)
.SetRight(santimetr);
DrawTopRightSign(sheet);
DrawMainTilte(sheet);
DrawOtherAcceptors(sheet);
sheet.Range(51, 1, 51, 5)
.Merge()
.SetValue($"{totalDate.Year:00}")
.Style.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Center);
var k = 4.95867768595041;
sheet.Column(1).Width = k*2.3;
sheet.Column(2).Width = k*6;
sheet.Column(3).Width = k*0.53;
sheet.Column(4).Width = k*2.3;
sheet.Column(5).Width = k*6;
}
private void DrawTopRightSign(IXLWorksheet sheet)
{
if (acceptDirectionDirector is null)
return;
var user = acceptDirectionDirector.User;
sheet.Cell(1, 5)
.SetValue("Согласовано:");
sheet.Cell(2, 5)
.SetValue(user.Position);
sheet.Cell(3, 5)
.SetValue(user.Company?.Caption);
sheet.Cell(4, 5)
.SetValue($"{user.Surname} {user.Name} {user.Patronymic}");
sheet.Cell(5, 5)
.SetValue(FormatDate(acceptDirectionDirector.DateCreated));
sheet.Range(1,5, 5,5).Style.Alignment
.SetHorizontal(XLAlignmentHorizontalValues.Right);
}
private void DrawMainTilte(IXLWorksheet sheet)
{
sheet.Range(11, 1, 11, 5)
.Merge()
.SetValue($"Программа на бурение скважины №{well.Caption}, куст №{well.Cluster}");
sheet.Range(12, 1, 12, 5)
.Merge()
.SetValue($"{well.Deposit} месторождения");
sheet.Range(11, 1, 12, 5).Style
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Center)
.Font.SetFontSize(14)
.Font.SetBold(true);
}
private void DrawOtherAcceptors(IXLWorksheet sheet)
{
const int baseRow = 16;
const int deltaRow = 7;
(int row, int col)[] addresses =
{
(baseRow + 4 * deltaRow, 4), (baseRow + 4 * deltaRow, 1),
(baseRow + 3 * deltaRow, 4), (baseRow + 3 * deltaRow, 1),
(baseRow + 2 * deltaRow, 4), (baseRow + 2 * deltaRow, 1),
(baseRow + 1 * deltaRow, 4), (baseRow + 1 * deltaRow, 1),
(baseRow + 0 * deltaRow, 4), (baseRow + 0 * deltaRow, 1),
};
var i = 0;
for (; i < acceptsOthers.Count && i < 10; i++)
DrawAccept(sheet, acceptsOthers[i], addresses[i]);
sheet.Cell(addresses[i-1].row - 2, 1)
.SetValue("Утверждаю:");
}
private void DrawAccept(IXLWorksheet sheet, FileMarkDto mark, (int row, int col) startAddress)
{
int startRow = startAddress.row;
int startCol = startAddress.col;
var user = mark.User;
sheet.Cell(startRow, startCol)
.SetValue("Должность");
sheet.Range(startRow, startCol + 1, startRow + 1, startCol + 1)
.Merge()
.SetValue(user.Position);
sheet.Cell(startRow + 2, startCol)
.SetValue("Компания");
sheet.Range(startRow + 2, startCol + 1, startRow + 3, startCol + 1)
.Merge()
.SetValue(user.Company?.Caption);
sheet.Range(startRow + 4, startCol, startRow + 4, startCol + 1)
.Merge()
.SetValue($"{user.Surname} {user.Name} {user.Patronymic}");
sheet.Range(startRow + 5, startCol, startRow + 5, startCol + 1)
.Merge()
.SetValue(FormatDate(mark.DateCreated));
sheet.Range(startRow, startCol, startRow + 5, startCol + 1)
.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin)
.Alignment.SetVertical(XLAlignmentVerticalValues.Top)
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Left);
}
private static string FormatDate(DateTime dateTime)
=> $"{dateTime.Day:00}.{dateTime.Month:00}.{dateTime.Year:00}";
}
#nullable disable
}

View File

@ -1,6 +1,7 @@
using AsbCloudApp.Data;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Repository;
namespace AsbCloudInfrastructure.Services
{

View File

@ -1,6 +1,7 @@
using AsbCloudApp.Data.SAUB;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Repository;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
@ -26,14 +27,12 @@ namespace AsbCloudInfrastructure.Services.SAUB
//{ "", new SetpointInfoDto { Name = "", DisplayName = "Обороты ВСП, об/мин" } }, // Оно в ПЛК спинмастера, пока сделать нельзя, позднее можно.
//{ "", new SetpointInfoDto { Name = "", DisplayName = "Расход промывочной жидкости, л/с" } }, // Нет в контроллере
};
private readonly IAsbCloudDbContext db;
private readonly SetpointsRequestRepository setpointsRepository;
private readonly ITelemetryService telemetryService;
private readonly CrudCacheServiceBase<SetpointsRequestDto, SetpointsRequest> setpointsRepository;
public SetpointsService(IAsbCloudDbContext db, ITelemetryService telemetryService)
public SetpointsService(IAsbCloudDbContext db, ITelemetryService telemetryService, IWellService wellService)
{
setpointsRepository = new CrudCacheServiceBase<SetpointsRequestDto, SetpointsRequest>(db, q => q.Include(s => s.Author).Include(s => s.Well));
this.db = db;
setpointsRepository = new SetpointsRequestRepository(db, wellService);
this.telemetryService = telemetryService;
}
@ -62,25 +61,38 @@ namespace AsbCloudInfrastructure.Services.SAUB
var filtered = all.Where(s =>
s.IdWell == idWell &&
s.IdState == 1 &&
s.UploadDate.AddSeconds(s.ObsolescenceSec) > DateTime.Now);
s.UploadDate.AddSeconds(s.ObsolescenceSec) > DateTime.UtcNow)
.ToList();
if (!filtered.Any())
return null;
foreach (var entity in filtered)
entity.IdState = 2;
foreach (var item in filtered)
{
item.IdState = 2;
item.UploadDate = DateTime.SpecifyKind(item.UploadDate, DateTimeKind.Utc);
}
await setpointsRepository.UpdateRangeAsync(filtered, token);
return filtered;
}
public async Task<int> UpdateStateAsync(int id, SetpointsRequestDto setpointsRequestDto, CancellationToken token)
public async Task<int> UpdateStateAsync(SetpointsRequestDto setpointsRequestDto, CancellationToken token)
{
if (setpointsRequestDto.IdState != 3 && setpointsRequestDto.IdState != 4)
throw new ArgumentOutOfRangeException(nameof(setpointsRequestDto), $"{nameof(setpointsRequestDto.IdState)} = {setpointsRequestDto.IdState}. Mast be 3 or 4.");
var entity = await setpointsRepository.GetAsync(id, token);
if (setpointsRequestDto.Id <= 0)
throw new ArgumentOutOfRangeException(nameof(setpointsRequestDto), $"{nameof(setpointsRequestDto.Id)} = {setpointsRequestDto.Id}. Mast be > 0");
if (setpointsRequestDto.IdWell <= 0)
throw new ArgumentOutOfRangeException(nameof(setpointsRequestDto), $"{nameof(setpointsRequestDto.IdWell)} = {setpointsRequestDto.IdWell}. Mast be > 0");
var entity = await setpointsRepository.GetOrDefaultAsync(setpointsRequestDto.Id, token);
if (entity.IdWell != setpointsRequestDto.IdWell)
return 0;
if (entity is null)
return 0;

View File

@ -1,6 +1,7 @@
using AsbCloudApp.Data;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Repository;
using Mapster;
using Microsoft.EntityFrameworkCore;
using System;

View File

@ -56,7 +56,7 @@ namespace AsbCloudInfrastructure.Services
var dtos = entities?.Select(Convert);
return dtos;
}
public UserRoleDto Get(int id)
public UserRoleDto GetOrDefault(int id)
{
var entity = cacheUserRoles.FirstOrDefault(r => r.Id == id);
if (entity is null)
@ -65,7 +65,7 @@ namespace AsbCloudInfrastructure.Services
return dto;
}
public async Task<UserRoleDto> GetAsync(int id, CancellationToken token = default)
public async Task<UserRoleDto> GetOrDefaultAsync(int id, CancellationToken token = default)
{
var entity = await cacheUserRoles.FirstOrDefaultAsync(r => r.Id == id, token)
.ConfigureAwait(false);

View File

@ -81,7 +81,7 @@ namespace AsbCloudInfrastructure.Services
return dtos;
}
public UserExtendedDto Get(int id)
public UserExtendedDto GetOrDefault(int id)
{
var entity = cacheUsers.FirstOrDefault(u => u.Id == id);
var dto = Convert(entity);
@ -89,7 +89,7 @@ namespace AsbCloudInfrastructure.Services
return dto;
}
public async Task<UserExtendedDto> GetAsync(int id, CancellationToken token = default)
public async Task<UserExtendedDto> GetOrDefaultAsync(int id, CancellationToken token = default)
{
var entity = await cacheUsers.FirstOrDefaultAsync(u => u.Id == id, token).ConfigureAwait(false);
var dto = Convert(entity);

View File

@ -0,0 +1,72 @@
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services
{
public class UserSettingsRepository : IUserSettingsRepository
{
private readonly IAsbCloudDbContext context;
public UserSettingsRepository(IAsbCloudDbContext context)
{
this.context = context;
}
public Task<object> GetOrDefaultAsync(int userId, string key, CancellationToken token)
=> context.Set<UserSetting>()
.Where(s => s.IdUser == userId && s.Key == key)
.Select(s=>s.Value)
.FirstOrDefaultAsync(token);
public async Task<int> InsertAsync(int userId, string key, object value, CancellationToken token)
{
var set = context.Set<UserSetting>();
if (await set.AnyAsync(s=>s.IdUser == userId && s.Key == key, token))
return IUserSettingsRepository.ErrorKeyIsUsed;
var entity = new UserSetting
{
IdUser = userId,
Key = key,
Value = value,
};
context.Set<UserSetting>()
.Add(entity);
return await context.SaveChangesAsync(token);
}
public async Task<int> UpdateAsync(int userId, string key, object value, CancellationToken token)
{
var set = context.Set<UserSetting>();
var updatingItem = await set
.FirstOrDefaultAsync(s => s.IdUser == userId && s.Key == key, token);
if (updatingItem is null)
return IUserSettingsRepository.ErrorKeyNotFound;
updatingItem.Value = value;
set.Update(updatingItem);
return await context.SaveChangesAsync(token);
}
public async Task<int> DeleteAsync(int userId, string key, CancellationToken token)
{
var set = context.Set<UserSetting>();
var removingItem = await set
.FirstOrDefaultAsync(s=>s.IdUser == userId && s.Key ==key, token);
if(removingItem is null)
return IUserSettingsRepository.ErrorKeyNotFound;
set.Remove(removingItem);
return await context.SaveChangesAsync(token);
}
}
}

View File

@ -31,7 +31,7 @@ namespace AsbCloudInfrastructure.Services.WellOperationService
if (!tvd.Any())
return null;
var well = await wellService.GetAsync(idWell, token);
var well = await wellService.GetOrDefaultAsync(idWell, token);
var ecxelTemplateStream = GetExcelTemplateStream();
using var workbook = new XLWorkbook(ecxelTemplateStream, XLEventTracking.Disabled);

View File

@ -12,6 +12,7 @@ using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services.WellOperationService
{
#nullable enable
public class WellOperationService : IWellOperationService
{
private readonly IAsbCloudDbContext db;
@ -219,4 +220,5 @@ namespace AsbCloudInfrastructure.Services.WellOperationService
.ConfigureAwait(false);
}
}
#nullable disable
}

View File

@ -3,6 +3,7 @@ using AsbCloudApp.Exceptions;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.EfCache;
using AsbCloudInfrastructure.Repository;
using AsbCloudInfrastructure.Services.Cache;
using Mapster;
using Microsoft.EntityFrameworkCore;
@ -62,7 +63,7 @@ namespace AsbCloudInfrastructure.Services
public DateTimeOffset GetLastTelemetryDate(int idWell)
{
var well = Get(idWell);
var well = GetOrDefault(idWell);
if (well?.IdTelemetry is null)
return DateTimeOffset.MinValue;
@ -154,7 +155,7 @@ namespace AsbCloudInfrastructure.Services
public async Task<string> GetWellCaptionByIdAsync(int idWell, CancellationToken token)
{
var entity = await GetAsync(idWell, token).ConfigureAwait(false);
var entity = await GetOrDefaultAsync(idWell, token).ConfigureAwait(false);
var dto = Convert(entity);
return dto.Caption;
}
@ -186,7 +187,7 @@ namespace AsbCloudInfrastructure.Services
public async Task<IEnumerable<int>> GetClusterWellsIdsAsync(int idWell, CancellationToken token)
{
var well = await GetAsync(idWell, token);
var well = await GetOrDefaultAsync(idWell, token);
if (well is null)
return null;
@ -239,7 +240,7 @@ namespace AsbCloudInfrastructure.Services
{
var dto = entity.Adapt<CompanyDto>();
dto.CompanyTypeCaption = entity.CompanyType?.Caption
?? companyTypesService.Get(entity.IdCompanyType).Caption;
?? companyTypesService.GetOrDefault(entity.IdCompanyType).Caption;
return dto;
}
@ -265,7 +266,7 @@ namespace AsbCloudInfrastructure.Services
public SimpleTimezoneDto GetTimezone(int idWell)
{
var well = Get(idWell);
var well = GetOrDefault(idWell);
if (well == null)
throw new ArgumentInvalidException($"idWell: {idWell} does not exist.", nameof(idWell));
return GetTimezone(well);
@ -334,7 +335,7 @@ namespace AsbCloudInfrastructure.Services
public DatesRangeDto GetDatesRange(int idWell)
{
var well = Get(idWell);
var well = GetOrDefault(idWell);
if (well is null)
throw new Exception($"Well id: {idWell} does not exist.");

View File

@ -1,4 +1,5 @@
using AsbCloudApp.Services;
using AsbCloudDb;
using AsbCloudDb.Model;
using Mapster;
using Microsoft.EntityFrameworkCore;
@ -71,8 +72,9 @@ namespace AsbCloudInfrastructure.Services
return Task.CompletedTask;
var timezoneHours = telemetryService.GetTimezone(idTelemetry).Hours;
var entities = dtos.Select(dto => Convert(dto, idTelemetry, timezoneHours));
var entities = dtos
.DistinctBy(d => d.DateTime)
.Select(dto => Convert(dto, idTelemetry, timezoneHours));
dbset.AddRange(entities);
return db.SaveChangesAsync(token);
}

View File

@ -7,6 +7,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.6" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageReference Include="Moq" Version="4.18.1" />
<PackageReference Include="xunit" Version="2.4.1" />

View File

@ -43,7 +43,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests
public async Task GetById()
{
var id = await Insert();
var gotItem = await service.GetAsync(id, CancellationToken.None);
var gotItem = await service.GetOrDefaultAsync(id, CancellationToken.None);
Assert.True(id > 0);
Assert.Equal(id, gotItem.Id);
}

View File

@ -1,7 +1,7 @@
using AsbCloudApp.Data;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Services;
using AsbCloudInfrastructure.Repository;
namespace AsbCloudWebApi.Tests.ServicesTests
{

View File

@ -1,4 +1,5 @@
using AsbCloudApp.Data;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Services;
@ -6,6 +7,8 @@ using AsbCloudInfrastructure.Services.Cache;
using AsbCloudInfrastructure.Services.DetectOperations;
using Moq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
@ -17,9 +20,17 @@ namespace AsbCloudWebApi.Tests.ServicesTests
private readonly AsbCloudDbContext context;
private readonly CacheDb cacheDb;
private readonly DetectedOperationService service;
#region Задача данных
private readonly DetectedOperationRequest request;
private Deposit deposit = new Deposit { Id = 1, Caption = "Депозит 1" };
private Cluster cluster = new Cluster { Id = 1, Caption = "Кластер 1", IdDeposit = 1, Timezone = new SimpleTimezone() };
private WellDto wellDto = new WellDto
{
Id = 1,
Caption = "Test well 1",
IdTelemetry = 1,
IdCluster = 1,
Timezone = new SimpleTimezoneDto { Hours = 5 }
};
private Well well = new Well
{
Id = 1,
@ -35,7 +46,8 @@ namespace AsbCloudWebApi.Tests.ServicesTests
Patronymic = "Тест",
Surname = "Тестович"
};
private DetectedOperation do1 = new DetectedOperation
private List<DetectedOperation> do1 = new List<DetectedOperation> {
new DetectedOperation
{
Id = 1,
IdCategory = 1,
@ -43,14 +55,54 @@ namespace AsbCloudWebApi.Tests.ServicesTests
DateStart = DateTimeOffset.Parse("2022-05-16T10:00:00.286Z"),
DateEnd = DateTimeOffset.Parse("2022-05-16T18:00:00.286Z"),
DepthStart = 100,
Value = 50,
DepthEnd = 1000
};
},
new DetectedOperation
{
Id = 2,
IdCategory = 1,
IdTelemetry = 1,
DateStart = DateTimeOffset.Parse("2022-05-16T10:00:00.286Z"),
DateEnd = DateTimeOffset.Parse("2022-05-16T18:00:00.286Z"),
DepthStart = 100,
Value = 10,
DepthEnd = 1000
}};
private Telemetry telemetry = new Telemetry
{
Id = 1,
RemoteUid = Guid.NewGuid().ToString()
};
#endregion
private OperationValue ovd = new OperationValue
{
Id = 1,
StandardValue = 200,
TargetValue = 100,
DepthEnd = 300,
DepthStart = 100,
IdOperationCategory=1,
IdWell = 1
};
private List<Schedule> sch = new List<Schedule> { new Schedule
{
Id = 1,
IdDriller = 1,
IdWell = 1,
DrillStart = DateTimeOffset.Parse("2022-05-16T10:00:00.286Z"),
DrillEnd = DateTimeOffset.Parse("2022-05-16T18:00:00.286Z"),
ShiftStart = new TimeOnly(10, 00),
ShiftEnd = new TimeOnly(18, 00)
}, new Schedule
{
Id = 2,
IdDriller = 1,
IdWell = 1,
DrillStart = DateTimeOffset.Parse("2022-05-17T10:00:00.286Z"),
DrillEnd = DateTimeOffset.Parse("2022-05-17T18:00:00.286Z"),
ShiftStart = new TimeOnly(10, 00),
ShiftEnd = new TimeOnly(18, 00)
} };
public DetectedOperationServiceTest()
{
@ -62,17 +114,25 @@ namespace AsbCloudWebApi.Tests.ServicesTests
context.Clusters.Add(cluster);
context.Wells.Add(well);
context.Drillers.Add(driller);
context.DetectedOperations.Add(do1);
context.DetectedOperations.AddRange(do1);
context.OperationValues.Add(ovd);
context.Schedule.AddRange(sch);
context.SaveChanges();
var timezone = new SimpleTimezoneDto { Hours = 5 };
var wellServiceMock = new Mock<IWellService>();
wellServiceMock.Setup(s => s.GetTimezone(It.IsAny<int>())).Returns(timezone);
wellServiceMock.Setup(s => s.GetOrDefaultAsync(It.IsAny<int>(),CancellationToken.None)).Returns(Task.Run(() => wellDto));
var operationValueService = new OperationValueService(context);
var scheduleService = new ScheduleService(context, wellServiceMock.Object);
service = new DetectedOperationService(context, wellServiceMock.Object, operationValueService, scheduleService);
request = new DetectedOperationRequest
{
IdWell = 1,
IdCategory = 1
};
AsbCloudInfrastructure.DependencyInjection.MapsterSetup();
}
@ -84,9 +144,40 @@ namespace AsbCloudWebApi.Tests.ServicesTests
[Fact]
public async Task Count_grouping_by_driller()
{
var count = 10;
var list = await service.GetAsync(1, null, CancellationToken.None);
Assert.Equal(3, count);
var list = await service.GetAsync(request, CancellationToken.None);
Assert.Equal(2, list.Stats.First().Count);
}
[Fact]
public async Task AvgVal_grouping_by_driller()
{
var list = await service.GetAsync(request, CancellationToken.None);
Assert.Equal(30, list.Stats.First().AverageValue);
}
[Fact]
public async Task AvgTargetVal_grouping_by_driller()
{
var list = await service.GetAsync(request, CancellationToken.None);
Assert.Equal(100, list.Stats.First().AverageTargetValue);
}
[Fact]
public async Task Loss_grouping_by_driller()
{
var list = await service.GetAsync(request, CancellationToken.None);
Assert.Equal(0, list.Stats.First().Loss);
}
[Fact]
public async Task Efficiency_grouping_by_driller()
{
var list = await service.GetAsync(request, CancellationToken.None);
Assert.Equal(100, list.Stats.First().Efficiency);
}
[Fact]
public async Task GroupCount_grouping_by_driller()
{
var list = await service.GetAsync(request, CancellationToken.None);
Assert.Equal(1, list.Stats.Count());
}
}

View File

@ -162,7 +162,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests
db.DrillingProgramParts.Add(new DrillingProgramPart { IdFileCategory = 1001, IdWell = idWell });
db.SaveChanges();
userServiceMock.Setup((s) => s.GetAsync(It.IsAny<int>(), It.IsAny<CancellationToken>()))
userServiceMock.Setup((s) => s.GetOrDefaultAsync(It.IsAny<int>(), It.IsAny<CancellationToken>()))
.Returns(Task.FromResult(publisher1.Adapt<UserExtendedDto>()));
var service = new DrillingProgramService(
@ -197,7 +197,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests
IdUserRole = idUserRole
});
db.SaveChanges();
userServiceMock.Setup((s) => s.GetAsync(It.IsAny<int>(), It.IsAny<CancellationToken>()))
userServiceMock.Setup((s) => s.GetOrDefaultAsync(It.IsAny<int>(), It.IsAny<CancellationToken>()))
.Returns(Task.FromResult(publisher1.Adapt<UserExtendedDto>()));
var service = new DrillingProgramService(
@ -346,7 +346,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests
);
await db.SaveChangesAsync();
wellServiceMock.Setup(s => s.GetAsync(It.IsAny<int>(), It.IsAny<CancellationToken>()))
wellServiceMock.Setup(s => s.GetOrDefaultAsync(It.IsAny<int>(), It.IsAny<CancellationToken>()))
.Returns(Task.FromResult(new WellDto { Caption = "test well", Cluster = "test cluster" }));
var service = new DrillingProgramService(
@ -376,7 +376,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests
await db.SaveChangesAsync();
wellServiceMock.Setup(s => s.GetAsync(It.IsAny<int>(), It.IsAny<CancellationToken>()))
wellServiceMock.Setup(s => s.GetOrDefaultAsync(It.IsAny<int>(), It.IsAny<CancellationToken>()))
.Returns(Task.FromResult(new WellDto { Caption = "test well", Cluster = "test cluster" }));
var service = new DrillingProgramService(

View File

@ -119,7 +119,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests
Permissions = new[] { new PermissionDto { Id = 2_000_001 } },
};
var id = await service.InsertAsync(newRole, CancellationToken.None);
var entity = await service.GetAsync(id);
var entity = await service.GetOrDefaultAsync(id);
Assert.Equal(newRole.Permissions.Count(), entity.Permissions.Count());
}
@ -134,7 +134,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests
Roles = new[] { new UserRoleDto { Id = 1_000_001 } }
};
var id = await service.InsertAsync(newRole, CancellationToken.None);
var entity = await service.GetAsync(id);
var entity = await service.GetOrDefaultAsync(id);
Assert.Equal(newRole.Roles.Count, entity.Roles.Count);
}
@ -164,7 +164,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests
Permissions = new[] { new PermissionDto { Id = 2_000_001 } },
};
var id = await service.UpdateAsync(modRole, CancellationToken.None);
var entity = await service.GetAsync(id);
var entity = await service.GetOrDefaultAsync(id);
Assert.Equal(modRole.Permissions.Count(), entity.Permissions.Count());
}
@ -180,7 +180,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests
Roles = new[] { new UserRoleDto { Id = 1_000_001 } }
};
var id = await service.UpdateAsync(modRole, CancellationToken.None);
var entity = await service.GetAsync(id);
var entity = await service.GetOrDefaultAsync(id);
Assert.Equal(modRole.Roles.Count(), entity.Roles.Count());
}
}

View File

@ -1,17 +1,23 @@
using AsbCloudDb.Model;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
namespace AsbCloudWebApi.Tests
{
internal static class TestHelpter
{
// Попробовать когда-нибудь https://github.com/MichalJankowskii/Moq.EntityFrameworkCore
public static AsbCloudDbContext MakeTestContext()
{
var options = new DbContextOptionsBuilder<AsbCloudDbContext>()
//.UseInMemoryDatabase(System.Guid.NewGuid().ToString())
//.ConfigureWarnings(configBuilder =>
// configBuilder.Ignore(InMemoryEventId.TransactionIgnoredWarning))
.UseNpgsql("Host=localhost;Database=tests;Username=postgres;Password=q;Persist Security Info=True;Include Error Detail=True")
.Options;
var context = new AsbCloudDbContext(options);
context.Database.EnsureDeleted();
//context.Database.EnsureDeleted();
context.Database.EnsureCreated();
return context;
}

View File

@ -9,8 +9,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.5" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="6.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.6" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="6.0.6" />
<PackageReference Include="protobuf-net" Version="3.1.4" />
<PackageReference Include="protobuf-net.AspNetCore" Version="3.0.101" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.3.1" />

View File

@ -5,6 +5,9 @@ using Microsoft.AspNetCore.Mvc;
namespace AsbCloudWebApi.Controllers
{
/// <summary>
/// Редактор кустов для админки
/// </summary>
[Route("api/admin/cluster")]
[ApiController]
[Authorize]

View File

@ -5,6 +5,9 @@ using Microsoft.AspNetCore.Mvc;
namespace AsbCloudWebApi.Controllers
{
/// <summary>
/// Редактор компаний для админки
/// </summary>
[Route("api/admin/company")]
[ApiController]
[Authorize]

View File

@ -5,6 +5,9 @@ using Microsoft.AspNetCore.Mvc;
namespace AsbCloudWebApi.Controllers
{
/// <summary>
/// Редактор типов компаний для админки
/// </summary>
[Route("api/admin/companyType")]
[ApiController]
[Authorize]

View File

@ -5,6 +5,9 @@ using Microsoft.AspNetCore.Mvc;
namespace AsbCloudWebApi.Controllers
{
/// <summary>
/// Редактор месторождений для админки
/// </summary>
[Route("api/admin/deposit")]
[ApiController]
[Authorize]

View File

@ -9,6 +9,9 @@ using System.Reflection;
namespace AsbCloudWebApi.Controllers
{
/// <summary>
/// Редактор разрешений пользователей для админки
/// </summary>
[Route("api/admin/permission")]
[ApiController]
[Authorize]

View File

@ -7,6 +7,9 @@ using System.Threading.Tasks;
namespace AsbCloudWebApi.Controllers
{
/// <summary>
/// Редактор телеметрий для админки
/// </summary>
[Route("api/admin/telemetry")]
[ApiController]
[Authorize]

View File

@ -5,6 +5,9 @@ using Microsoft.AspNetCore.Mvc;
namespace AsbCloudWebApi.Controllers
{
/// <summary>
/// Редактор пользователей для админки
/// </summary>
[Route("api/admin/user")]
[ApiController]
[Authorize]

View File

@ -6,6 +6,9 @@ using System.Threading.Tasks;
namespace AsbCloudWebApi.Controllers
{
/// <summary>
/// Редактор ролей пользователей для админки
/// </summary>
[Route("api/admin/role")]
[ApiController]
[Authorize]
@ -16,8 +19,8 @@ namespace AsbCloudWebApi.Controllers
{
UpdateForbidAsync = async (dto, token) =>
{
var role = await service.GetAsync(dto.Id, token);
return role?.IdType != 1;
var role = await service.GetOrDefaultAsync(dto.Id, token);
return role?.Id == 1;
};
DeleteForbidAsync = (id, token) =>

View File

@ -7,6 +7,9 @@ using System.Threading.Tasks;
namespace AsbCloudWebApi.Controllers
{
/// <summary>
/// Редактор скважин для админки
/// </summary>
[Route("api/admin/well")]
[ApiController]
[Authorize]
@ -18,7 +21,7 @@ namespace AsbCloudWebApi.Controllers
[HttpPost("EnshureTimezonesIsSet")]
[Permission]
public async Task<IActionResult> EnsureTimestamps(CancellationToken token)
public async Task<IActionResult> EnshureTimezonesIsSet(CancellationToken token)
{
await ((IWellService)service).EnshureTimezonesIsSetAsync(token);
return Ok();

View File

@ -8,6 +8,9 @@ using System.Threading.Tasks;
namespace AsbCloudWebApi.Controllers
{
/// <summary>
/// Авторизация
/// </summary>
[Route("/auth")]
[ApiController]
public class AuthController : ControllerBase

View File

@ -9,7 +9,7 @@ using System.Threading.Tasks;
namespace AsbCloudWebApi.Controllers
{
/// <summary>
/// Контроллер кустов
/// Инфо о кустах
/// </summary>
[Route("api/cluster")]
[ApiController]

View File

@ -57,7 +57,7 @@ namespace AsbCloudWebApi.Controllers
[Permission]
public virtual async Task<ActionResult<T>> GetAsync(int id, CancellationToken token)
{
var result = await service.GetAsync(id, token).ConfigureAwait(false);
var result = await service.GetOrDefaultAsync(id, token).ConfigureAwait(false);
return Ok(result);
}

View File

@ -11,7 +11,7 @@ using System.Threading.Tasks;
namespace AsbCloudWebApi.Controllers
{
/// <summary>
/// CRUD контроллер для админки.
/// CRUD контроллер dto связных со скважиной для админки.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TService"></typeparam>
@ -68,7 +68,12 @@ namespace AsbCloudWebApi.Controllers
return Ok(result);
}
/// <inheritdoc/>
/// <summary>
/// Получить одну запись по Id
/// </summary>
/// <param name="id">id записи</param>
/// <param name="token"></param>
/// <returns>запись</returns>
[HttpGet("{id}")]
public override async Task<ActionResult<T>> GetAsync(int id, CancellationToken token)
{
@ -79,7 +84,12 @@ namespace AsbCloudWebApi.Controllers
return Ok(result);
}
/// <inheritdoc/>
/// <summary>
/// Добавить запись
/// </summary>
/// <param name="value">запись</param>
/// <param name="token"></param>
/// <returns>id</returns>
[HttpPost]
public override async Task<ActionResult<int>> InsertAsync([FromBody] T value, CancellationToken token)
{
@ -88,7 +98,13 @@ namespace AsbCloudWebApi.Controllers
return await base.InsertAsync(value, token);
}
/// <inheritdoc/>
/// <summary>
/// Добавить несколько записей<br/>
/// При невозможности добавить любую из записей, все не будут добавлены.
/// </summary>
/// <param name="values">записи</param>
/// <param name="token"></param>
/// <returns>id</returns>
[HttpPost("range")]
public override async Task<ActionResult<int>> InsertRangeAsync([FromBody] IEnumerable<T> values, CancellationToken token)
{
@ -99,7 +115,12 @@ namespace AsbCloudWebApi.Controllers
return await base.InsertRangeAsync(values, token);
}
/// <inheritdoc/>
/// <summary>
/// Редактировать запись по id
/// </summary>
/// <param name="value">запись</param>
/// <param name="token"></param>
/// <returns>1 - успешно отредактировано, 0 - нет</returns>
[HttpPut]
public override async Task<ActionResult<int>> UpdateAsync([FromBody] T value, CancellationToken token)
{
@ -108,11 +129,16 @@ namespace AsbCloudWebApi.Controllers
return await base.UpdateAsync(value, token);
}
/// <inheritdoc/>
/// <summary>
/// Удалить запись по id
/// </summary>
/// <param name="id">id записи</param>
/// <param name="token"></param>
/// <returns>1 - успешно удалено, 0 - нет</returns>
[HttpDelete("{id}")]
public override async Task<ActionResult<int>> DeleteAsync(int id, CancellationToken token)
{
var item = await service.GetAsync(id, token);
var item = await service.GetOrDefaultAsync(id, token);
if (item is null)
return NoContent();
if (!await UserHasAccesToWellAsync(item.IdWell, token))

View File

@ -10,6 +10,9 @@ using System.Threading.Tasks;
namespace AsbCloudWebApi.Controllers
{
/// <summary>
/// Суточный рапорт
/// </summary>
[Route("api/well/{idWell}/[controller]")]
[ApiController]
[Authorize]
@ -94,6 +97,7 @@ namespace AsbCloudWebApi.Controllers
/// Сформировать и скачать рапорт в формате excel
/// </summary>
/// <param name="idWell"></param>
/// <param name="date"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpGet("{date}/excel")]
@ -101,7 +105,7 @@ namespace AsbCloudWebApi.Controllers
[ProducesResponseType(typeof(PhysicalFileResult), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> DownloadAsync(int idWell, DateTime date, CancellationToken token = default)
{
var well = await wellService.GetAsync(idWell, token);
var well = await wellService.GetOrDefaultAsync(idWell, token);
var stream = await dailyReportService.MakeReportAsync(idWell, date, token);
if (stream != null)
{

View File

@ -9,7 +9,7 @@ using System.Threading.Tasks;
namespace AsbCloudWebApi.Controllers
{
/// <summary>
/// Контроллер для месторождений
/// Инфо о месторождениях
/// </summary>
[Route("api/deposit")]
[ApiController]

View File

@ -10,7 +10,7 @@ using System.Threading.Tasks;
namespace AsbCloudWebApi.Controllers
{
/// <summary>
/// Контроллер для коридоров бурения на панели
/// Коридоры бурения для панели бурильщика
/// </summary>
[ApiController]
[Route("api/[controller]")]
@ -18,14 +18,12 @@ namespace AsbCloudWebApi.Controllers
public class DrillFlowChartController : CrudWellRelatedController<DrillFlowChartDto, IDrillFlowChartService>
{
private readonly ITelemetryService telemetryService;
private readonly IWellService wellService;
public DrillFlowChartController(IWellService wellService, IDrillFlowChartService service,
ITelemetryService telemetryService)
: base(wellService, service)
{
this.telemetryService = telemetryService;
this.wellService = wellService;
}
/// <summary>

View File

@ -8,7 +8,7 @@ using System.Threading.Tasks;
namespace AsbCloudWebApi.Controllers
{
/// <summary>
/// Контроллер для режимов бурения
/// Режимы бурения
/// </summary>
[Route("api/well/{idWell}/drillParams/")]
[ApiController]

View File

@ -5,6 +5,9 @@ using Microsoft.AspNetCore.Mvc;
namespace AsbCloudWebApi.Controllers
{
/// <summary>
/// Справочник бурильщиков
/// </summary>
[Route("api/driller")]
[ApiController]
[Authorize]

Some files were not shown because too many files have changed in this diff Show More