Merge branch 'dev' into feature/#13123485--import-fact-trajectories

This commit is contained in:
on.nemtina 2023-11-22 22:19:00 +05:00
commit 68294474eb
34 changed files with 9754 additions and 672 deletions

View File

@ -28,7 +28,7 @@ namespace AsbCloudApp.Data
/// <summary> /// <summary>
/// Название типа компании /// Название типа компании
/// </summary> /// </summary>
[StringLength(30, MinimumLength = 1, ErrorMessage = "Допустимое имя типа компании от 1 до 30 символов")] [StringLength(255, MinimumLength = 1, ErrorMessage = "Допустимое имя типа компании от 1 до 255 символов")]
public string? CompanyTypeCaption { get; set; } = null!; public string? CompanyTypeCaption { get; set; } = null!;
} }

View File

@ -34,16 +34,14 @@ namespace AsbCloudApp.Data.User
/// <summary> /// <summary>
/// Email /// Email
/// </summary> /// </summary>
[Required]
[RegularExpression(@"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$", ErrorMessage = "Некорректный email")] [RegularExpression(@"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$", ErrorMessage = "Некорректный email")]
public string Email { get; set; } = null!; public string? Email { get; set; }
/// <summary> /// <summary>
/// Phone /// Phone
/// </summary> /// </summary>
[Required]
[RegularExpression(@"^(?:\+7|8)\s?(?:\(\d{3}\)|\d{3})\s?\d{3}-?\d{2}-?\d{2}$", ErrorMessage = "Некорректный номер телефона")] [RegularExpression(@"^(?:\+7|8)\s?(?:\(\d{3}\)|\d{3})\s?\d{3}-?\d{2}-?\d{2}$", ErrorMessage = "Некорректный номер телефона")]
public string Phone { get; set; } = null!; public string? Phone { get; set; }
/// <summary> /// <summary>
/// Должность /// Должность

View File

@ -1,9 +1,7 @@
using AsbCloudApp.Data; using AsbCloudApp.Data;
using AsbCloudApp.Data.DetectedOperation; using AsbCloudApp.Data.DetectedOperation;
using AsbCloudApp.Requests; using AsbCloudApp.Requests;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -62,13 +60,5 @@ namespace AsbCloudApp.Services
/// <param name="token"></param> /// <param name="token"></param>
/// <returns></returns> /// <returns></returns>
Task<IEnumerable<DetectedOperationStatDto>?> GetOperationsStatAsync(DetectedOperationRequest request, CancellationToken token); Task<IEnumerable<DetectedOperationStatDto>?> GetOperationsStatAsync(DetectedOperationRequest request, CancellationToken token);
/// <summary>
/// Выгрузка в Excel
/// </summary>
/// <param name="idsWells"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<Stream> ExportAsync(IEnumerable<int> idsWells, CancellationToken token);
} }
} }

View File

@ -0,0 +1,67 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class UpdateTable_t_contact_Set_Email_and_Phone_Nullable : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "phone",
table: "t_contact",
type: "character varying(50)",
maxLength: 50,
nullable: true,
comment: "номер телефона",
oldClrType: typeof(string),
oldType: "character varying(50)",
oldMaxLength: 50,
oldComment: "номер телефона");
migrationBuilder.AlterColumn<string>(
name: "email",
table: "t_contact",
type: "character varying(255)",
maxLength: 255,
nullable: true,
comment: "email",
oldClrType: typeof(string),
oldType: "character varying(255)",
oldMaxLength: 255,
oldComment: "email");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "phone",
table: "t_contact",
type: "character varying(50)",
maxLength: 50,
nullable: false,
defaultValue: "",
comment: "номер телефона",
oldClrType: typeof(string),
oldType: "character varying(50)",
oldMaxLength: 50,
oldNullable: true,
oldComment: "номер телефона");
migrationBuilder.AlterColumn<string>(
name: "email",
table: "t_contact",
type: "character varying(255)",
maxLength: 255,
nullable: false,
defaultValue: "",
comment: "email",
oldClrType: typeof(string),
oldType: "character varying(255)",
oldMaxLength: 255,
oldNullable: true,
oldComment: "email");
}
}
}

View File

@ -214,7 +214,6 @@ namespace AsbCloudDb.Migrations
.HasComment("компания"); .HasComment("компания");
b.Property<string>("Email") b.Property<string>("Email")
.IsRequired()
.HasMaxLength(255) .HasMaxLength(255)
.HasColumnType("character varying(255)") .HasColumnType("character varying(255)")
.HasColumnName("email") .HasColumnName("email")
@ -240,7 +239,6 @@ namespace AsbCloudDb.Migrations
.HasComment("ключ скважины"); .HasComment("ключ скважины");
b.Property<string>("Phone") b.Property<string>("Phone")
.IsRequired()
.HasMaxLength(50) .HasMaxLength(50)
.HasColumnType("character varying(50)") .HasColumnType("character varying(50)")
.HasColumnName("phone") .HasColumnName("phone")

View File

@ -26,11 +26,11 @@ namespace AsbCloudDb.Model
[Column("email"), Comment("email")] [Column("email"), Comment("email")]
[StringLength(255)] [StringLength(255)]
public string Email { get; set; } = string.Empty; public string? Email { get; set; }
[Column("phone"), Comment("номер телефона")] [Column("phone"), Comment("номер телефона")]
[StringLength(50)] [StringLength(50)]
public string Phone { get; set; } = string.Empty; public string? Phone { get; set; }
[Column("position"), Comment("должность")] [Column("position"), Comment("должность")]
[StringLength(255)] [StringLength(255)]

View File

@ -32,6 +32,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Services\DetectOperations\DetectOperations.xlsx" />
<EmbeddedResource Include="Services\DailyReport\DailyReportTemplate.xlsx" /> <EmbeddedResource Include="Services\DailyReport\DailyReportTemplate.xlsx" />
<EmbeddedResource Include="Services\DrillTestReport\DrillTestReportTemplate.xlsx" /> <EmbeddedResource Include="Services\DrillTestReport\DrillTestReportTemplate.xlsx" />
<EmbeddedResource Include="Services\Trajectory\Templates\FactTrajectoryTemplate.xlsx" /> <EmbeddedResource Include="Services\Trajectory\Templates\FactTrajectoryTemplate.xlsx" />

View File

@ -1,4 +1,5 @@
using AsbCloudApp.Data; using System;
using AsbCloudApp.Data;
using AsbCloudApp.Data.AutogeneratedDailyReport; using AsbCloudApp.Data.AutogeneratedDailyReport;
using AsbCloudApp.Data.DrillTestReport; using AsbCloudApp.Data.DrillTestReport;
using AsbCloudApp.Data.Manuals; using AsbCloudApp.Data.Manuals;
@ -40,7 +41,6 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System;
namespace AsbCloudInfrastructure namespace AsbCloudInfrastructure
{ {
@ -163,8 +163,10 @@ namespace AsbCloudInfrastructure
services.AddScoped<IAsbCloudDbContext>(provider => provider.GetRequiredService<AsbCloudDbContext>()); services.AddScoped<IAsbCloudDbContext>(provider => provider.GetRequiredService<AsbCloudDbContext>());
services.AddSingleton(new WitsInfoService()); services.AddSingleton(new WitsInfoService());
services.AddSingleton<ITelemetryDataCache<TelemetryDataSaubDto>>(provider => TelemetryDataCache<TelemetryDataSaubDto>.GetInstance<TelemetryDataSaub>(provider)); services.AddSingleton<ITelemetryDataCache<TelemetryDataSaubDto>>(provider =>
services.AddSingleton<ITelemetryDataCache<TelemetryDataSpinDto>>(provider => TelemetryDataCache<TelemetryDataSpinDto>.GetInstance<TelemetryDataSpin>(provider)); TelemetryDataCache<TelemetryDataSaubDto>.GetInstance<TelemetryDataSaub>(provider));
services.AddSingleton<ITelemetryDataCache<TelemetryDataSpinDto>>(provider =>
TelemetryDataCache<TelemetryDataSpinDto>.GetInstance<TelemetryDataSpin>(provider));
services.AddSingleton<IRequerstTrackerService, RequestTrackerService>(); services.AddSingleton<IRequerstTrackerService, RequestTrackerService>();
services.AddSingleton<PeriodicBackgroundWorker>(); services.AddSingleton<PeriodicBackgroundWorker>();
services.AddSingleton<BackgroundWorker>(); services.AddSingleton<BackgroundWorker>();
@ -300,6 +302,8 @@ namespace AsbCloudInfrastructure
services.AddTransient<IWellOperationExcelParser<WellOperationImportDefaultOptionsDto>, WellOperationDefaultExcelParser>(); services.AddTransient<IWellOperationExcelParser<WellOperationImportDefaultOptionsDto>, WellOperationDefaultExcelParser>();
services.AddTransient<IWellOperationExcelParser<WellOperationImportGazpromKhantosOptionsDto>, WellOperationGazpromKhantosExcelParser>(); services.AddTransient<IWellOperationExcelParser<WellOperationImportGazpromKhantosOptionsDto>, WellOperationGazpromKhantosExcelParser>();
services.AddTransient<DetectedOperationExportService>();
return services; return services;
} }

View File

@ -1,34 +1,78 @@
using AsbCloudApp.Data; using AsbCloudDb.Model;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using ClosedXML.Excel; using ClosedXML.Excel;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsbCloudInfrastructure.Services.DetectOperations.Detectors;
namespace AsbCloudInfrastructure.Services.DetectOperations namespace AsbCloudInfrastructure.Services.DetectOperations;
{
internal class DetectedOperationExportService public class DetectedOperationExportService
{ {
private readonly IAsbCloudDbContext db; private readonly DetectorAbstract[] detectors = { new DetectorDrilling(), new DetectorSlipsTime() };
private readonly IWellService wellService;
public DetectedOperationExportService(IAsbCloudDbContext db, IWellService wellService) private readonly IDictionary<int, string> domains = new Dictionary<int, string>
{ {
this.db = db; { 1, "https://cloud.digitaldrilling.ru" },
this.wellService = wellService; { 2, "https://cloud.autodrilling.ru" }
};
private const int headerRowsCount = 1;
private const string cellDepositName = "B1";
private const string cellClusterName = "B2";
private const string cellWellName = "B3";
private const string cellDeltaDate = "H2";
private const int columnOperationName = 1;
private const int columnDateStart = 2;
private const int columnDateEnd = 3;
private const int columnDuration = 4;
private const int columnDepthStart = 5;
private const int columnDepthEnd = 6;
private const int columnDeltaDepth = 7;
private const int columnDepth = 8;
private const int columnIdReasonOfEnd = 9;
private readonly IAsbCloudDbContext dbContext;
public DetectedOperationExportService(IAsbCloudDbContext dbContext)
{
this.dbContext = dbContext;
} }
public async Task<Stream> ExportAsync(IEnumerable<int> idsWells, CancellationToken token) public async Task<Stream> ExportAsync(int idWell, int idDomain, CancellationToken cancellationToken)
{ {
using var workbook = new XLWorkbook(XLEventTracking.Disabled); var well = await dbContext.Wells
.Include(w => w.Cluster)
.ThenInclude(c => c.Deposit)
.SingleOrDefaultAsync(w => w.Id == idWell, cancellationToken);
await AddSheetsAsync(workbook, idsWells, token); if (well is null)
throw new ArgumentNullException(nameof(well));
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);
return await GenerateExcelFileStreamAsync(well, idDomain, operations, cancellationToken);
}
private async Task<Stream> GenerateExcelFileStreamAsync(Well well, int idDomain, IEnumerable<DetectedOperation> detectedOperations,
CancellationToken cancellationToken)
{
using var excelTemplateStream = await GetExcelTemplateStreamAsync(cancellationToken);
using var workbook = new XLWorkbook(excelTemplateStream, XLEventTracking.Disabled);
await AddToWorkbookAsync(workbook, well, idDomain, detectedOperations, cancellationToken);
MemoryStream memoryStream = new MemoryStream(); MemoryStream memoryStream = new MemoryStream();
workbook.SaveAs(memoryStream, new SaveOptions { }); workbook.SaveAs(memoryStream, new SaveOptions { });
@ -36,85 +80,145 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
return memoryStream; return memoryStream;
} }
private async Task AddSheetsAsync(XLWorkbook workbook, IEnumerable<int> idsWells, CancellationToken token) private async Task AddToWorkbookAsync(XLWorkbook workbook, Well well, int idDomain, IEnumerable<DetectedOperation> detectedOperations,
CancellationToken cancellationToken)
{ {
if(!idsWells.Any()) const string sheetName = "Операции";
if (!detectedOperations.Any())
return; return;
var wells = idsWells.Select(i => wellService.GetOrDefault(i)) var sheet = workbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetName)
.Where(w => w is not null && w.IdTelemetry is not null) ?? throw new FileFormatException($"Книга excel не содержит листа {sheetName}.");
.Select(w => w!);
if (!wells.Any()) await AddToSheetAsync(sheet, well, idDomain, detectedOperations.OrderBy(x => x.DateStart).ThenBy(x => x.DepthStart).ToArray(),
return; cancellationToken);
}
var idsTelemetries = wells.Select(w => w.IdTelemetry); private async Task AddToSheetAsync(IXLWorksheet sheet, Well well, int idDomain, IList<DetectedOperation> detectedOperations,
if (!idsTelemetries.Any()) CancellationToken cancellationToken)
return; {
var wellOperationCategories = await dbContext.WellOperationCategories.ToListAsync(cancellationToken);
var operations = await db.DetectedOperations sheet.Cell(cellDepositName).Value = well.Cluster.Deposit.Caption;
.Include(o => o.OperationCategory) 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);
var timeZoneWell = TimeSpan.FromHours(well.Timezone.Hours);
for (int i = 0; i < detectedOperations.Count; i++)
{
var dateStart = detectedOperations[i].DateStart.ToOffset(timeZoneWell);
var dateEnd = detectedOperations[i].DateEnd.ToOffset(timeZoneWell);
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(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;
var link =
$"{domains[idDomain]}/well/{well.Id}/telemetry/monitoring?end={Uri.EscapeDataString(dateStart.AddSeconds(3544).ToString("yyyy-MM-ddTHH:mm:ss.fff"))}&range=3600";
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
: 0;
}
}
private async Task<Stream> GetExcelTemplateStreamAsync(CancellationToken cancellationToken)
{
string resourceName = Assembly.GetExecutingAssembly()
.GetManifestResourceNames()
.FirstOrDefault(n => n.EndsWith("DetectOperations.xlsx"))!;
using var stream = Assembly.GetExecutingAssembly()
.GetManifestResourceStream(resourceName)!;
var memoryStream = new MemoryStream();
await stream.CopyToAsync(memoryStream, cancellationToken);
memoryStream.Position = 0;
return memoryStream;
}
private async Task<IEnumerable<DetectedOperation>> DetectOperationsAsync(int idTelemetry, DateTimeOffset begin,
CancellationToken token)
{
var query = dbContext.TelemetryDataSaub
.AsNoTracking() .AsNoTracking()
.Where(o => idsTelemetries.Contains(o.IdTelemetry)) .Where(d => d.IdTelemetry == idTelemetry)
.OrderBy(o => o.IdTelemetry) .Where(d => d.BlockPosition >= 0)
.ThenBy(o => o.DateStart) .Select(d => new DetectableTelemetry
.ToListAsync(token);
var groups = operations.GroupBy(o => o.IdTelemetry);
foreach (var well in wells)
{ {
var ops = groups.FirstOrDefault(g => g.Key == well.IdTelemetry) DateTime = d.DateTime,
?.ToList(); IdUser = d.IdUser,
WellDepth = d.WellDepth,
Pressure = d.Pressure,
HookWeight = d.HookWeight,
BlockPosition = d.BlockPosition,
BitDepth = d.BitDepth,
RotorSpeed = d.RotorSpeed,
})
.OrderBy(d => d.DateTime);
if(ops?.Any() != true) var startDate = begin;
var detectedOperations = new List<DetectedOperation>(8);
DetectedOperation? lastDetectedOperation = null;
const int minOperationLength = 5;
const int maxDetectorsInterpolationFrameLength = 30;
const int gap = maxDetectorsInterpolationFrameLength + minOperationLength;
while (true)
{
var data = await query
.Where(d => d.DateTime > startDate)
.ToArrayAsync(token);
if (data.Length < gap)
break;
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; continue;
var sheetName = $"{well.Cluster}_{well.Caption}" detectedOperations.Add(result!.Operation);
.Replace('.','_'); lastDetectedOperation = result.Operation;
isDetected = true;
var sheet = workbook.AddWorksheet(sheetName); step = 1;
AddHeader(sheet); positionBegin = result.TelemetryEnd;
const int headerHeight = 1; break;
for(var i = 0; i< ops.Count; i++ )
AddRow(sheet, ops[i], well, i + 1 + headerHeight);
}
} }
private static void AddHeader(IXLWorksheet sheet) if (step > 20)
{ step = 10;
var rowNumber = 1; positionBegin += step;
sheet.Cell(rowNumber, 1).Value = "Name";
sheet.Column(1).Width = 34;
sheet.Cell(rowNumber, 2).Value = "DateStart";
sheet.Column(2).Width = 17;
sheet.Cell(rowNumber, 3).Value = "DateEnd";
sheet.Column(3).Width = 17;
sheet.Cell(rowNumber, 4).Value = "DepthStart";
sheet.Column(4).Width = 9;
sheet.Cell(rowNumber, 5).Value = "DepthEnd";
sheet.Column(5).Width = 9;
sheet.Cell(rowNumber, 6).Value = "KeyValue";
sheet.Column(6).Width = 9;
sheet.SheetView.FreezeRows(rowNumber);
} }
private static void AddRow(IXLWorksheet sheet, DetectedOperation operation, WellDto well, int rowNumber) if (isDetected)
{ startDate = lastDetectedOperation!.DateEnd;
var timezoneoffsetHours = well.Timezone.Hours; else
sheet.Cell(rowNumber, 1).Value = operation.OperationCategory.Name; startDate = data[positionEnd].DateTime;
sheet.Cell(rowNumber, 2).Value = operation.DateStart.ToRemoteDateTime(timezoneoffsetHours);
sheet.Cell(rowNumber, 3).Value = operation.DateEnd.ToRemoteDateTime(timezoneoffsetHours);
sheet.Cell(rowNumber, 4).Value = operation.DepthStart;
sheet.Cell(rowNumber, 5).Value = operation.DepthEnd;
sheet.Cell(rowNumber, 6).Value = operation.Value;
}
} }
return detectedOperations;
}
} }

View File

@ -6,9 +6,7 @@ using AsbCloudDb;
using AsbCloudDb.Model; using AsbCloudDb.Model;
using Mapster; using Mapster;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -333,13 +331,6 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
.ToArrayAsync(token); .ToArrayAsync(token);
return result; return result;
} }
public Task<Stream> ExportAsync(IEnumerable<int> idsWells, CancellationToken token)
{
var exportService = new DetectedOperationExportService(db, wellService);
return exportService.ExportAsync(idsWells, token);
}
} }
} }

View File

@ -7,7 +7,6 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
internal abstract class DetectorAbstract internal abstract class DetectorAbstract
{ {
private readonly int idOperation;
private readonly int stepLength = 3; private readonly int stepLength = 3;
protected const int IdReasonOfEnd_NotDetected = 0; protected const int IdReasonOfEnd_NotDetected = 0;
@ -32,12 +31,11 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
protected const int IdReasonOfEnd_BlockPositionIsHi = 501; protected const int IdReasonOfEnd_BlockPositionIsHi = 501;
protected const int IdReasonOfEnd_BlockPositionDeviates = 502; protected const int IdReasonOfEnd_BlockPositionDeviates = 502;
protected const int IdReasonOfEnd_Drilling = 600;
protected const int IdReasonOfEnd_Custom1 = 10_000; protected const int IdReasonOfEnd_Custom1 = 10_000;
protected DetectorAbstract(int idOperation) public abstract Func<DetectableTelemetry[], int, int, int> GetIdOperation { get; }
{
this.idOperation = idOperation;
}
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)
{ {
@ -45,21 +43,28 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
if (DetectBegin(telemetry, begin, previousOperation)) if (DetectBegin(telemetry, begin, previousOperation))
{ {
// Поиск окончания соответствия критерию // Поиск окончания соответствия критерию
int idReasonOfEnd = 0;
var positionEnd = begin; var positionEnd = begin;
while (positionEnd < end) while (positionEnd < end)
{ {
positionEnd += stepLength; positionEnd += stepLength;
if ((positionEnd > end)) if (positionEnd > end)
break;
idReasonOfEnd = DetectEnd(telemetry, positionEnd, previousOperation);
if(idReasonOfEnd is IdReasonOfEnd_DeltaDepthIsHi or IdReasonOfEnd_PressureIsLo &&
!IsValidByWellDepthDoesNotChange(telemetry, begin, positionEnd))
break; break;
var idReasonOfEnd = DetectEnd(telemetry, positionEnd, previousOperation);
if (idReasonOfEnd != IdReasonOfEnd_NotDetected) if (idReasonOfEnd != IdReasonOfEnd_NotDetected)
{ break;
}
result = null;
result = MakeOperation(idTelemetry, telemetry, begin, positionEnd, idReasonOfEnd); result = MakeOperation(idTelemetry, telemetry, begin, positionEnd, idReasonOfEnd);
return true; return true;
} }
}
}
result = null; result = null;
return false; return false;
} }
@ -81,7 +86,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
Operation = new DetectedOperation Operation = new DetectedOperation
{ {
IdTelemetry = idTelemetry, IdTelemetry = idTelemetry,
IdCategory = idOperation, IdCategory = GetIdOperation.Invoke(telemetry, begin, end),
IdUsersAtStart = pBegin.IdUser ?? -1, IdUsersAtStart = pBegin.IdUser ?? -1,
DateStart = pBegin.DateTime, DateStart = pBegin.DateTime,
DateEnd = pEnd.DateTime, DateEnd = pEnd.DateTime,

View File

@ -1,4 +1,5 @@
using AsbCloudDb.Model; using System;
using AsbCloudDb.Model;
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
{ {
@ -8,12 +9,12 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
/// </summary> /// </summary>
internal class DetectorDevelopment : DetectorAbstract internal class DetectorDevelopment : DetectorAbstract
{ {
public DetectorDevelopment()
: base(WellOperationCategory.IdDevelopment) { }
protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end) protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end)
=> CalcDeltaMinutes(telemetry, begin, end); => CalcDeltaMinutes(telemetry, begin, end);
public override Func<DetectableTelemetry[], int, int, int> GetIdOperation => (_, _, _)
=> WellOperationCategory.IdDevelopment;
protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{ {
if (previousOperation?.IdCategory == WellOperationCategory.IdSlipsTime) if (previousOperation?.IdCategory == WellOperationCategory.IdSlipsTime)

View File

@ -0,0 +1,63 @@
using System;
using System.Linq;
using AsbCloudDb.Model;
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors;
internal class DetectorDrilling : DetectorAbstract
{
public override Func<DetectableTelemetry[], int, int, int> GetIdOperation => DefineDrillingOperation;
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;
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)
return IdReasonOfEnd_DeltaDepthIsHi;
if (point0.Pressure < 25)
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)
{
const int idSlideWithOscillation = 12000;
var telemetryRange = telemetry[begin.. end];
var avgRotorSpeed = telemetryRange.Average(t => t.RotorSpeed);
if (avgRotorSpeed < 10)
return WellOperationCategory.IdSlide;
var despersion = telemetryRange
.Average(t => Math.Pow((t.RotorSpeed - avgRotorSpeed) / avgRotorSpeed, 2));
return despersion < 0.2d ? WellOperationCategory.IdRotor : idSlideWithOscillation;
}
}

View File

@ -1,59 +1,59 @@
using AsbCloudDb.Model; // using AsbCloudDb.Model;
//
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors // namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
{ // {
//
/// <summary> // /// <summary>
/// Промывка // /// Промывка
/// </summary> // /// </summary>
internal class DetectorFlashing : DetectorAbstract // internal class DetectorFlashing : DetectorAbstract
{ // {
public DetectorFlashing() // public DetectorFlashing()
: base(WellOperationCategory.IdFlashing) // : base(WellOperationCategory.IdFlashing)
{ } // { }
//
protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end) // protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end)
=> CalcDeltaMinutes(telemetry, begin, end); // => CalcDeltaMinutes(telemetry, begin, end);
//
protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) // protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{ // {
if (!((previousOperation?.IdCategory == WellOperationCategory.IdRotor) || // if (!((previousOperation?.IdCategory == WellOperationCategory.IdRotor) ||
(previousOperation?.IdCategory == WellOperationCategory.IdSlide))) // (previousOperation?.IdCategory == WellOperationCategory.IdSlide)))
return false; // return false;
//
var point0 = telemetry[position]; // var point0 = telemetry[position];
var delta = point0.WellDepth - point0.BitDepth; // var delta = point0.WellDepth - point0.BitDepth;
if (delta > 0.05d) // if (delta > 0.05d)
return false; // return false;
//
if (point0.Pressure < 15) // if (point0.Pressure < 15)
return false; // return false;
//
if (point0.BlockPosition < 3) // if (point0.BlockPosition < 3)
return false; // return false;
//
if (ContainsDeviationApprox(telemetry, t => t.WellDepth, position, 150, 0.0001)) // if (ContainsDeviationApprox(telemetry, t => t.WellDepth, position, 150, 0.0001))
return false; // return false;
//
return true; // return true;
} // }
//
protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) // protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{ // {
var point0 = telemetry[position]; // var point0 = telemetry[position];
var delta = point0.WellDepth - point0.BitDepth; // var delta = point0.WellDepth - point0.BitDepth;
if ((delta > 0.03d ) // if ((delta > 0.03d )
&& (point0.Pressure > 15) // && (point0.Pressure > 15)
&& ContainsDeviationApprox(telemetry, t=>t.BlockPosition, position, 60, 0.03)) // && ContainsDeviationApprox(telemetry, t=>t.BlockPosition, position, 60, 0.03))
return IdReasonOfEnd_Custom1; // return IdReasonOfEnd_Custom1;
//
return IdReasonOfEnd_NotDetected; // return IdReasonOfEnd_NotDetected;
} // }
//
protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end) // protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end)
=> IsValidByWellDepthDoesNotChange(telemetry, begin, end); // => IsValidByWellDepthDoesNotChange(telemetry, begin, end);
} // }
//
//
} // }
//

View File

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

View File

@ -1,62 +1,54 @@
using AsbCloudDb.Model; // using AsbCloudDb.Model;
using System.Linq; // using System.Linq;
//
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors // namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
{ // {
//
internal class DetectorRotor : DetectorAbstract // public class DetectorRotor : DetectorAbstract
{ // {
public DetectorRotor() // public DetectorRotor()
: base(WellOperationCategory.IdRotor) { } // : base(WellOperationCategory.IdRotor) { }
//
protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) // protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{ // {
var point0 = telemetry[position]; // var point0 = telemetry[position];
var delta = point0.WellDepth - point0.BitDepth; // var delta = point0.WellDepth - point0.BitDepth;
if (delta > 0.03d) // if (delta > 0.03d)
return false; // return false;
//
if (point0.Pressure < 25) // if (point0.Pressure < 25)
return false; // return false;
//
if (point0.RotorSpeed < 5) // if (point0.RotorSpeed < 5)
return false; // return false;
//
var point1 = telemetry[position + 1]; // var point1 = telemetry[position + 1];
if (point1.WellDepth - point0.WellDepth <= 0.003) // if (point1.WellDepth - point0.WellDepth <= 0.003)
return false; // return false;
//
return true; // return true;
} // }
//
protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) // protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{ // {
var point0 = telemetry[position]; // var point0 = telemetry[position];
var delta = point0.WellDepth - point0.BitDepth; // var delta = point0.WellDepth - point0.BitDepth;
if (delta > 0.03d) // if (delta > 0.03d)
return IdReasonOfEnd_DeltaDepthIsHi; // return IdReasonOfEnd_DeltaDepthIsHi;
//
if (point0.Pressure < 25) // if (point0.Pressure < 25)
return IdReasonOfEnd_PressureIsLo; // return IdReasonOfEnd_PressureIsLo;
//
var avgRotorSpeed = CalcAvgAppr(d => d.RotorSpeed, telemetry, position, 60); // return IdReasonOfEnd_NotDetected;
// }
if (avgRotorSpeed < 10) //
return IdReasonOfEnd_AvgRotorSpeedIsLo; // protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end)
// => IsValidByWellDepthIncreasing(telemetry, begin, end);
if (!DeviatesFromBegin(telemetry, t => t.WellDepth, position, 150, 0.003)) //
return IdReasonOfEnd_WellDepthDeviates; // protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end)
// => CalcRop(telemetry, begin, end);
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);
}
}

View File

@ -1,61 +1,49 @@
using AsbCloudDb.Model; // using AsbCloudDb.Model;
//
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors // namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
{ // {
//
internal class DetectorSlide : DetectorAbstract // public class DetectorSlide : DetectorAbstract
{ // {
public DetectorSlide() // public DetectorSlide()
: base(WellOperationCategory.IdSlide) { } // : base(WellOperationCategory.IdSlide) { }
//
protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) // protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{ // {
var point0 = telemetry[position]; // var point0 = telemetry[position];
var delta = point0.WellDepth - point0.BitDepth; // var delta = point0.WellDepth - point0.BitDepth;
if (delta > 0.03d) // if (delta > 0.03d)
return false; // return false;
//
if (point0.Pressure < 25) // if (point0.Pressure < 25)
return false; // return false;
//
if (point0.RotorSpeed > 5) // return true;
return false; // }
//
var point1 = telemetry[position + 1]; // protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
if (point1.WellDepth - point0.WellDepth <= 0.003) // {
return false; // var point0 = telemetry[position];
// var delta = point0.WellDepth - point0.BitDepth;
return true; // if (delta > 0.03d)
} // return IdReasonOfEnd_DeltaDepthIsHi;
//
protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) // if (point0.Pressure < 25)
{ // return IdReasonOfEnd_PressureIsLo;
var point0 = telemetry[position]; //
var delta = point0.WellDepth - point0.BitDepth; // if (!DeviatesFromBegin(telemetry, t => t.WellDepth, position, 150, 0.003))
if (delta > 0.03d) // return IdReasonOfEnd_WellDepthDeviates;
return IdReasonOfEnd_DeltaDepthIsHi; //
// return IdReasonOfEnd_NotDetected;
if (point0.Pressure < 25) // }
return IdReasonOfEnd_PressureIsLo; //
// protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end)
var avgRotorSpeed = CalcAvgAppr(d => d.RotorSpeed, telemetry, position, 60); // => IsValidByWellDepthIncreasing(telemetry, begin, end);
//
if (avgRotorSpeed > 10) // protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end)
return IdReasonOfEnd_AvgRotorSpeedIsHi; // => CalcRop(telemetry, begin, end);
// }
if (!DeviatesFromBegin(telemetry, t => t.WellDepth, position, 150, 0.003)) //
return IdReasonOfEnd_WellDepthDeviates; //
// }
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);
}
}

View File

@ -1,17 +1,16 @@
using AsbCloudDb.Model; using System;
using AsbCloudDb.Model;
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
{ {
internal class DetectorSlipsTime : DetectorAbstract internal class DetectorSlipsTime : DetectorAbstract
{ {
public DetectorSlipsTime()
: base(WellOperationCategory.IdSlipsTime)
{ }
protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end) protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end)
=> CalcDeltaMinutes(telemetry, begin, end); => CalcDeltaMinutes(telemetry, begin, end);
public override Func<DetectableTelemetry[], int, int, int> GetIdOperation => (_, _, _) => WellOperationCategory.IdSlipsTime;
protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{ {
var point0 = telemetry[position]; var point0 = telemetry[position];

View File

@ -1,69 +1,69 @@
using AsbCloudDb.Model; // using AsbCloudDb.Model;
using System.Linq; // using System.Linq;
using System; // using System;
//
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors // namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
{ // {
//
//
/// <summary> // /// <summary>
/// Статический замер телесистемы // /// Статический замер телесистемы
/// </summary> // /// </summary>
internal class DetectorStaticSurveying: DetectorAbstract // internal class DetectorStaticSurveying: DetectorAbstract
{ // {
public DetectorStaticSurveying() // public DetectorStaticSurveying()
: base(WellOperationCategory.IdStaticSurveying) { } // : base(WellOperationCategory.IdStaticSurveying) { }
//
protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) // protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{ // {
var point0 = telemetry[position]; // var point0 = telemetry[position];
//
if (point0.Pressure < 15) // if (point0.Pressure < 15)
return false; // return false;
//
var delta = point0.WellDepth - point0.BitDepth; // var delta = point0.WellDepth - point0.BitDepth;
if (delta > 2.5d || delta < 0.15d) // if (delta > 2.5d || delta < 0.15d)
return false; // return false;
//
if (point0.RotorSpeed > 30) // if (point0.RotorSpeed > 30)
return false; // return false;
//
if (ContainsDeviation(telemetry, t => t.BlockPosition, position, 120, 0.03)) // if (ContainsDeviation(telemetry, t => t.BlockPosition, position, 120, 0.03))
return false; // return false;
//
if (ContainsDeviation(telemetry, t => t.Pressure, position, 60, 10)) // if (ContainsDeviation(telemetry, t => t.Pressure, position, 60, 10))
return false; // return false;
//
return true; // return true;
} // }
//
protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) // protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{ // {
var point0 = telemetry[position]; // var point0 = telemetry[position];
//
var delta = point0.WellDepth - point0.BitDepth; // var delta = point0.WellDepth - point0.BitDepth;
if (delta > 2.5d ) // if (delta > 2.5d )
return IdReasonOfEnd_DeltaDepthIsHi; // return IdReasonOfEnd_DeltaDepthIsHi;
//
if (point0.RotorSpeed > 30) // if (point0.RotorSpeed > 30)
return IdReasonOfEnd_RotorSpeedIsHi; // return IdReasonOfEnd_RotorSpeedIsHi;
//
if (RisesFromBegin(telemetry, t => t.Pressure, position, 10, 15)) // if (RisesFromBegin(telemetry, t => t.Pressure, position, 10, 15))
return IdReasonOfEnd_PressureIsRising; // return IdReasonOfEnd_PressureIsRising;
//
if (ContainsDeviation(telemetry, t => t.BlockPosition, position, 10, 0.05)) // if (ContainsDeviation(telemetry, t => t.BlockPosition, position, 10, 0.05))
return IdReasonOfEnd_BlockPositionDeviates; // return IdReasonOfEnd_BlockPositionDeviates;
//
return IdReasonOfEnd_NotDetected; // return IdReasonOfEnd_NotDetected;
} // }
//
protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end) // protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end)
=> IsValidByWellDepthDoesNotChange(telemetry, begin, end); // => IsValidByWellDepthDoesNotChange(telemetry, begin, end);
//
protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end) // protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end)
=> CalcDeltaMinutes(telemetry, begin, end); // => CalcDeltaMinutes(telemetry, begin, end);
} // }
//
//
} // }
//

View File

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

View File

@ -1,61 +1,61 @@
using AsbCloudDb.Model; // using AsbCloudDb.Model;
//
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors // namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
{ // {
//
/// <summary> // /// <summary>
/// Шаблонировка при бурении // /// Шаблонировка при бурении
/// </summary> // /// </summary>
internal class DetectorTemplatingWhileDrilling : DetectorAbstract // internal class DetectorTemplatingWhileDrilling : DetectorAbstract
{ // {
public DetectorTemplatingWhileDrilling() // public DetectorTemplatingWhileDrilling()
: base(WellOperationCategory.IdTemplatingWhileDrilling) { } // : base(WellOperationCategory.IdTemplatingWhileDrilling) { }
//
protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end) // protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end)
=> CalcDeltaMinutes(telemetry, begin, end); // => CalcDeltaMinutes(telemetry, begin, end);
//
protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) // protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{ // {
if (previousOperation?.IdCategory != WellOperationCategory.IdFlashing) // if (previousOperation?.IdCategory != WellOperationCategory.IdFlashing)
return false; // return false;
//
var point0 = telemetry[position]; // var point0 = telemetry[position];
//
if (point0.Pressure < 15) // if (point0.Pressure < 15)
return false; // return false;
//
if (point0.RotorSpeed < 1) // if (point0.RotorSpeed < 1)
return false; // return false;
//
if (RisesFromBegin(telemetry, t => t.BlockPosition, position, 30, 0.5)) // if (RisesFromBegin(telemetry, t => t.BlockPosition, position, 30, 0.5))
return false; // return false;
//
return true; // return true;
} // }
//
protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) // protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation)
{ // {
var point0 = telemetry[position]; // var point0 = telemetry[position];
//
if (point0.Pressure < 15) // if (point0.Pressure < 15)
return IdReasonOfEnd_PressureIsLo; // return IdReasonOfEnd_PressureIsLo;
//
if (RisesFromBegin(telemetry, t=>t.WellDepth, position, 10, 0.01)) // if (RisesFromBegin(telemetry, t=>t.WellDepth, position, 10, 0.01))
return IdReasonOfEnd_WellDepthDeviates; // return IdReasonOfEnd_WellDepthDeviates;
//
var delta = point0.WellDepth - point0.BitDepth; // var delta = point0.WellDepth - point0.BitDepth;
if ( (delta > 0.03d ) // if ( (delta > 0.03d )
&& (point0.Pressure > 15) // && (point0.Pressure > 15)
&& (!ContainsDeviationApprox(telemetry, t=>t.BlockPosition, position, 60, 0.03))) // && (!ContainsDeviationApprox(telemetry, t=>t.BlockPosition, position, 60, 0.03)))
return IdReasonOfEnd_Custom1; // return IdReasonOfEnd_Custom1;
//
return IdReasonOfEnd_NotDetected; // return IdReasonOfEnd_NotDetected;
} // }
//
protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end) // protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end)
=> IsValidByWellDepthDoesNotChange(telemetry, begin, end); // => IsValidByWellDepthDoesNotChange(telemetry, begin, end);
} // }
//
//
} // }
//

View File

@ -3,11 +3,10 @@
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
{ {
class OperationDetectorResult public class OperationDetectorResult
{ {
public int TelemetryBegin { get; set; } public int TelemetryBegin { get; set; }
public int TelemetryEnd { get; set; } public int TelemetryEnd { get; set; }
public DetectedOperation Operation { get; set; } = null!; public DetectedOperation Operation { get; set; } = null!;
} }
} }

View File

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

View File

@ -16,8 +16,8 @@ public class WorkOperationDetection: Work
{ {
private static readonly DetectorAbstract[] detectors = new DetectorAbstract[] private static readonly DetectorAbstract[] detectors = new DetectorAbstract[]
{ {
new DetectorRotor(), // new DetectorRotor(),
new DetectorSlide(), // new DetectorSlide(),
//new DetectorDevelopment(), //new DetectorDevelopment(),
//new DetectorTemplating(), //new DetectorTemplating(),
new DetectorSlipsTime(), new DetectorSlipsTime(),

View File

@ -25,9 +25,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Update="wwwroot\**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="AsbCloudWebApi.service"> <Content Include="AsbCloudWebApi.service">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>

View File

@ -133,6 +133,7 @@ namespace AsbCloudWebApi.Controllers
[HttpGet("datesRange")] [HttpGet("datesRange")]
[Permission] [Permission]
[ProducesResponseType(typeof(DatesRangeDto), (int)System.Net.HttpStatusCode.OK)] [ProducesResponseType(typeof(DatesRangeDto), (int)System.Net.HttpStatusCode.OK)]
[ProducesResponseType((int)System.Net.HttpStatusCode.NoContent)]
public async Task<IActionResult> GetReportsDateRangeAsync(int idWell, CancellationToken token) public async Task<IActionResult> GetReportsDateRangeAsync(int idWell, CancellationToken token)
{ {
int? idCompany = User.GetCompanyId(); int? idCompany = User.GetCompanyId();
@ -146,6 +147,9 @@ namespace AsbCloudWebApi.Controllers
var wellReportsDatesRange = reportService.GetDatesRangeOrDefault(idWell); var wellReportsDatesRange = reportService.GetDatesRangeOrDefault(idWell);
if (wellReportsDatesRange is null)
return NoContent();
return Ok(wellReportsDatesRange); return Ok(wellReportsDatesRange);
} }
} }

View File

@ -5,9 +5,10 @@ using AsbCloudApp.Services;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.ComponentModel.DataAnnotations;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsbCloudInfrastructure.Services.DetectOperations;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
namespace AsbCloudWebApi.Controllers.SAUB namespace AsbCloudWebApi.Controllers.SAUB
@ -22,11 +23,14 @@ namespace AsbCloudWebApi.Controllers.SAUB
{ {
private readonly IDetectedOperationService detectedOperationService; private readonly IDetectedOperationService detectedOperationService;
private readonly IWellService wellService; private readonly IWellService wellService;
private readonly DetectedOperationExportService detectedOperationExportService;
public DetectedOperationController(IDetectedOperationService detectedOperationService, IWellService wellService) public DetectedOperationController(IDetectedOperationService detectedOperationService, IWellService wellService,
DetectedOperationExportService detectedOperationExportService)
{ {
this.detectedOperationService = detectedOperationService; this.detectedOperationService = detectedOperationService;
this.wellService = wellService; this.wellService = wellService;
this.detectedOperationExportService = detectedOperationExportService;
} }
/// <summary> /// <summary>
@ -118,42 +122,23 @@ namespace AsbCloudWebApi.Controllers.SAUB
/// Создает excel файл с операциями по скважине /// Создает excel файл с операциями по скважине
/// </summary> /// </summary>
/// <param name="idWell">id скважины</param> /// <param name="idWell">id скважины</param>
/// <param name="idCluster"></param> /// <param name="idDomain">Идентификатор домена</param>
/// <param name="token"> Токен отмены задачи </param> /// <param name="token"></param>
/// <returns>Запрашиваемый файл</returns>
[HttpGet("export")] [HttpGet("export")]
[Permission] [Permission]
[ProducesResponseType(typeof(PhysicalFileResult), (int)System.Net.HttpStatusCode.OK, "application/octet-stream")] [ProducesResponseType(typeof(PhysicalFileResult), (int)System.Net.HttpStatusCode.OK, "application/octet-stream")]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(typeof(ValidationProblemDetails), (int)System.Net.HttpStatusCode.BadRequest)] [ProducesResponseType(typeof(ValidationProblemDetails), (int)System.Net.HttpStatusCode.BadRequest)]
public async Task<IActionResult> ExportAsync(int? idWell, int? idCluster, CancellationToken token) public async Task<IActionResult> ExportAsync(int idWell, [Range(1, 2)] int idDomain, CancellationToken token)
{ {
if (idCluster is null && idWell is null) var idCompany = User.GetCompanyId();
return this.ValidationBadRequest(nameof(idWell), $"One of {nameof(idWell)} or {nameof(idCluster)} mast be set.");
int? idCompany = User.GetCompanyId();
if (idCompany is null) if (idCompany is null)
return Forbid(); return Forbid();
IEnumerable<int> idsWells; var stream = await detectedOperationExportService.ExportAsync(idWell, idDomain, token);
if (idCluster is not null)
{
var companyWells = await wellService.GetAsync(new() { IdCompany = idCompany }, token);
idsWells = companyWells.Where(w => w.IdCluster == idCluster)
.Select(w=>w.Id);
}
else
{
if (!await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
idWell!.Value, token).ConfigureAwait(false))
return Forbid();
idsWells = new List<int> { (int)idWell };
}
var stream = await detectedOperationService.ExportAsync(idsWells, token); return File(stream, "application/octet-stream", "operations.xlsx");
var fileName = "operations.xlsx";
return File(stream, fileName);
} }
} }
} }

View File

@ -139,6 +139,7 @@ namespace AsbCloudWebApi.Controllers.SAUB
[HttpGet("{idWell}/dateRange")] [HttpGet("{idWell}/dateRange")]
[ProducesResponseType(typeof(DatesRangeDto), (int)System.Net.HttpStatusCode.OK)] [ProducesResponseType(typeof(DatesRangeDto), (int)System.Net.HttpStatusCode.OK)]
[ProducesResponseType((int)System.Net.HttpStatusCode.NotFound)] [ProducesResponseType((int)System.Net.HttpStatusCode.NotFound)]
[ProducesResponseType((int)System.Net.HttpStatusCode.NoContent)]
public virtual async Task<ActionResult<DatesRangeDto?>> GetRangeAsync( public virtual async Task<ActionResult<DatesRangeDto?>> GetRangeAsync(
[FromRoute] int idWell, [FromRoute] int idWell,
[Required] DateTimeOffset geDate, [Required] DateTimeOffset geDate,
@ -158,6 +159,9 @@ namespace AsbCloudWebApi.Controllers.SAUB
var content = await telemetryDataService.GetRangeAsync(idWell, geDate, leDate, token); var content = await telemetryDataService.GetRangeAsync(idWell, geDate, leDate, token);
if (content is null)
return NoContent();
return Ok(content); return Ok(content);
} }
@ -171,6 +175,7 @@ namespace AsbCloudWebApi.Controllers.SAUB
[Permission] [Permission]
[ProducesResponseType(typeof(DatesRangeDto), (int)System.Net.HttpStatusCode.OK)] [ProducesResponseType(typeof(DatesRangeDto), (int)System.Net.HttpStatusCode.OK)]
[ProducesResponseType((int)System.Net.HttpStatusCode.NotFound)] [ProducesResponseType((int)System.Net.HttpStatusCode.NotFound)]
[ProducesResponseType((int)System.Net.HttpStatusCode.NoContent)]
public virtual async Task<ActionResult<DatesRangeDto?>> GetDataDatesRangeAsync(int idWell, public virtual async Task<ActionResult<DatesRangeDto?>> GetDataDatesRangeAsync(int idWell,
CancellationToken token) CancellationToken token)
{ {

View File

@ -132,17 +132,6 @@ namespace AsbCloudWebApi
app.UseDeveloperExceptionPage(); app.UseDeveloperExceptionPage();
} }
app.UseDefaultFiles();
app.UseStaticFiles(
new StaticFileOptions
{
OnPrepareResponse = ctx =>
{
ctx.Context.Response.Headers.CacheControl = "public,max-age=2592000";
ctx.Context.Response.Headers.Expires = System.DateTime.UtcNow.AddDays(10).ToString("R", System.Globalization.CultureInfo.InvariantCulture);
}
}
);
app.UseCors("ClientPermission"); app.UseCors("ClientPermission");
app.UseRouting(); app.UseRouting();

View File

@ -1 +0,0 @@
<!doctype html><html lang="ru"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="white"/><meta name="theme-color" media="(prefers-color-scheme: light)" content="white"/><meta name="theme-color" media="(prefers-color-scheme: dark)" content="black"/><meta name="description" content="Онлайн мониторинг процесса бурения в реальном времени в офисе заказчика"/><title>DDrill</title><script defer="defer" src="/runtime~main.5f05be65.js"></script><script defer="defer" src="/vendors.408d069b.js"></script><script defer="defer" src="/main.c3c166db.js"></script></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

View File

@ -23,6 +23,10 @@
<PackageReference Include="System.Net.Http" Version="4.3.4" /> <PackageReference Include="System.Net.Http" Version="4.3.4" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<EmbeddedResource Include="DetectedOperations\Files\Operations.xlsx" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<WCFMetadata Include="Connected Services" /> <WCFMetadata Include="Connected Services" />

View File

@ -1,23 +1,23 @@
using System.IO; 
using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Services;
using AsbCloudDb.Model; using AsbCloudDb.Model;
using AsbCloudInfrastructure; using ConsoleApp1.DetectedOperations;
using CliWrap;
using Mapster;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleApp1 namespace ConsoleApp1
{ {
class Program class Program
{ {
static void Main(/*string[] args*/) private static DbContextOptions<AsbCloudDbContext> options = new DbContextOptionsBuilder<AsbCloudDbContext>()
.UseNpgsql("Host=localhost;Database=postgres;Port=5433;Username=postgres;Password=root;Persist Security Info=True")
.Options;
static async Task Main(/*string[] args*/)
{ {
using var db = new AsbCloudDbContext(options);
await new DetectedOperationExportService(db).Export(483, CancellationToken.None);
} }
} }