Add Db store for detected operations.

Add backgroudService for periodically detect.
This commit is contained in:
ngfrolov 2022-04-25 17:41:18 +05:00
parent 4a91a29f07
commit 45fb5ab8e8
8 changed files with 6067 additions and 31 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,62 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class Add_DetectedOperations : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "t_detected_operation",
columns: table => new
{
id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
id_telemetry = table.Column<int>(type: "integer", nullable: false),
id_category = table.Column<int>(type: "integer", nullable: false, comment: "Id категории операции"),
id_user = table.Column<int>(type: "integer", nullable: false, comment: "Id пользователя по телеметрии на момент начала операции"),
date_start = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата начала операции"),
date_end = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата начала операции"),
depth_start = table.Column<double>(type: "double precision", nullable: false, comment: "Глубина на начало операции, м"),
depth_end = table.Column<double>(type: "double precision", nullable: false, comment: "Глубина после завершения операции, м")
},
constraints: table =>
{
table.PrimaryKey("PK_t_detected_operation", x => x.id);
table.ForeignKey(
name: "FK_t_detected_operation_t_telemetry_id_telemetry",
column: x => x.id_telemetry,
principalTable: "t_telemetry",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_t_detected_operation_t_well_operation_category_id_category",
column: x => x.id_category,
principalTable: "t_well_operation_category",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
},
comment: "автоматически определенные операции по телеметрии");
migrationBuilder.CreateIndex(
name: "IX_t_detected_operation_id_category",
table: "t_detected_operation",
column: "id_category");
migrationBuilder.CreateIndex(
name: "IX_t_detected_operation_id_telemetry",
table: "t_detected_operation",
column: "id_telemetry");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "t_detected_operation");
}
}
}

View File

@ -171,6 +171,60 @@ namespace AsbCloudDb.Migrations
b.HasComment("Месторождение");
});
modelBuilder.Entity("AsbCloudDb.Model.DetectedOperation", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTimeOffset>("DateEnd")
.HasColumnType("timestamp with time zone")
.HasColumnName("date_end")
.HasComment("Дата начала операции");
b.Property<DateTimeOffset>("DateStart")
.HasColumnType("timestamp with time zone")
.HasColumnName("date_start")
.HasComment("Дата начала операции");
b.Property<double>("DepthEnd")
.HasColumnType("double precision")
.HasColumnName("depth_end")
.HasComment("Глубина после завершения операции, м");
b.Property<double>("DepthStart")
.HasColumnType("double precision")
.HasColumnName("depth_start")
.HasComment("Глубина на начало операции, м");
b.Property<int>("IdCategory")
.HasColumnType("integer")
.HasColumnName("id_category")
.HasComment("Id категории операции");
b.Property<int>("IdTelemetry")
.HasColumnType("integer")
.HasColumnName("id_telemetry");
b.Property<int>("IdUsersAtStart")
.HasColumnType("integer")
.HasColumnName("id_user")
.HasComment("Id пользователя по телеметрии на момент начала операции");
b.HasKey("Id");
b.HasIndex("IdCategory");
b.HasIndex("IdTelemetry");
b.ToTable("t_detected_operation");
b.HasComment("автоматически определенные операции по телеметрии");
});
modelBuilder.Entity("AsbCloudDb.Model.DrillFlowChart", b =>
{
b.Property<int>("Id")
@ -5142,6 +5196,25 @@ namespace AsbCloudDb.Migrations
b.Navigation("CompanyType");
});
modelBuilder.Entity("AsbCloudDb.Model.DetectedOperation", b =>
{
b.HasOne("AsbCloudDb.Model.WellOperationCategory", "OperationCategory")
.WithMany()
.HasForeignKey("IdCategory")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("AsbCloudDb.Model.Telemetry", "Telemetry")
.WithMany()
.HasForeignKey("IdTelemetry")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("OperationCategory");
b.Navigation("Telemetry");
});
modelBuilder.Entity("AsbCloudDb.Model.DrillFlowChart", b =>
{
b.HasOne("AsbCloudDb.Model.Well", "Well")

View File

@ -12,6 +12,7 @@ namespace AsbCloudDb.Model
public virtual DbSet<Company> Companies { get; set; }
public virtual DbSet<CompanyType> CompaniesTypes { get; set; }
public virtual DbSet<Deposit> Deposits { get; set; }
public virtual DbSet<DetectedOperation> DetectedOperations { get; set; }
public virtual DbSet<DrillingProgramPart> DrillingProgramParts { get; set; }
public virtual DbSet<FileCategory> FileCategories { get; set; }
public virtual DbSet<FileInfo> Files { get; set; }

View File

@ -0,0 +1,52 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization;
namespace AsbCloudDb.Model
{
[Table("t_detected_operation"), Comment("автоматически определенные операции по телеметрии")]
public class DetectedOperation
{
[Key]
[Column("id")]
public int Id { get; set; }
[Column("id_telemetry")]
public int IdTelemetry { get; set; }
[Column("id_category"), Comment("Id категории операции")]
public int IdCategory { get; set; }
[Column("id_user"), Comment("Id пользователя по телеметрии на момент начала операции")]
public int IdUsersAtStart { get; set; }
[Column("date_start", TypeName = "timestamp with time zone"), Comment("Дата начала операции")]
public DateTimeOffset DateStart { get; set; }
[Column("date_end", TypeName = "timestamp with time zone"), Comment("Дата начала операции")]
public DateTimeOffset DateEnd { get; set; }
[NotMapped]
public double DurationMinutes => (DateEnd - DateStart).TotalMinutes;
[Column("depth_start"), Comment("Глубина на начало операции, м")]
public double DepthStart { get; set; }
[Column("depth_end"), Comment("Глубина после завершения операции, м")]
public double DepthEnd { get; set; }
[JsonIgnore]
[ForeignKey(nameof(IdTelemetry))]
public virtual Telemetry Telemetry { get; set; }
[JsonIgnore]
[ForeignKey(nameof(IdCategory))]
public virtual WellOperationCategory OperationCategory { get; set; }
public override string ToString()
=> $"{IdCategory}\t{DateStart:G}\t{DateEnd:G}\t{DurationMinutes:#0.#}\t{DepthStart:#0.#}\t{DepthEnd:#0.#}";
//=> $"{{\"type\": {IdType},\t\"begin\":\"{Begin:G}\",\t\"end\":\"{End:G}\",\t\"depthBegin\":\"{BeginWellDepth:#0.#}\",\t\"depthEnd\":\"{EndWellDepth:#0.#}\"}}";
}
}

View File

@ -42,6 +42,7 @@ namespace AsbCloudDb.Model
DbSet<DrillingProgramPart> DrillingProgramParts { get; set; }
DbSet<RelationUserDrillingProgramPart> RelationDrillingProgramPartUsers { get; set; }
DbSet<RelationCompanyWell> RelationCompaniesWells { get; set; }
DbSet<DetectedOperation> DetectedOperations { get; set; }
int SaveChanges();
int SaveChanges(bool acceptAllChangesOnSuccess);

View File

@ -1,19 +0,0 @@
using System;
namespace AsbCloudInfrastructure.Services.DetectOperations
{
public class DetectedOperation
{
public int IdCategory { get; set; }
public int IdUsersAtStart { get; set; }
public DateTimeOffset DateStart { get; set; }
public DateTimeOffset DateEnd { get; set; }
public double DurationMinutes => (DateEnd - DateStart).TotalMinutes;
public double DepthStart { get; set; }
public double DepthEnd { get; set; }
public override string ToString()
=> $"{IdCategory}\t{DateStart:G}\t{DateEnd:G}\t{DurationMinutes:#0.#}\t{DepthStart:#0.#}\t{DepthEnd:#0.#}";
//=> $"{{\"type\": {IdType},\t\"begin\":\"{Begin:G}\",\t\"end\":\"{End:G}\",\t\"depthBegin\":\"{BeginWellDepth:#0.#}\",\t\"depthEnd\":\"{EndWellDepth:#0.#}\"}}";
}
}

View File

@ -51,7 +51,8 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
try
{
using var context = new AsbCloudDbContext(options);
var added = await DetectedAllTelemetriesAsync(context, token);
Trace.TraceInformation($"Total detection complete. Added {added} operations.");
}
catch (Exception ex)
{
@ -72,21 +73,42 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
await base.StopAsync(token).ConfigureAwait(false);
}
private async Task<DateTime> GetLastAnalysisDateAsync(int idTelemetry, IAsbCloudDbContext db, CancellationToken token)
private async Task<int> DetectedAllTelemetriesAsync(IAsbCloudDbContext db, CancellationToken token)
{
var lastAnalysisInDb = await (from analysis in db.TelemetryAnalysis
where analysis.IdTelemetry == idTelemetry
orderby analysis.UnixDate
select analysis)
.LastOrDefaultAsync(token)
.ConfigureAwait(false);
var lastDetectedDatesQuery = db.DetectedOperations
.GroupBy(o => o.IdTelemetry)
.Select(g => new
{
IdTelemetry = g.Key,
LastDate = g.Max(o => o.DateEnd)
});
DateTime lastAnalysisDate = new DateTime(0, DateTimeKind.Utc);
var lastDetectedDates = await db.Telemetries
.Join(lastDetectedDatesQuery,
t => t.Id,
o => o.IdTelemetry,
(outer, inner) => new
{
IdTelemetry = outer.Id,
inner.LastDate
})
.ToListAsync(token);
if (lastAnalysisInDb is not null)
lastAnalysisDate = DateTime.UnixEpoch.AddSeconds(lastAnalysisInDb.DurationSec + lastAnalysisInDb.UnixDate);
bool isAnyAdded = false;
return lastAnalysisDate;
foreach (var item in lastDetectedDates)
{
var newOperations = await DetectOperationsAsync(item.IdTelemetry, item.LastDate, db, token);
if (newOperations.Any())
{
db.DetectedOperations.AddRange(newOperations);
isAnyAdded = true;
}
}
return isAnyAdded
? await db.SaveChangesAsync(token)
: 0;
}
private async Task<IEnumerable<DetectedOperation>> DetectOperationsAsync(int idTelemetry, DateTimeOffset begin, IAsbCloudDbContext db, CancellationToken token)