forked from ddrilling/AsbCloudServer
Merge branch 'dev' into feature/detected_operations
This commit is contained in:
commit
04a6300123
@ -1,20 +1,19 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace AsbCloudApp.Requests;
|
||||
|
||||
/// <summary>
|
||||
/// Запрос на получение статистики использования подсистем бурильщиком
|
||||
/// Запрос на получение статистики использования подсистем бурильщиком
|
||||
/// </summary>
|
||||
public class GetStatRequest: RequestBase
|
||||
public class GetStatRequest : RequestBase
|
||||
{
|
||||
/// <summary>
|
||||
/// id скважин
|
||||
/// id скважин
|
||||
/// </summary>
|
||||
public IEnumerable<int> IdsWells { get; set; } = new List<int>();
|
||||
|
||||
/// <summary>
|
||||
/// id Бурильщика
|
||||
/// список ключей бурильщиков
|
||||
/// </summary>
|
||||
public int? IdDriller { get; set; }
|
||||
public IEnumerable<int> IdsDrillers { get; set; } = new List<int>();
|
||||
}
|
9389
AsbCloudDb/Migrations/20240329070104_Update_WellOperationCategory_IdConditioning.Designer.cs
generated
Normal file
9389
AsbCloudDb/Migrations/20240329070104_Update_WellOperationCategory_IdConditioning.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,32 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace AsbCloudDb.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Update_WellOperationCategory_IdConditioning : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.UpdateData(
|
||||
table: "t_well_operation_category",
|
||||
keyColumn: "id",
|
||||
keyValue: 5007,
|
||||
column: "name",
|
||||
value: "Проработка");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.UpdateData(
|
||||
table: "t_well_operation_category",
|
||||
keyColumn: "id",
|
||||
keyValue: 5007,
|
||||
column: "name",
|
||||
value: "Проработка перед наращиванием");
|
||||
}
|
||||
}
|
||||
}
|
@ -6496,7 +6496,7 @@ namespace AsbCloudDb.Migrations
|
||||
IdParent = 4003,
|
||||
KeyValueName = "dT",
|
||||
KeyValueUnits = "мин",
|
||||
Name = "Проработка перед наращиванием"
|
||||
Name = "Проработка"
|
||||
},
|
||||
new
|
||||
{
|
||||
|
@ -154,9 +154,9 @@ namespace AsbCloudDb.Model
|
||||
/// </summary>
|
||||
public const int IdFlashingBeforeConnection = 5005;
|
||||
/// <summary>
|
||||
/// Проработка перед наращиванием
|
||||
/// Проработка
|
||||
/// </summary>
|
||||
public const int IdDevelopment = 5007;
|
||||
public const int IdConditioning = 5007;
|
||||
/// <summary>
|
||||
/// Шаблонировка во время бурения
|
||||
/// </summary>
|
||||
@ -278,7 +278,7 @@ namespace AsbCloudDb.Model
|
||||
new () {Id = IdStaticSurveying, IdParent = 4002, Name = "Замер ЗТС (запись MWD)", KeyValueName = "dT", KeyValueUnits = "мин" },
|
||||
new () {Id = IdFlashingBeforeConnection, IdParent = 4003, Name = "Промывка перед наращиванием", KeyValueName = "dT", KeyValueUnits = "мин" },
|
||||
new () {Id = 5006, IdParent = 4003, Name = "Проработка во время бурения", KeyValueName = "dT", KeyValueUnits = "мин" },
|
||||
new () {Id = IdDevelopment, IdParent = 4003, Name = "Проработка перед наращиванием", KeyValueName = "dT", KeyValueUnits = "мин" },
|
||||
new () {Id = IdConditioning, IdParent = 4003, Name = "Проработка", KeyValueName = "dT", KeyValueUnits = "мин" },
|
||||
new () {Id = IdTemplatingWhileDrilling, IdParent = 4003, Name = "Шаблонировка во время бурения", KeyValueName = "dT", KeyValueUnits = "мин" },
|
||||
new () {Id = IdTemplating, IdParent = 4003, Name = "Шаблонировка перед наращиванием", KeyValueName = "dT", KeyValueUnits = "мин" },
|
||||
new () {Id = 5010, IdParent = 4004, Name = "Наращивание", KeyValueName = "dT", KeyValueUnits = "мин" },
|
||||
|
@ -56,11 +56,11 @@ namespace AsbCloudInfrastructure.Repository
|
||||
public async Task<IEnumerable<ScheduleDto>> GetPageAsync(GetStatRequest request, CancellationToken token)
|
||||
{
|
||||
var idWell = request.IdsWells;
|
||||
var idDriller = request.IdDriller;
|
||||
var idsDrillers = request.IdsDrillers;
|
||||
var query = GetQuery().Where(s => request.IdsWells.Contains(s.IdWell));
|
||||
if (idDriller is not null)
|
||||
if (idsDrillers.Any())
|
||||
{
|
||||
query.Where(s => s.IdDriller == idDriller);
|
||||
query = query.Where(s => idsDrillers.Contains(s.IdDriller));
|
||||
}
|
||||
|
||||
var result = await query.ToArrayAsync(token);
|
||||
|
@ -29,6 +29,7 @@ public class DetectedOperationService : IDetectedOperationService
|
||||
new DetectorDrilling(),
|
||||
new DetectorSlipsTime(),
|
||||
new DetectorFlashing(),
|
||||
new DetectorConditioning(),
|
||||
};
|
||||
|
||||
public DetectedOperationService(
|
||||
|
@ -0,0 +1,55 @@
|
||||
using AsbCloudApp.Data.DetectedOperation;
|
||||
using AsbCloudDb.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
|
||||
{
|
||||
public class DetectorConditioning : DetectorAbstract
|
||||
{
|
||||
protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end) =>
|
||||
CalcDeltaMinutes(telemetry, begin, end);
|
||||
|
||||
protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperationDto? previousOperation)
|
||||
{
|
||||
var currentPoint = telemetry[position];
|
||||
if (currentPoint.Pressure < 10)
|
||||
return false;
|
||||
|
||||
if (currentPoint.RotorSpeed <= 8)
|
||||
return false;
|
||||
|
||||
var delta = currentPoint.WellDepth - currentPoint.BitDepth;
|
||||
if (delta < 0.03d)
|
||||
return false;
|
||||
|
||||
if (currentPoint.BitDepth < 150)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperationDto? previousOperation)
|
||||
{
|
||||
var currentPoint = telemetry[position];
|
||||
|
||||
if (currentPoint.Pressure < 10)
|
||||
return IdReasonOfEnd_PressureIsLo;
|
||||
if (currentPoint.RotorSpeed <=8)
|
||||
return IdReasonOfEnd_RotorSpeedIsHi;
|
||||
if ((currentPoint.WellDepth - currentPoint.BitDepth) < 0.03d)
|
||||
return IdReasonOfEnd_DeltaWellDepthAndBithDepthIsLo;
|
||||
if (currentPoint.BitDepth < 150)
|
||||
return IdReasonOfEnd_BithDepthIsLo;
|
||||
return IdReasonOfEnd_NotDetected;
|
||||
}
|
||||
|
||||
protected override (int IdCategory, IDictionary<string, object> ExtraData) GetSpecificInformation(DetectableTelemetry[] telemetry, int begin, int end)
|
||||
{
|
||||
return (WellOperationCategory.IdConditioning, new Dictionary<string, object>());
|
||||
}
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
|
||||
return false;
|
||||
|
||||
var delta = currentPoint.WellDepth - currentPoint.BitDepth;
|
||||
if (delta < 0.03d)
|
||||
if (delta < 0.01d)
|
||||
return false;
|
||||
|
||||
if (currentPoint.RotorSpeed > 8)
|
||||
@ -47,7 +47,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
|
||||
|
||||
if (currentPoint.Pressure < 10)
|
||||
return IdReasonOfEnd_PressureIsLo;
|
||||
if ((currentPoint.WellDepth - currentPoint.BitDepth) < 0.03d)
|
||||
if ((currentPoint.WellDepth - currentPoint.BitDepth) < 0.01d)
|
||||
return IdReasonOfEnd_DeltaWellDepthAndBithDepthIsLo;
|
||||
if (currentPoint.RotorSpeed > 8)
|
||||
return IdReasonOfEnd_RotorSpeedIsHi;
|
||||
|
@ -13,13 +13,13 @@
|
||||
Признак начала операции =
|
||||
( давление >= 10 атм ) И
|
||||
( обороты ротора <= 8 об/мин) И
|
||||
( расстояние от долота до забоя >= 0.03 м) И
|
||||
( расстояние от долота до забоя >= 0.01 м) И
|
||||
( глубина забоя не изменяется) И
|
||||
( глубина долота >= 150 м);
|
||||
|
||||
Признак окончания операции =
|
||||
( давление < 10 атм ) ИЛИ
|
||||
( расстояние от долота до забоя < 0.03 м ) ИЛИ
|
||||
( расстояние от долота до забоя < 0.01 м ) ИЛИ
|
||||
( обороты ротора > 8 об/мин) ИЛИ
|
||||
( глубина долота < 150 м);
|
||||
|
||||
|
@ -0,0 +1,29 @@
|
||||
# Алгоритм определения проработки
|
||||
## Описание
|
||||
|
||||
Проработка – операция, во время которой производится подъем или спуск бурильного инструмента с вращением и циркуляцией. Следующей операцией после проработки будет либо шаблонировка (аналогично проработке, но БЕЗ вращением и циркуляции), либо разгрузка инструмента в клинья (снижение веса на крюке) – наращивание или отворот бурильного инструмента.
|
||||
|
||||
Проработка определяется как время между:
|
||||
- начало подъема/спуска бурильного инструмента с циркуляцией и вращением;
|
||||
- разгрузкой инструмента на клинья (остается только вес крюкоблока и ВСП). При этом давление менее 20 атм. ЛИБО
|
||||
- начало подъема/спуска бурильного инструмента с циркуляцией, НО БЕЗ вращения
|
||||
Разделяется два вида проработки:
|
||||
1. перед наращиванием - соотношение глубины забоя и глубины долота <= 30 метров;
|
||||
2. при спуско-подъёмных операциях (СПО)соотношение глубины забоя - глубины долота > 30 метров.
|
||||
|
||||
## Метод определения
|
||||
|
||||
Признак начала операции =
|
||||
( давление >= 10 атм ) И
|
||||
( обороты ротора > 8 об/мин ) И
|
||||
( расстояние от долота до забоя >= 0.03м ) И
|
||||
( глубина долота >= 150м);
|
||||
|
||||
Признак окончания операции =
|
||||
( обороты ротора <= 8 об/мин ) ИЛИ
|
||||
( давление < 10 атм ) ИЛИ
|
||||
( расстояние от долота до забоя < 0.03м ) ИЛИ
|
||||
( глубина долота < 150м);
|
||||
|
||||
## Ключевой параметр
|
||||
Продолжительность операции.
|
@ -67,14 +67,9 @@ internal class SubsystemService : ISubsystemService
|
||||
|
||||
var detectedOperations = await detectedOperationService
|
||||
.GetOperationsAsync(byWellRequest, token);
|
||||
var detectedOperationsByCurrentDriller = detectedOperations.Where(d => d.Driller?.Id == schedule.IdDriller);
|
||||
|
||||
var groupByDriller = detectedOperations
|
||||
.Where(operation => operation.Driller is not null)
|
||||
.GroupBy(operation => operation.Driller);
|
||||
|
||||
foreach (var entry in groupByDriller)
|
||||
{
|
||||
var drillerOperationsStat = await CalcStatAsync(entry, token);
|
||||
var drillerOperationsStat = await CalcStatAsync(detectedOperationsByCurrentDriller, token);
|
||||
var dto = new DrillerDetectedOperationStatDto
|
||||
{
|
||||
Statistic = drillerOperationsStat,
|
||||
@ -83,7 +78,6 @@ internal class SubsystemService : ISubsystemService
|
||||
};
|
||||
result.Add(dto);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
11
AsbCloudWebApi.IntegrationTests/Clients/IDrillerClient.cs
Normal file
11
AsbCloudWebApi.IntegrationTests/Clients/IDrillerClient.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using AsbCloudApp.Data;
|
||||
using Refit;
|
||||
|
||||
namespace AsbCloudWebApi.IntegrationTests.Clients;
|
||||
|
||||
public interface IDrillerClient
|
||||
{
|
||||
|
||||
[Get("/api/drillers")]
|
||||
Task<IApiResponse<IEnumerable<DrillerDto>>> GetAsync([Query(CollectionFormat.Multi)] IEnumerable<int> idsWells, CancellationToken token);
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Requests;
|
||||
using AsbCloudDb.Model;
|
||||
using AsbCloudWebApi.IntegrationTests.Clients;
|
||||
using AsbCloudWebApi.IntegrationTests.Data;
|
||||
using System;
|
||||
using Xunit;
|
||||
|
||||
namespace AsbCloudWebApi.IntegrationTests.Controllers;
|
||||
|
||||
public class DrillerControllerTest : BaseIntegrationTest
|
||||
{
|
||||
private readonly IDrillerClient client;
|
||||
|
||||
public DrillerControllerTest(WebAppFactoryFixture factory)
|
||||
: base(factory)
|
||||
{
|
||||
client = factory.GetAuthorizedHttpClient<IDrillerClient>(string.Empty);
|
||||
|
||||
dbContext.CleanupDbSet<Driller>();
|
||||
dbContext.CleanupDbSet<Schedule>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetByWellIds_returns_success()
|
||||
{
|
||||
//arrange
|
||||
var well1 = CreateWellAsync(2);
|
||||
var well2 = CreateWellAsync(3);
|
||||
var well3 = CreateWellAsync(4);
|
||||
dbContext.Wells.Add(well1);
|
||||
dbContext.Wells.Add(well2);
|
||||
dbContext.Wells.Add(well3);
|
||||
|
||||
var driller1 = CreateDrillerAsync(1);
|
||||
var driller2 = CreateDrillerAsync(2);
|
||||
var driller3 = CreateDrillerAsync(3);
|
||||
|
||||
var schedule1= CreateScheduleAsync(well1.Id, driller1);
|
||||
var schedule2 = CreateScheduleAsync(well2.Id, driller2);
|
||||
var schedule3 = CreateScheduleAsync(well3.Id, driller3);
|
||||
|
||||
dbContext.Schedule.Add(schedule1);
|
||||
dbContext.Schedule.Add(schedule2);
|
||||
dbContext.Schedule.Add(schedule3);
|
||||
|
||||
await dbContext.SaveChangesAsync();
|
||||
|
||||
////act
|
||||
var idsWells = dbContext.Wells.ToArray().Select(w => w.Id);
|
||||
var response = await client.GetAsync(idsWells, CancellationToken.None);
|
||||
|
||||
////assert
|
||||
Assert.NotNull(response.Content);
|
||||
Assert.Equal(3, response.Content.Count());
|
||||
}
|
||||
|
||||
private static Schedule CreateScheduleAsync(int idWell, Driller driller) => new()
|
||||
{
|
||||
IdWell = idWell,
|
||||
ShiftStart = new TimeOnly(8, 0, 0),
|
||||
ShiftEnd = new TimeOnly(20, 0, 0),
|
||||
DrillStart = new DateTimeOffset(new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc)),
|
||||
DrillEnd = new DateTimeOffset(new DateTime(2024, 2, 1, 0, 0, 0, DateTimeKind.Utc)),
|
||||
Driller = driller
|
||||
};
|
||||
|
||||
|
||||
private static Well CreateWellAsync(int idWell) => new()
|
||||
{
|
||||
Id = idWell,
|
||||
IdWellType = 1,
|
||||
IdState = 1,
|
||||
Caption = $"Скважина {idWell}",
|
||||
Latitude = 10,
|
||||
Longitude = 20,
|
||||
Timezone = Defaults.Timezone,
|
||||
IdCluster = 1,
|
||||
};
|
||||
|
||||
private static Driller CreateDrillerAsync(int idDriller) => new()
|
||||
{
|
||||
Id = idDriller,
|
||||
Name = idDriller.ToString(),
|
||||
Patronymic = idDriller.ToString(),
|
||||
Surname= idDriller.ToString()
|
||||
};
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
using AsbCloudDb.Model;
|
||||
using AsbCloudInfrastructure.Services.DetectOperations;
|
||||
using AsbCloudInfrastructure.Services.DetectOperations.Detectors;
|
||||
using System;
|
||||
using Xunit;
|
||||
|
||||
namespace AsbCloudWebApi.Tests.Services.DetectedOperations.Detectors;
|
||||
|
||||
/// <summary>
|
||||
/// Тестирование автоопределения операции "Проработка"
|
||||
/// </summary>
|
||||
public class DetectorConditioningTests : DetectorFlashing
|
||||
{
|
||||
private readonly DetectorConditioning detector = new();
|
||||
|
||||
/// <summary>
|
||||
/// Операция, попадающая под автоопределение операции промывки
|
||||
/// </summary>
|
||||
private readonly DetectableTelemetry telemetry = new()
|
||||
{
|
||||
Pressure = 21,
|
||||
RotorSpeed = 9,
|
||||
WellDepth = 152,
|
||||
BitDepth = 151,
|
||||
DateTime = DateTimeOffset.Now,
|
||||
};
|
||||
|
||||
|
||||
[Fact]
|
||||
public void DetectOperation_find_startOperation_notFind_endOperation()
|
||||
{
|
||||
//arrange
|
||||
var point0 = telemetry.Copy();
|
||||
var point1 = telemetry.Copy();
|
||||
point1.DateTime = point0.DateTime.AddMinutes(3);
|
||||
|
||||
var telemetries = new[] { point0, point1 };
|
||||
|
||||
//act
|
||||
var isDetectOperation = detector.TryDetect(0, telemetries, 0, telemetries.Length - 1, null, out var result);
|
||||
|
||||
//assert
|
||||
Assert.True(isDetectOperation);
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(WellOperationCategory.IdConditioning, result.Operation.IdCategory);
|
||||
Assert.Equal(IdReasonOfEnd_NotDetected, result.Operation.ExtraData["IdReasonOfEnd"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DetectOperation_with_BitDepth_LE_150_is_fail()
|
||||
{
|
||||
//arrange
|
||||
var point0 = telemetry.Copy();
|
||||
point0.BitDepth = 150;
|
||||
|
||||
var point1 = telemetry.Copy();
|
||||
|
||||
var telemetries = new[] { point0, point1 };
|
||||
|
||||
//act
|
||||
|
||||
var isDetectOperation = detector.TryDetect(0, telemetries, 0, telemetries.Length - 1, null, out var result);
|
||||
|
||||
//assert
|
||||
Assert.False(isDetectOperation);
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(WellOperationCategory.IdConditioning, result.Operation.IdCategory);
|
||||
Assert.Equal(IdReasonOfEnd_NotDetected, result.Operation.ExtraData["IdReasonOfEnd"]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Fact]
|
||||
public void DetectOperations_Begin_And_End_by_Pressure_Less_10_is_success()
|
||||
{
|
||||
//arrange
|
||||
var point0 = telemetry.Copy();
|
||||
var point1 = telemetry.Copy();
|
||||
point1.Pressure = 9;
|
||||
|
||||
var telemetries = new[] { point0, point1 };
|
||||
|
||||
//act
|
||||
var isDetectOperation = detector.TryDetect(0, telemetries, 0, telemetries.Length - 1, null, out var result);
|
||||
|
||||
//assert
|
||||
Assert.False(isDetectOperation);
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(WellOperationCategory.IdConditioning, result.Operation.IdCategory);
|
||||
Assert.Equal(IdReasonOfEnd_PressureIsLo, result.Operation.ExtraData["IdReasonOfEnd"]);
|
||||
}
|
||||
}
|
@ -1,7 +1,13 @@
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Requests;
|
||||
using AsbCloudApp.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AsbCloudWebApi.Controllers
|
||||
{
|
||||
@ -13,8 +19,38 @@ namespace AsbCloudWebApi.Controllers
|
||||
[Authorize]
|
||||
public class DrillerController : CrudController<DrillerDto, ICrudRepository<DrillerDto>>
|
||||
{
|
||||
public DrillerController(ICrudRepository<DrillerDto> service)
|
||||
private IScheduleRepository scheduleRepository;
|
||||
|
||||
public DrillerController(ICrudRepository<DrillerDto> service, IScheduleRepository scheduleRepository)
|
||||
: base(service)
|
||||
{ }
|
||||
{
|
||||
this.scheduleRepository = scheduleRepository;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить список бурильщиков по ключам скважин
|
||||
/// </summary>
|
||||
/// <param name="idsWells">массив ключей скважин</param>
|
||||
/// <param name="token">token</param>
|
||||
/// <returns>все записи</returns>
|
||||
[HttpGet("/api/drillers")]
|
||||
[Permission]
|
||||
[ProducesResponseType(typeof(IEnumerable<DrillerDto>), StatusCodes.Status200OK)]
|
||||
public async Task<IActionResult> GetAsync([FromQuery] IEnumerable<int> idsWells, CancellationToken token)
|
||||
{
|
||||
var request = new GetStatRequest()
|
||||
{
|
||||
IdsWells = idsWells,
|
||||
};
|
||||
var schedulePage = await scheduleRepository.GetPageAsync(request, token);
|
||||
var drillers = schedulePage
|
||||
.Select(s => s.Driller)
|
||||
.Where(d => d is not null)
|
||||
.GroupBy(d => d.Id)
|
||||
.Select(group => group.First())
|
||||
.OrderBy(d => d.Surname);
|
||||
|
||||
return Ok(drillers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user