forked from ddrilling/AsbCloudServer
Merge branch 'feature/detected_operations_change_algorithm' into dev
This commit is contained in:
commit
39de15e1ba
9090
AsbCloudDb/Migrations/20231205063450_DetectedOperation_add_subsystems_and_extraData.Designer.cs
generated
Normal file
9090
AsbCloudDb/Migrations/20231205063450_DetectedOperation_add_subsystems_and_extraData.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,72 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace AsbCloudDb.Migrations
|
||||
{
|
||||
public partial class DetectedOperation_add_subsystems_and_extraData : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DeleteData(
|
||||
table: "t_relation_user_role_permission",
|
||||
keyColumns: new[] { "id_permission", "id_user_role" },
|
||||
keyValues: new object[] { 529, 1 });
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
table: "t_permission",
|
||||
keyColumn: "id",
|
||||
keyValue: 529);
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "id_reason_of_end",
|
||||
table: "t_detected_operation");
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "enabled_subsystems",
|
||||
table: "t_detected_operation",
|
||||
type: "integer",
|
||||
nullable: false,
|
||||
defaultValue: 0,
|
||||
comment: "флаги аключенных подсистем");
|
||||
|
||||
migrationBuilder.AddColumn<IDictionary<string, object>>(
|
||||
name: "extra_data",
|
||||
table: "t_detected_operation",
|
||||
type: "jsonb",
|
||||
nullable: false,
|
||||
defaultValue: new { },
|
||||
comment: "доп. инфо по операции");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "enabled_subsystems",
|
||||
table: "t_detected_operation");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "extra_data",
|
||||
table: "t_detected_operation");
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "id_reason_of_end",
|
||||
table: "t_detected_operation",
|
||||
type: "integer",
|
||||
nullable: false,
|
||||
defaultValue: 0,
|
||||
comment: "Код признака окончания операции");
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
table: "t_permission",
|
||||
columns: new[] { "id", "description", "name" },
|
||||
values: new object[] { 529, "Разрешение редактировать фактические траектории", "FactTrajectory.edit" });
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
table: "t_relation_user_role_permission",
|
||||
columns: new[] { "id_permission", "id_user_role" },
|
||||
values: new object[] { 529, 1 });
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using AsbCloudDb.Model;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@ -382,16 +383,22 @@ namespace AsbCloudDb.Migrations
|
||||
.HasColumnName("depth_start")
|
||||
.HasComment("Глубина на начало операции, м");
|
||||
|
||||
b.Property<int>("EnabledSubsystems")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("enabled_subsystems")
|
||||
.HasComment("флаги аключенных подсистем");
|
||||
|
||||
b.Property<IDictionary<string, object>>("ExtraData")
|
||||
.IsRequired()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("extra_data")
|
||||
.HasComment("доп. инфо по операции");
|
||||
|
||||
b.Property<int>("IdCategory")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id_category")
|
||||
.HasComment("Id категории операции");
|
||||
|
||||
b.Property<int>("IdReasonOfEnd")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id_reason_of_end")
|
||||
.HasComment("Код признака окончания операции");
|
||||
|
||||
b.Property<int>("IdTelemetry")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id_telemetry");
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Text.Json.Serialization;
|
||||
@ -40,8 +41,11 @@ namespace AsbCloudDb.Model
|
||||
[Column("value"), Comment("Ключевой показатель операции")]
|
||||
public double Value { get; set; }
|
||||
|
||||
[Column("id_reason_of_end"), Comment("Код признака окончания операции")]
|
||||
public int IdReasonOfEnd { get; set; }
|
||||
[Column("enabled_subsystems"), Comment("флаги аключенных подсистем")]
|
||||
public int EnabledSubsystems { get; set; }
|
||||
|
||||
[Column("extra_data", TypeName = "jsonb"), Comment("доп. инфо по операции")]
|
||||
public IDictionary<string, object> ExtraData { get; set; } = null!;
|
||||
|
||||
[JsonIgnore]
|
||||
[ForeignKey(nameof(IdTelemetry))]
|
||||
@ -53,5 +57,57 @@ namespace AsbCloudDb.Model
|
||||
|
||||
public override string ToString()
|
||||
=> $"{IdCategory}\t{DateStart:G}\t{DateEnd:G}\t{DurationMinutes:#0.#}\t{DepthStart:#0.#}\t{DepthEnd:#0.#}";
|
||||
|
||||
/// <summary>
|
||||
/// Флаги аключенных подсистем
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum EnabledSubsystemsFlags
|
||||
{
|
||||
/// <summary>
|
||||
/// Автоподача долота
|
||||
/// </summary>
|
||||
AutoRotor = 1 << 0,
|
||||
/// <summary>
|
||||
/// БУРЕНИЕ В СЛАЙДЕ
|
||||
/// </summary>
|
||||
AutoSlide = 1 << 1,
|
||||
/// <summary>
|
||||
/// ПРОРАБОТКА
|
||||
/// </summary>
|
||||
AutoConditionig = 1 << 2,
|
||||
/// <summary>
|
||||
/// СПУСК СПО
|
||||
/// </summary>
|
||||
AutoSinking = 1 << 3,
|
||||
/// <summary>
|
||||
/// ПОДЪЕМ СПО
|
||||
/// </summary>
|
||||
AutoLifting = 1 << 4,
|
||||
/// <summary>
|
||||
/// ПОДЪЕМ С ПРОРАБОТКОЙ
|
||||
/// </summary>
|
||||
AutoLiftingWithConditionig = 1 << 5,
|
||||
/// <summary>
|
||||
/// блокировка
|
||||
/// </summary>
|
||||
AutoBlocknig = 1 << 6,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Есть ли флаг подсистемы у операции
|
||||
/// </summary>
|
||||
/// <param name="flag"></param>
|
||||
/// <returns></returns>
|
||||
public bool HasSubsystemFlag(EnabledSubsystemsFlags flag)
|
||||
=> HasSubsystemFlag((int)flag);
|
||||
|
||||
/// <summary>
|
||||
/// Есть ли флаг/флаги подсистемы у операции
|
||||
/// </summary>
|
||||
/// <param name="flags"></param>
|
||||
/// <returns></returns>
|
||||
public bool HasSubsystemFlag(int flags)
|
||||
=> (EnabledSubsystems & flags) > 0;
|
||||
}
|
||||
}
|
Binary file not shown.
@ -1,12 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.DetectOperations
|
||||
{
|
||||
namespace AsbCloudInfrastructure.Services.DetectOperations;
|
||||
|
||||
public class DetectableTelemetry
|
||||
{
|
||||
public DateTimeOffset DateTime { get; set; }
|
||||
public int? IdUser { get; set; }
|
||||
public int Mode { get; set; }
|
||||
public float WellDepth { get; set; }
|
||||
public float Pressure { get; set; }
|
||||
public float HookWeight { get; set; }
|
||||
@ -14,5 +14,3 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
|
||||
public float BitDepth { get; set; }
|
||||
public float RotorSpeed { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -9,12 +9,17 @@ using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AsbCloudInfrastructure.Services.DetectOperations.Detectors;
|
||||
using AsbCloudApp.Repositories;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.DetectOperations;
|
||||
|
||||
public class DetectedOperationExportService
|
||||
{
|
||||
private readonly DetectorAbstract[] detectors = { new DetectorDrilling(), new DetectorSlipsTime() };
|
||||
private readonly DetectorAbstract[] detectors =
|
||||
{
|
||||
new DetectorDrilling(),
|
||||
new DetectorSlipsTime()
|
||||
};
|
||||
|
||||
private readonly IDictionary<int, string> domains = new Dictionary<int, string>
|
||||
{
|
||||
@ -38,12 +43,15 @@ public class DetectedOperationExportService
|
||||
private const int columnDeltaDepth = 7;
|
||||
private const int columnDepth = 8;
|
||||
private const int columnIdReasonOfEnd = 9;
|
||||
private const int columnComment = 10;
|
||||
|
||||
private readonly IAsbCloudDbContext dbContext;
|
||||
private readonly IWellOperationRepository wellOperationRepository;
|
||||
|
||||
public DetectedOperationExportService(IAsbCloudDbContext dbContext)
|
||||
public DetectedOperationExportService(IAsbCloudDbContext dbContext, IWellOperationRepository wellOperationRepository)
|
||||
{
|
||||
this.dbContext = dbContext;
|
||||
this.wellOperationRepository = wellOperationRepository;
|
||||
}
|
||||
|
||||
public async Task<Stream> ExportAsync(int idWell, int idDomain, CancellationToken cancellationToken)
|
||||
@ -59,20 +67,19 @@ public class DetectedOperationExportService
|
||||
if (!well.IdTelemetry.HasValue)
|
||||
throw new ArgumentNullException(nameof(well));
|
||||
|
||||
var operations = await DetectOperationsAsync(well.IdTelemetry.Value, new DateTime(2023, 10, 14)
|
||||
.ToUtcDateTimeOffset(well.Timezone.Hours), cancellationToken);
|
||||
var operations = await DetectOperationsAsync(well.IdTelemetry.Value, DateTime.UnixEpoch, cancellationToken);
|
||||
|
||||
return await GenerateExcelFileStreamAsync(well, idDomain, operations, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task<Stream> GenerateExcelFileStreamAsync(Well well, int idDomain, IEnumerable<DetectedOperation> detectedOperations,
|
||||
private async Task<Stream> GenerateExcelFileStreamAsync(Well well, int idDomain, IEnumerable<OperationDetectorResult> operationDetectorResults,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
using var excelTemplateStream = await GetExcelTemplateStreamAsync(cancellationToken);
|
||||
|
||||
using var workbook = new XLWorkbook(excelTemplateStream, XLEventTracking.Disabled);
|
||||
|
||||
await AddToWorkbookAsync(workbook, well, idDomain, detectedOperations, cancellationToken);
|
||||
await AddToWorkbookAsync(workbook, well, idDomain, operationDetectorResults, cancellationToken);
|
||||
|
||||
MemoryStream memoryStream = new MemoryStream();
|
||||
workbook.SaveAs(memoryStream, new SaveOptions { });
|
||||
@ -80,22 +87,23 @@ public class DetectedOperationExportService
|
||||
return memoryStream;
|
||||
}
|
||||
|
||||
private async Task AddToWorkbookAsync(XLWorkbook workbook, Well well, int idDomain, IEnumerable<DetectedOperation> detectedOperations,
|
||||
private async Task AddToWorkbookAsync(XLWorkbook workbook, Well well, int idDomain, IEnumerable<OperationDetectorResult> operationDetectorResults,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
const string sheetName = "Операции";
|
||||
|
||||
if (!detectedOperations.Any())
|
||||
if (!operationDetectorResults.Any())
|
||||
return;
|
||||
|
||||
var sheet = workbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetName)
|
||||
?? throw new FileFormatException($"Книга excel не содержит листа {sheetName}.");
|
||||
|
||||
await AddToSheetAsync(sheet, well, idDomain, detectedOperations.OrderBy(x => x.DateStart).ThenBy(x => x.DepthStart).ToArray(),
|
||||
await AddToSheetAsync(sheet, well, idDomain, operationDetectorResults
|
||||
.OrderBy(x => x.Operation.DateStart).ThenBy(x => x.Operation.DepthStart).ToArray(),
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
private async Task AddToSheetAsync(IXLWorksheet sheet, Well well, int idDomain, IList<DetectedOperation> detectedOperations,
|
||||
private async Task AddToSheetAsync(IXLWorksheet sheet, Well well, int idDomain, IList<OperationDetectorResult> operationDetectorResults,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var wellOperationCategories = await dbContext.WellOperationCategories.ToListAsync(cancellationToken);
|
||||
@ -103,39 +111,69 @@ public class DetectedOperationExportService
|
||||
sheet.Cell(cellDepositName).Value = well.Cluster.Deposit.Caption;
|
||||
sheet.Cell(cellClusterName).Value = well.Cluster.Caption;
|
||||
sheet.Cell(cellWellName).Value = well.Caption;
|
||||
sheet.Cell(cellDeltaDate).Value = detectedOperations.Max(o => o.DateEnd) - detectedOperations.Min(o => o.DateStart);
|
||||
sheet.Cell(cellDeltaDate).Value = operationDetectorResults.Max(o => o.Operation.DateEnd) - operationDetectorResults.Min(o => o.Operation.DateStart);
|
||||
|
||||
var timeZoneWell = TimeSpan.FromHours(well.Timezone.Hours);
|
||||
var detectedOperations = operationDetectorResults.Select(o => o.Operation).ToArray();
|
||||
|
||||
for (int i = 0; i < detectedOperations.Count; i++)
|
||||
for (int i = 0; i < operationDetectorResults.Count; i++)
|
||||
{
|
||||
var dateStart = detectedOperations[i].DateStart.ToOffset(timeZoneWell);
|
||||
var dateEnd = detectedOperations[i].DateEnd.ToOffset(timeZoneWell);
|
||||
var current = detectedOperations[i];
|
||||
var dateStart = current.DateStart.ToRemoteDateTime(well.Timezone.Hours);
|
||||
var dateEnd = current.DateEnd.ToRemoteDateTime(well.Timezone.Hours);
|
||||
|
||||
var row = sheet.Row(5 + i + headerRowsCount);
|
||||
|
||||
row.Cell(columnOperationName).Value = detectedOperations[i].IdCategory == 12000
|
||||
? "Бурение в слайде с осцилляцией"
|
||||
: wellOperationCategories.Single(o => o.Id == detectedOperations[i].IdCategory).Name;
|
||||
row.Cell(columnOperationName).Value = GetCategoryName(wellOperationCategories, current);
|
||||
row.Cell(columnDateEnd).Value = dateEnd;
|
||||
row.Cell(columnDuration).Value = (dateEnd - dateStart).TotalMinutes;
|
||||
row.Cell(columnDepthStart).Value = detectedOperations[i].DepthStart;
|
||||
row.Cell(columnDepthEnd).Value = detectedOperations[i].DepthEnd;
|
||||
row.Cell(columnDepth).Value = detectedOperations[i].DepthEnd - detectedOperations[i].DepthStart;
|
||||
row.Cell(columnIdReasonOfEnd).Value = detectedOperations[i].IdReasonOfEnd;
|
||||
row.Cell(columnDepthStart).Value = current.DepthStart;
|
||||
row.Cell(columnDepthEnd).Value = current.DepthEnd;
|
||||
row.Cell(columnDepth).Value = current.DepthEnd - current.DepthStart;
|
||||
|
||||
if (current.ExtraData.TryGetValue("IdReasonOfEnd", out object? idReasonOfEndObject)
|
||||
&& idReasonOfEndObject is int idReasonOfEnd)
|
||||
row.Cell(columnIdReasonOfEnd).Value = GetIdReasonOfEnd(idReasonOfEnd);
|
||||
|
||||
var link =
|
||||
$"{domains[idDomain]}/well/{well.Id}/telemetry/monitoring?end={Uri.EscapeDataString(dateStart.AddSeconds(3544).ToString("yyyy-MM-ddTHH:mm:ss.fff"))}&range=3600";
|
||||
$"{domains[idDomain]}/well/{well.Id}/telemetry/monitoring?end={Uri.EscapeDataString(dateStart.AddSeconds(1800 * 0.9).ToString("yyyy-MM-ddTHH:mm:ss.fff"))}&range=1800";
|
||||
|
||||
row.Cell(columnDateStart).Value = dateStart;
|
||||
row.Cell(columnDateStart).SetHyperlink(new XLHyperlink(link));
|
||||
|
||||
row.Cell(columnDeltaDepth).Value = i > 0 && i + 1 < detectedOperations.Count
|
||||
? detectedOperations[i].DepthStart - detectedOperations[i - 1].DepthEnd
|
||||
row.Cell(columnDeltaDepth).Value = i > 0 && i + 1 < detectedOperations.Length
|
||||
? current.DepthStart - detectedOperations[i - 1].DepthEnd
|
||||
: 0;
|
||||
|
||||
row.Cell(columnComment).Value = CreateComment(operationDetectorResults[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetCategoryName(IEnumerable<WellOperationCategory> wellOperationCategories, DetectedOperation current)
|
||||
{
|
||||
var idCategory = current.IdCategory;
|
||||
if (idCategory == WellOperationCategory.IdSlide
|
||||
&& current.ExtraData[DetectorDrilling.ExtraDataKeyHasOscillation] is bool hasOscillation
|
||||
&& hasOscillation)
|
||||
return "Бурение в слайде с осцилляцией";
|
||||
|
||||
var category = wellOperationCategories.FirstOrDefault(o => o.Id == current.IdCategory);
|
||||
|
||||
if(category is not null)
|
||||
return category.Name;
|
||||
|
||||
return $"Операция №{idCategory}";
|
||||
}
|
||||
|
||||
private static string GetIdReasonOfEnd(int idReasonOfEnd)
|
||||
=> idReasonOfEnd switch {
|
||||
0 => "Не определена",
|
||||
1 => "Не определено начало операции",
|
||||
101 => "Разница глубин забоя и положением долота",
|
||||
300 => "Низкое давление",
|
||||
_ => idReasonOfEnd.ToString($"Причина № {idReasonOfEnd}"),
|
||||
|
||||
};
|
||||
|
||||
private async Task<Stream> GetExcelTemplateStreamAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
string resourceName = Assembly.GetExecutingAssembly()
|
||||
@ -152,7 +190,28 @@ public class DetectedOperationExportService
|
||||
return memoryStream;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<DetectedOperation>> DetectOperationsAsync(int idTelemetry, DateTimeOffset begin,
|
||||
private static string CreateComment(OperationDetectorResult operationDetectorResult)
|
||||
{
|
||||
var operation = operationDetectorResult.Operation;
|
||||
switch (operation.IdCategory)
|
||||
{
|
||||
case WellOperationCategory.IdRotor:
|
||||
case WellOperationCategory.IdSlide:
|
||||
var comment = "";
|
||||
if (operation.ExtraData.TryGetValue(DetectorDrilling.ExtraDataKeyAvgRotorSpeed, out object? oAvgRotorSpeed))
|
||||
comment += $"Средняя скорость оборотов ротора: {oAvgRotorSpeed}\r\n";
|
||||
|
||||
if (operation.ExtraData.TryGetValue(DetectorDrilling.ExtraDataKeyDispersionOfNormalizedRotorSpeed, out object? oDispersionOfNormalizedRotorSpeed))
|
||||
comment += $"Дисперсия нормированных оборотов ротора: {oDispersionOfNormalizedRotorSpeed}";
|
||||
|
||||
return comment;
|
||||
|
||||
default:
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<OperationDetectorResult>> DetectOperationsAsync(int idTelemetry, DateTimeOffset begin,
|
||||
CancellationToken token)
|
||||
{
|
||||
var query = dbContext.TelemetryDataSaub
|
||||
@ -173,7 +232,7 @@ public class DetectedOperationExportService
|
||||
.OrderBy(d => d.DateTime);
|
||||
|
||||
var startDate = begin;
|
||||
var detectedOperations = new List<DetectedOperation>(8);
|
||||
var detectedOperationResults = new List<OperationDetectorResult>(8);
|
||||
DetectedOperation? lastDetectedOperation = null;
|
||||
const int minOperationLength = 5;
|
||||
const int maxDetectorsInterpolationFrameLength = 30;
|
||||
@ -191,26 +250,21 @@ public class DetectedOperationExportService
|
||||
var isDetected = false;
|
||||
var positionBegin = 0;
|
||||
var positionEnd = data.Length - gap;
|
||||
var step = 10;
|
||||
while (positionEnd > positionBegin)
|
||||
{
|
||||
step++;
|
||||
foreach (var detector in detectors)
|
||||
{
|
||||
if (!detector.TryDetect(idTelemetry, data, positionBegin, positionEnd, lastDetectedOperation, out var result))
|
||||
continue;
|
||||
|
||||
detectedOperations.Add(result!.Operation);
|
||||
lastDetectedOperation = result.Operation;
|
||||
detectedOperationResults.Add(result!);
|
||||
lastDetectedOperation = result!.Operation;
|
||||
isDetected = true;
|
||||
step = 1;
|
||||
positionBegin = result.TelemetryEnd;
|
||||
break;
|
||||
}
|
||||
|
||||
if (step > 20)
|
||||
step = 10;
|
||||
positionBegin += step;
|
||||
positionBegin += 1;
|
||||
}
|
||||
|
||||
if (isDetected)
|
||||
@ -219,6 +273,6 @@ public class DetectedOperationExportService
|
||||
startDate = data[positionEnd].DateTime;
|
||||
}
|
||||
|
||||
return detectedOperations;
|
||||
return detectedOperationResults;
|
||||
}
|
||||
}
|
@ -201,7 +201,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
|
||||
if (well?.IdTelemetry is null || well.Timezone is null)
|
||||
return 0;
|
||||
|
||||
var query = BuildQuery(well, request);
|
||||
var query = BuildQueryBase(well, request);
|
||||
|
||||
if (query is null)
|
||||
return 0;
|
||||
@ -232,13 +232,10 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
|
||||
};
|
||||
}
|
||||
|
||||
private IQueryable<DetectedOperation>? BuildQuery(WellDto well, DetectedOperationRequest request)
|
||||
{
|
||||
if (well?.IdTelemetry is null || well.Timezone is null)
|
||||
return null;
|
||||
|
||||
private IQueryable<DetectedOperation> BuildQueryBase(WellDto well, DetectedOperationRequest request)
|
||||
{
|
||||
var query = db.Set<DetectedOperation>()
|
||||
.Include(o => o.OperationCategory)
|
||||
.Where(o => o.IdTelemetry == well.IdTelemetry);
|
||||
|
||||
if (request is not null)
|
||||
@ -262,6 +259,14 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
|
||||
query = query.Where(o => o.IdUsersAtStart == request.EqIdTelemetryUser);
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
private IQueryable<DetectedOperation> BuildQuery(WellDto well, DetectedOperationRequest request)
|
||||
{
|
||||
IQueryable<DetectedOperation> query = BuildQueryBase(well, request)
|
||||
.Include(o => o.OperationCategory);
|
||||
|
||||
if (request?.SortFields?.Any() == true)
|
||||
{
|
||||
query = query.SortBy(request.SortFields);
|
||||
|
@ -1,11 +1,11 @@
|
||||
using AsbCloudDb.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
|
||||
{
|
||||
|
||||
internal abstract class DetectorAbstract
|
||||
public abstract class DetectorAbstract
|
||||
{
|
||||
private readonly int stepLength = 3;
|
||||
|
||||
@ -35,9 +35,8 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
|
||||
|
||||
protected const int IdReasonOfEnd_Custom1 = 10_000;
|
||||
|
||||
public abstract Func<DetectableTelemetry[], int, int, int> GetIdOperation { get; }
|
||||
|
||||
public bool TryDetect(int idTelemetry, DetectableTelemetry[] telemetry, int begin, int end, DetectedOperation? previousOperation, out OperationDetectorResult? result)
|
||||
public bool TryDetect(int idTelemetry, DetectableTelemetry[] telemetry, int begin, int end, DetectedOperation? previousOperation,
|
||||
out OperationDetectorResult? result)
|
||||
{
|
||||
// Проверка соответствия критерию начала операции
|
||||
if (DetectBegin(telemetry, begin, previousOperation))
|
||||
@ -53,83 +52,141 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
|
||||
|
||||
idReasonOfEnd = DetectEnd(telemetry, positionEnd, previousOperation);
|
||||
|
||||
if(idReasonOfEnd is IdReasonOfEnd_DeltaDepthIsHi or IdReasonOfEnd_PressureIsLo &&
|
||||
!IsValidByWellDepthDoesNotChange(telemetry, begin, positionEnd))
|
||||
break;
|
||||
|
||||
if (idReasonOfEnd != IdReasonOfEnd_NotDetected)
|
||||
break;
|
||||
}
|
||||
|
||||
result = null;
|
||||
result = MakeOperation(idTelemetry, telemetry, begin, positionEnd, idReasonOfEnd);
|
||||
return true;
|
||||
}
|
||||
var (Begin, End) = RefineEdges(telemetry, begin, positionEnd);
|
||||
|
||||
if (!IsValidTelemetryRange(telemetry, Begin, End))
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
result = MakeOperationDetectorResult(idTelemetry, telemetry, Begin, End, idReasonOfEnd);
|
||||
|
||||
return IsValidOperationDetectorResult(result);
|
||||
}
|
||||
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual bool IsValidTelemetryRange(DetectableTelemetry[] telemetry, int begin, int end)
|
||||
=> end - begin > 1;
|
||||
|
||||
protected virtual (int Begin, int End) RefineEdges(DetectableTelemetry[] telemetry, int begin, int end)
|
||||
=> (begin, end);
|
||||
|
||||
protected virtual bool IsValidOperationDetectorResult(OperationDetectorResult operationDetectorResult)
|
||||
=> operationDetectorResult.Operation.DateEnd - operationDetectorResult.Operation.DateStart > TimeSpan.FromSeconds(3);
|
||||
|
||||
protected abstract bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation);
|
||||
|
||||
protected virtual int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
|
||||
=> DetectBegin(telemetry, position, previousOperation)
|
||||
? IdReasonOfEnd_NotDetected
|
||||
: IdReasonOfEnd_NotDetectBegin;
|
||||
|
||||
private OperationDetectorResult MakeOperation(int idTelemetry, DetectableTelemetry[] telemetry, int begin, int end, int idReasonOfEnd)
|
||||
private OperationDetectorResult MakeOperationDetectorResult(
|
||||
int idTelemetry,
|
||||
DetectableTelemetry[] telemetry,
|
||||
int begin,
|
||||
int end,
|
||||
int idReasonOfEnd)
|
||||
{
|
||||
var pBegin = telemetry[begin];
|
||||
var pEnd = telemetry[end];
|
||||
var operation = MakeDetectedOperation(idTelemetry, telemetry, begin, end);
|
||||
|
||||
operation.ExtraData["IdReasonOfEnd"] = idReasonOfEnd;
|
||||
|
||||
var result = new OperationDetectorResult
|
||||
{
|
||||
TelemetryBegin = begin,
|
||||
TelemetryEnd = end,
|
||||
Operation = new DetectedOperation
|
||||
Operation = operation,
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private DetectedOperation MakeDetectedOperation(int idTelemetry, DetectableTelemetry[] telemetry, int begin, int end)
|
||||
{
|
||||
var pBegin = telemetry[begin];
|
||||
var pEnd = telemetry[end];
|
||||
var (IdCategory, ExtraData) = GetSpecificInformation(telemetry, begin, end);
|
||||
var operation = new DetectedOperation
|
||||
{
|
||||
IdCategory = IdCategory,
|
||||
IdTelemetry = idTelemetry,
|
||||
IdCategory = GetIdOperation.Invoke(telemetry, begin, end),
|
||||
IdUsersAtStart = pBegin.IdUser ?? -1,
|
||||
DateStart = pBegin.DateTime,
|
||||
DateEnd = pEnd.DateTime,
|
||||
DepthStart = (double)pBegin.WellDepth,
|
||||
DepthEnd = (double)pEnd.WellDepth,
|
||||
ExtraData = ExtraData,
|
||||
Value = CalcValue(telemetry, begin, end),
|
||||
IdReasonOfEnd = idReasonOfEnd,
|
||||
},
|
||||
EnabledSubsystems = DetectEnabledSubsystems(telemetry, begin, end)
|
||||
};
|
||||
return result;
|
||||
|
||||
return operation;
|
||||
}
|
||||
|
||||
protected abstract bool IsValid(DetectableTelemetry[] telemetry, int begin, int end);
|
||||
/// <summary>
|
||||
/// Получение информации специфичной для конкретного детектора
|
||||
/// IdCategory - одна из констант WellOperationCategory
|
||||
/// ExtraData - дополнительная информация для отладки алгоритмов авто определения
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected abstract (int IdCategory, IDictionary<string, object> ExtraData) GetSpecificInformation(DetectableTelemetry[] telemetry, int begin, int end);
|
||||
|
||||
/// <summary>
|
||||
/// Расчет ключевого параметра операции
|
||||
/// </summary>
|
||||
/// <param name="telemetry"></param>
|
||||
/// <param name="begin"></param>
|
||||
/// <param name="end"></param>
|
||||
/// <returns></returns>
|
||||
protected abstract double CalcValue(DetectableTelemetry[] telemetry, int begin, int end);
|
||||
|
||||
/// <summary>
|
||||
/// Среднее арифметическое
|
||||
/// Определение включенных подсистем во время выполнения операции
|
||||
/// </summary>
|
||||
/// <param name="yGetter"></param>
|
||||
/// <param name="telemetry"></param>
|
||||
/// <param name="begin"></param>
|
||||
/// <param name="fragmentLength"></param>
|
||||
/// <param name="end"></param>
|
||||
/// <returns></returns>
|
||||
public static double CalcAvgAppr(
|
||||
Func<DetectableTelemetry, double> yGetter,
|
||||
DetectableTelemetry[] telemetry,
|
||||
int begin,
|
||||
int fragmentLength)
|
||||
private static int DetectEnabledSubsystems(DetectableTelemetry[] telemetry, int begin, int end)
|
||||
{
|
||||
var end = begin + fragmentLength;
|
||||
end = end < telemetry.Length
|
||||
? end
|
||||
: telemetry.Length;
|
||||
var subData = telemetry[begin..end].Select(yGetter);
|
||||
if (end - begin > 10)
|
||||
var enabledSubsystems = 0;
|
||||
|
||||
for (var i = begin; i < end; i += 2)
|
||||
{
|
||||
var ratio = (end - begin) / 5;
|
||||
subData = subData.Where((_,i) => i % ratio > 0);
|
||||
var mode = telemetry[i].Mode;
|
||||
|
||||
if(mode == 1)
|
||||
enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoRotor;
|
||||
|
||||
if (mode == 3)
|
||||
enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoSlide;
|
||||
|
||||
if (mode == 2)
|
||||
enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoConditionig;
|
||||
|
||||
if (mode == 4)
|
||||
enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoSinking;
|
||||
|
||||
if (mode == 5)
|
||||
enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoLifting;
|
||||
|
||||
if (mode == 6)
|
||||
enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoLiftingWithConditionig;
|
||||
|
||||
if (mode == 10)
|
||||
enabledSubsystems |= (int)DetectedOperation.EnabledSubsystemsFlags.AutoBlocknig;
|
||||
}
|
||||
|
||||
var avg = subData.Average();
|
||||
return avg;
|
||||
return enabledSubsystems;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -210,6 +267,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
|
||||
max = itemValue;
|
||||
sum += itemValue;
|
||||
}
|
||||
|
||||
return (min, max, sum, end - begin);
|
||||
}
|
||||
|
||||
@ -267,6 +325,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
|
||||
if (max - min > deviation)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -302,6 +361,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
|
||||
if (max - min > deviation)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -323,6 +383,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
|
||||
if (Math.Abs(beginPointValue - itemValue) > deviation)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -344,10 +405,9 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
|
||||
if (itemValue - beginPointValue > deviation)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,68 +1,66 @@
|
||||
using System;
|
||||
using AsbCloudDb.Model;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Проработка перед наращиванием
|
||||
/// </summary>
|
||||
internal class DetectorDevelopment : DetectorAbstract
|
||||
{
|
||||
protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end)
|
||||
=> CalcDeltaMinutes(telemetry, begin, end);
|
||||
|
||||
public override Func<DetectableTelemetry[], int, int, int> GetIdOperation => (_, _, _)
|
||||
=> WellOperationCategory.IdDevelopment;
|
||||
|
||||
protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
|
||||
{
|
||||
if (previousOperation?.IdCategory == WellOperationCategory.IdSlipsTime)
|
||||
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 int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
|
||||
{
|
||||
var point0 = telemetry[position];
|
||||
var delta = point0.WellDepth - point0.BitDepth;
|
||||
if (delta < 0.03d || delta > 30)
|
||||
return IdReasonOfEnd_DeltaDepthOutOfRange;
|
||||
|
||||
if (point0.Pressure < 15)
|
||||
return IdReasonOfEnd_PressureIsLo;
|
||||
|
||||
if (point0.BlockPosition > 31)
|
||||
return IdReasonOfEnd_BlockPositionIsHi;
|
||||
|
||||
if (point0.RotorSpeed < 10)
|
||||
return IdReasonOfEnd_RotorSpeedIsLo;
|
||||
|
||||
return IdReasonOfEnd_NotDetected;
|
||||
}
|
||||
|
||||
protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end)
|
||||
=> IsValidByWellDepthDoesNotChange(telemetry, begin, end);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// using System;
|
||||
// using AsbCloudDb.Model;
|
||||
//
|
||||
// namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
|
||||
// {
|
||||
//
|
||||
// /// <summary>
|
||||
// /// Проработка перед наращиванием
|
||||
// /// </summary>
|
||||
// internal class DetectorDevelopment : DetectorAbstract
|
||||
// {
|
||||
// protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end)
|
||||
// => CalcDeltaMinutes(telemetry, begin, end);
|
||||
//
|
||||
// public override Func<DetectableTelemetry[], int, int, int> GetIdOperation => (_, _, _)
|
||||
// => WellOperationCategory.IdDevelopment;
|
||||
//
|
||||
// protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
|
||||
// {
|
||||
// if (previousOperation?.IdCategory == WellOperationCategory.IdSlipsTime)
|
||||
// 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 int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
|
||||
// {
|
||||
// var point0 = telemetry[position];
|
||||
// var delta = point0.WellDepth - point0.BitDepth;
|
||||
// if (delta < 0.03d || delta > 30)
|
||||
// return IdReasonOfEnd_DeltaDepthOutOfRange;
|
||||
//
|
||||
// if (point0.Pressure < 15)
|
||||
// return IdReasonOfEnd_PressureIsLo;
|
||||
//
|
||||
// if (point0.BlockPosition > 31)
|
||||
// return IdReasonOfEnd_BlockPositionIsHi;
|
||||
//
|
||||
// if (point0.RotorSpeed < 10)
|
||||
// return IdReasonOfEnd_RotorSpeedIsLo;
|
||||
//
|
||||
// return IdReasonOfEnd_NotDetected;
|
||||
// }
|
||||
//
|
||||
// protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end)
|
||||
// => IsValidByWellDepthDoesNotChange(telemetry, begin, end);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
|
@ -1,12 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using AsbCloudDb.Model;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors;
|
||||
|
||||
internal class DetectorDrilling : DetectorAbstract
|
||||
public class DetectorDrilling : DetectorAbstract
|
||||
{
|
||||
public override Func<DetectableTelemetry[], int, int, int> GetIdOperation => DefineDrillingOperation;
|
||||
private const double dispersionOfNormalizedRotorSpeedThreshold = 0.2d;
|
||||
public const string ExtraDataKeyHasOscillation = "hasOscillation";
|
||||
public const string ExtraDataKeyDispersionOfNormalizedRotorSpeed = "dispersionOfNormalizedRotorSpeed";
|
||||
public const string ExtraDataKeyAvgRotorSpeed = "avgRotorSpeed";
|
||||
public const string ExtraDataKeyIsAfbEnabled = "isAfbEnabled";
|
||||
|
||||
protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
|
||||
{
|
||||
@ -15,10 +21,7 @@ internal class DetectorDrilling : DetectorAbstract
|
||||
if (delta > 0.03d)
|
||||
return false;
|
||||
|
||||
if (point0.Pressure < 25)
|
||||
return false;
|
||||
|
||||
if (point0.RotorSpeed < 5)
|
||||
if (point0.Pressure < 18)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -32,32 +35,58 @@ internal class DetectorDrilling : DetectorAbstract
|
||||
if (delta > 0.03d)
|
||||
return IdReasonOfEnd_DeltaDepthIsHi;
|
||||
|
||||
if (point0.Pressure < 25)
|
||||
if (point0.Pressure < 18)
|
||||
return IdReasonOfEnd_PressureIsLo;
|
||||
|
||||
return IdReasonOfEnd_NotDetected;
|
||||
}
|
||||
|
||||
protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end)
|
||||
=> IsValidByWellDepthIncreasing(telemetry, begin, end);
|
||||
|
||||
protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end)
|
||||
=> CalcRop(telemetry, begin, end);
|
||||
|
||||
private static int DefineDrillingOperation(DetectableTelemetry[] telemetry, int begin, int end)
|
||||
protected override bool IsValidOperationDetectorResult(OperationDetectorResult operationDetectorResult) =>
|
||||
Math.Abs(operationDetectorResult.Operation.DepthStart - operationDetectorResult.Operation.DepthEnd) > 0.01;
|
||||
|
||||
protected override (int Begin, int End) RefineEdges(DetectableTelemetry[] telemetry, int begin, int end)
|
||||
{
|
||||
const int idSlideWithOscillation = 12000;
|
||||
var i = end;
|
||||
for (; i > begin + 1; i--)
|
||||
if (telemetry[i].WellDepth - telemetry[i - 1].WellDepth > 0.001d)
|
||||
break;
|
||||
|
||||
return (begin, i);
|
||||
}
|
||||
|
||||
protected override (int IdCategory, IDictionary<string, object> ExtraData) GetSpecificInformation(DetectableTelemetry[] telemetry, int begin, int end)
|
||||
{
|
||||
var (avgRotorSpeed, dispersionOfNormalizedRotorSpeed) = CalcCriteries(telemetry, begin, end);
|
||||
var idCategory = GetIdOperation(avgRotorSpeed, dispersionOfNormalizedRotorSpeed);
|
||||
var extraData = new Dictionary<string, object>();
|
||||
extraData[ExtraDataKeyAvgRotorSpeed] = avgRotorSpeed;
|
||||
extraData[ExtraDataKeyDispersionOfNormalizedRotorSpeed] = dispersionOfNormalizedRotorSpeed;
|
||||
extraData[ExtraDataKeyHasOscillation] = dispersionOfNormalizedRotorSpeed > dispersionOfNormalizedRotorSpeedThreshold;
|
||||
return (idCategory, extraData);
|
||||
}
|
||||
|
||||
private static (double avgRotorSpeed, double dispersionOfNormalizedRotorSpeed) CalcCriteries(DetectableTelemetry[] telemetry, int begin, int end)
|
||||
{
|
||||
var telemetryRange = telemetry[begin..end];
|
||||
|
||||
var avgRotorSpeed = telemetryRange.Average(t => t.RotorSpeed);
|
||||
var dispersion = telemetryRange.Average(t => Math.Pow(t.RotorSpeed / avgRotorSpeed - 1, 2));
|
||||
return (avgRotorSpeed, dispersion);
|
||||
}
|
||||
|
||||
if (avgRotorSpeed < 10)
|
||||
private static int GetIdOperation(double avgRotorSpeed, double dispersionOfNormalizedRotorSpeed)
|
||||
{
|
||||
const int idSlideWithOscillation = WellOperationCategory.IdSlide;
|
||||
|
||||
if (avgRotorSpeed < 5)
|
||||
return WellOperationCategory.IdSlide;
|
||||
|
||||
var despersion = telemetryRange
|
||||
.Average(t => Math.Pow((t.RotorSpeed - avgRotorSpeed) / avgRotorSpeed, 2));
|
||||
if (dispersionOfNormalizedRotorSpeed < dispersionOfNormalizedRotorSpeedThreshold)
|
||||
return WellOperationCategory.IdRotor;
|
||||
else
|
||||
return idSlideWithOscillation;
|
||||
}
|
||||
|
||||
return despersion < 0.2d ? WellOperationCategory.IdRotor : idSlideWithOscillation;
|
||||
}
|
||||
}
|
@ -1,16 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using AsbCloudDb.Model;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
|
||||
{
|
||||
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors;
|
||||
|
||||
internal class DetectorSlipsTime : DetectorAbstract
|
||||
public class DetectorSlipsTime : DetectorAbstract
|
||||
{
|
||||
protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end)
|
||||
=> CalcDeltaMinutes(telemetry, begin, end);
|
||||
|
||||
public override Func<DetectableTelemetry[], int, int, int> GetIdOperation => (_, _, _) => WellOperationCategory.IdSlipsTime;
|
||||
|
||||
protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
|
||||
{
|
||||
var point0 = telemetry[position];
|
||||
@ -30,10 +28,12 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end)
|
||||
=> IsValidByWellDepthDoesNotChange(telemetry, begin, end);
|
||||
protected override (int IdCategory, IDictionary<string, object> ExtraData) GetSpecificInformation(DetectableTelemetry[] telemetry, int begin, int end)
|
||||
{
|
||||
return (WellOperationCategory.IdSlipsTime, new Dictionary<string, object>());
|
||||
}
|
||||
|
||||
|
||||
protected override bool IsValidOperationDetectorResult(OperationDetectorResult operationDetectorResult) =>
|
||||
Math.Abs(operationDetectorResult.Operation.DepthStart - operationDetectorResult.Operation.DepthEnd) > 0.01;
|
||||
}
|
||||
|
||||
|
@ -1,29 +1,27 @@
|
||||
Метод определения бурения
|
||||
# Алгоритм определения бурения в роторе, слайде, слайде с осцилляцией
|
||||
|
||||
## Метод определения операции бурения
|
||||
|
||||
Признак начала операции =
|
||||
|
||||
расстояние от долота до забоя < 0.03м И
|
||||
|
||||
давление > 25атм
|
||||
(расстояние от долота до забоя < 0.03м) И
|
||||
(давление > 18атм)
|
||||
|
||||
Признак окончания операции =
|
||||
(расстояние от долота до забоя > 0.03м) ИЛИ
|
||||
(давление < 18атм)
|
||||
|
||||
расстояние от долота до забоя > 0.03м ИЛИ
|
||||
## Валидация
|
||||
Для точного определения операции бурения, необходимо убрать диапазон в которых сработал признак окончания операции и не менялась глубина:
|
||||
Определили точку окончания операции исходя из Признак окончания операции.
|
||||
Определяем временной интервал, когда не менялась глубина (т.е. время шло, а глубина была неизменна)
|
||||
Определив начальную точку и точку окончания операции
|
||||
Исключаем этот интервал из операции.
|
||||
|
||||
давление < 25атм
|
||||
## Метод определения бурения в слайде
|
||||
Необходимо рассчитать средние обороты ротора за всю операцию бурения.
|
||||
Если среднее арифметическое больше константы (5 об/мин), то это бурение в роторе, если меньше, то это бурение в слайде.
|
||||
|
||||
Находим границы
|
||||
|
||||
После того когда мы нашли границы, мы должны определить операцию, тогда мы смотрим на забой точки окончания операций сравниваем с забоем точками начала операций:
|
||||
|
||||
Если они равны друг другу, то мы эту операцию дальше не обрабатываем, а выбрасываем.
|
||||
|
||||
Если они не равны, то у нас произошло увеличение забоя, значит эта операция бурения.
|
||||
|
||||
Дальше мы определяем как мы бурили в роторе или слайде, для этого нам необходимо рассчитать среднюю скорость(среднее арифметическое) за всю операцию бурения . Если среднее арифметическое больше константы (10 об/мин), то это бурение в роторе, если меньше, то это бурение в слайде.
|
||||
|
||||
Если бурение в роторе, то мы считаем только дисперсию нормированных оборотов ротора(по среднему значению). (Так как это может быть бурение в слайде с осцилляцией и выглядеть как бурение в роторе):
|
||||
|
||||
Если полученное значение меньше константы(0,2), то мы подтвердили что бурение в роторе.
|
||||
|
||||
Если полученное значение больше константы, то это бурение в слайде с осцилляцией.
|
||||
## Метод определения бурения в роторе, слайде с осцилляцией
|
||||
Необходимо рассчитать десперсию нормированных оборотов ротора по(по среднему значению)
|
||||
1. Если полученное значение больше константы(0,2), то мы подтвердили что бурение в роторе.
|
||||
2. Если полученное значение меньше константы, то это бурение в слайде с осцилляцией.
|
@ -16,11 +16,12 @@ public class WorkOperationDetection: Work
|
||||
{
|
||||
private static readonly DetectorAbstract[] detectors = new DetectorAbstract[]
|
||||
{
|
||||
new DetectorDrilling(),
|
||||
new DetectorSlipsTime()
|
||||
// new DetectorRotor(),
|
||||
// new DetectorSlide(),
|
||||
//new DetectorDevelopment(),
|
||||
//new DetectorTemplating(),
|
||||
new DetectorSlipsTime(),
|
||||
//new DetectorStaticSurveying(),
|
||||
//new DetectorFlashingBeforeConnection(),
|
||||
//new DetectorFlashing(),
|
||||
@ -95,6 +96,7 @@ public class WorkOperationDetection: Work
|
||||
{
|
||||
DateTime = d.DateTime,
|
||||
IdUser = d.IdUser,
|
||||
Mode = d.Mode,
|
||||
WellDepth = d.WellDepth,
|
||||
Pressure = d.Pressure,
|
||||
HookWeight = d.HookWeight,
|
||||
@ -116,7 +118,6 @@ public class WorkOperationDetection: Work
|
||||
{
|
||||
var data = await query
|
||||
.Where(d => d.DateTime > startDate)
|
||||
.Take(take)
|
||||
.ToArrayAsync(token);
|
||||
|
||||
if (data.Length < gap)
|
||||
@ -125,25 +126,21 @@ public class WorkOperationDetection: Work
|
||||
var isDetected = false;
|
||||
var positionBegin = 0;
|
||||
var positionEnd = data.Length - gap;
|
||||
var step = 10;
|
||||
while (positionEnd > positionBegin)
|
||||
{
|
||||
step ++;
|
||||
for (int i = 0; i < detectors.Length; i++)
|
||||
{
|
||||
if (detectors[i].TryDetect(idTelemetry, data, positionBegin, positionEnd, lastDetectedOperation, out OperationDetectorResult? result))
|
||||
foreach (var detector in detectors)
|
||||
{
|
||||
if (!detector.TryDetect(idTelemetry, data, positionBegin, positionEnd, lastDetectedOperation, out var result))
|
||||
continue;
|
||||
|
||||
detectedOperations.Add(result!.Operation);
|
||||
lastDetectedOperation = result.Operation;
|
||||
isDetected = true;
|
||||
step = 1;
|
||||
positionBegin = result.TelemetryEnd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (step > 20)
|
||||
step = 10;
|
||||
positionBegin += step;
|
||||
|
||||
positionBegin += 1;
|
||||
}
|
||||
|
||||
if (isDetected)
|
||||
|
@ -0,0 +1,290 @@
|
||||
using System.Collections.Generic;
|
||||
using AsbCloudDb.Model;
|
||||
using AsbCloudInfrastructure.Services.DetectOperations;
|
||||
using AsbCloudInfrastructure.Services.DetectOperations.Detectors;
|
||||
using Xunit;
|
||||
|
||||
namespace AsbCloudWebApi.Tests.Services.DetectedOperations.Detectors;
|
||||
|
||||
public class DetectorDrillingTests : DetectorDrilling
|
||||
{
|
||||
private const int idSlide = 5002;
|
||||
private const int idRotor = 5003;
|
||||
private const int idSlideWithOscillation = 12000;
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(TelemetryRangeDrillingRotor))]
|
||||
public void DefineDrillingOperation_ShouldReturn_DrillingRotor(DetectableTelemetry[] telemetryRange)
|
||||
{
|
||||
//act
|
||||
var result = GetIdOperation.Invoke(telemetryRange, 0, telemetryRange.Length);
|
||||
|
||||
//assert
|
||||
Assert.Equal(idRotor, result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(TelemetryRangeDrillingSlide))]
|
||||
public void DefineDrillingOperation_ShouldReturn_DrillingSlide(DetectableTelemetry[] telemetryRange)
|
||||
{
|
||||
//act
|
||||
var result = GetIdOperation.Invoke(telemetryRange, 0, telemetryRange.Length);
|
||||
|
||||
//assert
|
||||
Assert.Equal(idSlide, result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(TelemetryRangeDrillingSlideWithOscillation))]
|
||||
public void DefineDrillingOperation_ShouldReturn_DrillingSlideWithOscillation(DetectableTelemetry[] telemetryRange)
|
||||
{
|
||||
//act
|
||||
var result = GetIdOperation.Invoke(telemetryRange, 0, telemetryRange.Length);
|
||||
|
||||
//assert
|
||||
Assert.Equal(idSlideWithOscillation, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsValidOperationDetectorResult_ShouldReturn_True()
|
||||
{
|
||||
//arrange
|
||||
var operationDetectorResult = new OperationDetectorResult
|
||||
{
|
||||
Operation = new DetectedOperation
|
||||
{
|
||||
IdReasonOfEnd = IdReasonOfEnd_PressureIsLo,
|
||||
DepthStart = 5000,
|
||||
DepthEnd = 6000
|
||||
}
|
||||
};
|
||||
|
||||
//act
|
||||
var result = IsValidOperationDetectorResult(operationDetectorResult);
|
||||
|
||||
//assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsValidOperationDetectorResult_ShouldReturn_False()
|
||||
{
|
||||
//arrange
|
||||
var operationDetectorResult = new OperationDetectorResult
|
||||
{
|
||||
Operation = new DetectedOperation
|
||||
{
|
||||
IdReasonOfEnd = IdReasonOfEnd_PressureIsLo,
|
||||
DepthStart = 5000,
|
||||
DepthEnd = 5000
|
||||
}
|
||||
};
|
||||
|
||||
//act
|
||||
var result = IsValidOperationDetectorResult(operationDetectorResult);
|
||||
|
||||
//assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> TelemetryRangeDrillingRotor()
|
||||
{
|
||||
yield return new object[]
|
||||
{
|
||||
new[]
|
||||
{
|
||||
new DetectableTelemetry
|
||||
{
|
||||
WellDepth = 4.187f,
|
||||
Pressure = 27.815952f,
|
||||
HookWeight = 34.221367f,
|
||||
BlockPosition = 24.388f,
|
||||
BitDepth = 4.187f,
|
||||
RotorSpeed = 40.3f
|
||||
},
|
||||
new DetectableTelemetry
|
||||
{
|
||||
WellDepth = 4.232f,
|
||||
Pressure = 28.080372f,
|
||||
HookWeight = 34.162174f,
|
||||
BlockPosition = 24.343f,
|
||||
BitDepth = 4.232f,
|
||||
RotorSpeed = 40.3f
|
||||
},
|
||||
new DetectableTelemetry
|
||||
{
|
||||
WellDepth = 4.277f,
|
||||
Pressure = 29.047901f,
|
||||
HookWeight = 33.688717f,
|
||||
BlockPosition = 24.298f,
|
||||
BitDepth = 24.298f,
|
||||
RotorSpeed = 40.3f
|
||||
},
|
||||
new DetectableTelemetry
|
||||
{
|
||||
WellDepth = 4.309f,
|
||||
Pressure = 29.574032f,
|
||||
HookWeight = 33.692104f,
|
||||
BlockPosition = 24.266f,
|
||||
BitDepth = 4.309f,
|
||||
RotorSpeed = 40.4f
|
||||
},
|
||||
new DetectableTelemetry
|
||||
{
|
||||
WellDepth = 4.324f,
|
||||
Pressure = 24.007977f,
|
||||
HookWeight = 34.838448f,
|
||||
BlockPosition = 24.251f,
|
||||
BitDepth = 4.324f,
|
||||
RotorSpeed = 40.5f
|
||||
},
|
||||
new DetectableTelemetry
|
||||
{
|
||||
WellDepth = 4.324f,
|
||||
Pressure = 24.04114f,
|
||||
HookWeight = 34.423424f,
|
||||
BlockPosition = 24.252f,
|
||||
BitDepth = 4.323f,
|
||||
RotorSpeed = 40.3f
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> TelemetryRangeDrillingSlide()
|
||||
{
|
||||
yield return new object[]
|
||||
{
|
||||
new[]
|
||||
{
|
||||
new DetectableTelemetry
|
||||
{
|
||||
WellDepth = 447.276001f,
|
||||
Pressure = 26.619421f,
|
||||
HookWeight = 40.9143829f,
|
||||
BlockPosition = 4.559f,
|
||||
BitDepth = 477.265991f,
|
||||
RotorSpeed = 0
|
||||
},
|
||||
new DetectableTelemetry
|
||||
{
|
||||
WellDepth = 477.289f,
|
||||
Pressure = 28.716f,
|
||||
HookWeight = 38.27f,
|
||||
BlockPosition = 4.5f,
|
||||
BitDepth = 477.289f,
|
||||
RotorSpeed = 0.1f
|
||||
},
|
||||
new DetectableTelemetry
|
||||
{
|
||||
WellDepth = 477.30899f,
|
||||
Pressure = 33.953495f,
|
||||
HookWeight = 38.27f,
|
||||
BlockPosition = 4.5359997f,
|
||||
BitDepth = 477.289001f,
|
||||
RotorSpeed = 0.1f
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> TelemetryRangeDrillingSlideWithOscillation()
|
||||
{
|
||||
yield return new object[]
|
||||
{
|
||||
new[]
|
||||
{
|
||||
new DetectableTelemetry
|
||||
{
|
||||
IdUser = 1,
|
||||
WellDepth = 415.306f,
|
||||
Pressure = 53.731934f,
|
||||
HookWeight = 41.049942f,
|
||||
BlockPosition = 28.666f,
|
||||
BitDepth = 415.293f,
|
||||
RotorSpeed = 0.3f
|
||||
},
|
||||
new DetectableTelemetry
|
||||
{
|
||||
IdUser = 1,
|
||||
WellDepth = 415.311f,
|
||||
Pressure = 57.660595f,
|
||||
HookWeight = 40.898712f,
|
||||
BlockPosition = 28.648f,
|
||||
BitDepth = 415.311f,
|
||||
RotorSpeed = 0.2f
|
||||
},
|
||||
new DetectableTelemetry
|
||||
{
|
||||
IdUser = 1,
|
||||
WellDepth = 415.326f,
|
||||
Pressure = 59.211086f,
|
||||
HookWeight = 40.882797f,
|
||||
BlockPosition = 28.633f,
|
||||
BitDepth = 415.326f,
|
||||
RotorSpeed = 0.1f
|
||||
},
|
||||
new DetectableTelemetry
|
||||
{
|
||||
IdUser = 1,
|
||||
WellDepth = 415.344f,
|
||||
Pressure = 59.484406f,
|
||||
HookWeight = 40.91972f,
|
||||
BlockPosition = 28.615f,
|
||||
BitDepth = 415.344f,
|
||||
RotorSpeed = 0.2f
|
||||
},
|
||||
new DetectableTelemetry
|
||||
{
|
||||
IdUser = 1,
|
||||
WellDepth = 415.364f,
|
||||
Pressure = 60.739918f,
|
||||
HookWeight = 40.795666f,
|
||||
BlockPosition = 28.595f,
|
||||
BitDepth = 415.364f,
|
||||
RotorSpeed = 4.5f
|
||||
},
|
||||
new DetectableTelemetry
|
||||
{
|
||||
IdUser = 1,
|
||||
WellDepth = 415.378f,
|
||||
Pressure = 62.528984f,
|
||||
HookWeight = 40.52114f,
|
||||
BlockPosition = 28.581f,
|
||||
BitDepth = 415.378f,
|
||||
RotorSpeed = 22.6f
|
||||
},
|
||||
new DetectableTelemetry
|
||||
{
|
||||
IdUser = 1,
|
||||
WellDepth = 415.392f,
|
||||
Pressure = 67.0039f,
|
||||
HookWeight = 38.878895f,
|
||||
BlockPosition = 28.569f,
|
||||
BitDepth = 415.39f,
|
||||
RotorSpeed = 50f
|
||||
},
|
||||
new DetectableTelemetry
|
||||
{
|
||||
IdUser = 1,
|
||||
WellDepth = 415.392f,
|
||||
Pressure = 65.72418f,
|
||||
HookWeight = 42.53173f,
|
||||
BlockPosition = 28.622f,
|
||||
BitDepth = 415.337f,
|
||||
RotorSpeed = 93f
|
||||
},
|
||||
new DetectableTelemetry
|
||||
{
|
||||
IdUser = 1,
|
||||
WellDepth = 415.392f,
|
||||
Pressure = 56.82195f,
|
||||
HookWeight = 43.15844f,
|
||||
BlockPosition = 28.704f,
|
||||
BitDepth = 415.255f,
|
||||
RotorSpeed = 71.5f
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -60,7 +60,7 @@ namespace AsbCloudWebApi.Controllers.SAUB
|
||||
[FromQuery] DetectedOperationRequest request,
|
||||
CancellationToken token)
|
||||
{
|
||||
if (!await UserHasAccesToWellAsync(request.IdWell, token))
|
||||
if (!await UserHasAccessToWellAsync(request.IdWell, token))
|
||||
return Forbid();
|
||||
|
||||
var result = await detectedOperationService.GetAsync(request, token);
|
||||
@ -79,7 +79,7 @@ namespace AsbCloudWebApi.Controllers.SAUB
|
||||
[FromQuery] DetectedOperationRequest request,
|
||||
CancellationToken token)
|
||||
{
|
||||
if (!await UserHasAccesToWellAsync(request.IdWell, token))
|
||||
if (!await UserHasAccessToWellAsync(request.IdWell, token))
|
||||
return Forbid();
|
||||
|
||||
var result = await detectedOperationService.GetOperationsStatAsync(request, token);
|
||||
@ -101,14 +101,14 @@ namespace AsbCloudWebApi.Controllers.SAUB
|
||||
[FromQuery] DetectedOperationRequest request,
|
||||
CancellationToken token)
|
||||
{
|
||||
if (!await UserHasAccesToWellAsync(request.IdWell, token))
|
||||
if (!await UserHasAccessToWellAsync(request.IdWell, token))
|
||||
return Forbid();
|
||||
|
||||
var result = await detectedOperationService.DeleteAsync(request, token);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
protected async Task<bool> UserHasAccesToWellAsync(int idWell, CancellationToken token)
|
||||
protected async Task<bool> UserHasAccessToWellAsync(int idWell, CancellationToken token)
|
||||
{
|
||||
var idCompany = User.GetCompanyId();
|
||||
if (idCompany is not null &&
|
||||
|
@ -7,6 +7,7 @@ namespace AsbCloudWebApi
|
||||
{
|
||||
// Uncomment next line to find wired exceptions by tracing.
|
||||
//static TraceListenerView trace4debug = new TraceListenerView();
|
||||
enum A { a = 1 << 2 }
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user