forked from ddrilling/AsbCloudServer
Экспорт автоопределённых операций
This commit is contained in:
parent
acfe9ab000
commit
ae79426973
@ -14,210 +14,219 @@ using AsbCloudApp.Exceptions;
|
|||||||
using AsbCloudApp.Services;
|
using AsbCloudApp.Services;
|
||||||
using AsbCloudApp.Data;
|
using AsbCloudApp.Data;
|
||||||
using AsbCloudApp.Data.WellOperation;
|
using AsbCloudApp.Data.WellOperation;
|
||||||
|
using AsbCloudApp.Requests;
|
||||||
using AsbCloudInfrastructure.Services.DetectOperations.Detectors;
|
using AsbCloudInfrastructure.Services.DetectOperations.Detectors;
|
||||||
|
|
||||||
namespace AsbCloudInfrastructure.Services.DetectOperations;
|
namespace AsbCloudInfrastructure.Services.DetectOperations;
|
||||||
|
|
||||||
public class DetectedOperationExportService
|
public class DetectedOperationExportService
|
||||||
{
|
{
|
||||||
private readonly IWellService wellService;
|
private readonly IWellService wellService;
|
||||||
private readonly IWellOperationCategoryRepository wellOperationCategoryRepository;
|
private readonly IWellOperationCategoryRepository wellOperationCategoryRepository;
|
||||||
private readonly IDetectedOperationService detectedOperationService;
|
private readonly IDetectedOperationRepository detectedOperationRepository;
|
||||||
private const int headerRowsCount = 1;
|
private const int headerRowsCount = 1;
|
||||||
|
|
||||||
private const string cellDepositName = "B1";
|
private const string cellDepositName = "B1";
|
||||||
private const string cellClusterName = "B2";
|
private const string cellClusterName = "B2";
|
||||||
private const string cellWellName = "B3";
|
private const string cellWellName = "B3";
|
||||||
private const string cellDeltaDate = "H2";
|
private const string cellDeltaDate = "H2";
|
||||||
|
|
||||||
private const int columnOperationName = 1;
|
private const int columnOperationName = 1;
|
||||||
private const int columnDateStart = 2;
|
private const int columnDateStart = 2;
|
||||||
private const int columnDateEnd = 3;
|
private const int columnDateEnd = 3;
|
||||||
private const int columnDuration = 4;
|
private const int columnDuration = 4;
|
||||||
private const int columnDepthStart = 5;
|
private const int columnDepthStart = 5;
|
||||||
private const int columnDepthEnd = 6;
|
private const int columnDepthEnd = 6;
|
||||||
private const int columnDeltaDepth = 7;
|
private const int columnDeltaDepth = 7;
|
||||||
private const int columnDepth = 8;
|
private const int columnDepth = 8;
|
||||||
private const int columnIdReasonOfEnd = 9;
|
private const int columnIdReasonOfEnd = 9;
|
||||||
private const int columnComment = 10;
|
private const int columnComment = 10;
|
||||||
|
|
||||||
public DetectedOperationExportService(IWellService wellService,
|
public DetectedOperationExportService(IWellService wellService,
|
||||||
IWellOperationCategoryRepository wellOperationCategoryRepository,
|
IWellOperationCategoryRepository wellOperationCategoryRepository,
|
||||||
IDetectedOperationService detectedOperationService)
|
IDetectedOperationRepository detectedOperationRepository)
|
||||||
{
|
{
|
||||||
this.wellService = wellService;
|
this.wellService = wellService;
|
||||||
this.wellOperationCategoryRepository = wellOperationCategoryRepository;
|
this.wellOperationCategoryRepository = wellOperationCategoryRepository;
|
||||||
this.detectedOperationService = detectedOperationService;
|
this.detectedOperationRepository = detectedOperationRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Экспорт excel файла с операциями по скважине
|
/// Экспорт excel файла с операциями по скважине
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="idWell">ключ скважины</param>
|
/// <param name="idWell">ключ скважины</param>
|
||||||
/// <param name="host">хост</param>
|
/// <param name="host">хост</param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
/// <exception cref="ArgumentInvalidException"></exception>
|
/// <exception cref="ArgumentInvalidException"></exception>
|
||||||
public async Task<Stream> ExportAsync(int idWell, string host, CancellationToken token)
|
public async Task<Stream> ExportAsync(int idWell, string host, CancellationToken token)
|
||||||
{
|
{
|
||||||
var well = await wellService.GetOrDefaultAsync(idWell, token);
|
var well = await wellService.GetOrDefaultAsync(idWell, token);
|
||||||
|
|
||||||
if (well is null)
|
if (well is null)
|
||||||
throw new ArgumentInvalidException(nameof(idWell), $"Well {idWell} does not exist");
|
throw new ArgumentInvalidException(nameof(idWell), $"Well {idWell} does not exist");
|
||||||
|
|
||||||
if (!well.IdTelemetry.HasValue)
|
if (!well.IdTelemetry.HasValue)
|
||||||
throw new ArgumentInvalidException(nameof(idWell), $"Well {idWell} has no telemetry");
|
throw new ArgumentInvalidException(nameof(idWell), $"Well {idWell} has no telemetry");
|
||||||
|
|
||||||
var operations = await detectedOperationService.DetectOperationsAsync(well.IdTelemetry.Value, DateTime.UnixEpoch, token);
|
var request = new DetectedOperationByTelemetryRequest
|
||||||
|
{
|
||||||
|
IdTelemetry = well.IdTelemetry.Value
|
||||||
|
};
|
||||||
|
|
||||||
return await GenerateExcelFileStreamAsync(well, host, operations, token);
|
var operations = await detectedOperationRepository.Get(request, token);
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<Stream> GenerateExcelFileStreamAsync(WellDto well, string host, IEnumerable<DetectedOperationDto> operationDetectorResults,
|
return await GenerateExcelFileStreamAsync(well, host, operations, token);
|
||||||
CancellationToken cancellationToken)
|
}
|
||||||
{
|
|
||||||
using var excelTemplateStream = await GetExcelTemplateStreamAsync(cancellationToken);
|
|
||||||
|
|
||||||
using var workbook = new XLWorkbook(excelTemplateStream);
|
private async Task<Stream> GenerateExcelFileStreamAsync(WellDto well,
|
||||||
|
string host,
|
||||||
|
IEnumerable<DetectedOperationDto> operationDetectorResults,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
using var excelTemplateStream = await GetExcelTemplateStreamAsync(cancellationToken);
|
||||||
|
|
||||||
AddToWorkbook(workbook, well, host, operationDetectorResults);
|
using var workbook = new XLWorkbook(excelTemplateStream);
|
||||||
|
|
||||||
MemoryStream memoryStream = new MemoryStream();
|
AddToWorkbook(workbook, well, host, operationDetectorResults);
|
||||||
workbook.SaveAs(memoryStream, new SaveOptions { });
|
|
||||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
|
||||||
return memoryStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddToWorkbook(XLWorkbook workbook, WellDto well, string host, IEnumerable<DetectedOperationDto> operations)
|
MemoryStream memoryStream = new MemoryStream();
|
||||||
{
|
workbook.SaveAs(memoryStream, new SaveOptions { });
|
||||||
const string sheetName = "Операции";
|
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
return memoryStream;
|
||||||
|
}
|
||||||
|
|
||||||
if (!operations.Any())
|
private void AddToWorkbook(XLWorkbook workbook, WellDto well, string host, IEnumerable<DetectedOperationDto> operations)
|
||||||
return;
|
{
|
||||||
|
const string sheetName = "Операции";
|
||||||
|
|
||||||
var sheet = workbook.GetWorksheet(sheetName);
|
if (!operations.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
var orderedOperations = operations
|
var sheet = workbook.GetWorksheet(sheetName);
|
||||||
.OrderBy(x => x.DateStart)
|
|
||||||
.ThenBy(x => x.DepthStart).ToArray();
|
|
||||||
|
|
||||||
AddToSheet(sheet, well, host, orderedOperations);
|
var orderedOperations = operations
|
||||||
}
|
.OrderBy(x => x.DateStart)
|
||||||
|
.ThenBy(x => x.DepthStart).ToArray();
|
||||||
|
|
||||||
private void AddToSheet(IXLWorksheet sheet, WellDto well, string host, IList<DetectedOperationDto> operations)
|
AddToSheet(sheet, well, host, orderedOperations);
|
||||||
{
|
}
|
||||||
var wellOperationCategories = wellOperationCategoryRepository.Get(true);
|
|
||||||
|
|
||||||
sheet.Cell(cellDepositName).SetCellValue(well.Deposit);
|
private void AddToSheet(IXLWorksheet sheet, WellDto well, string host, IList<DetectedOperationDto> operations)
|
||||||
sheet.Cell(cellClusterName).SetCellValue(well.Cluster);
|
{
|
||||||
sheet.Cell(cellWellName).SetCellValue(well.Caption);
|
var wellOperationCategories = wellOperationCategoryRepository.Get(true);
|
||||||
|
|
||||||
var deltaDate = operations.Max(o => o.DateEnd - o.DateStart);
|
sheet.Cell(cellDepositName).SetCellValue(well.Deposit);
|
||||||
|
sheet.Cell(cellClusterName).SetCellValue(well.Cluster);
|
||||||
sheet.Cell(cellDeltaDate).SetCellValue(deltaDate);
|
sheet.Cell(cellWellName).SetCellValue(well.Caption);
|
||||||
|
|
||||||
for (int i = 0; i < operations.Count; i++)
|
var deltaDate = operations.Max(o => o.DateEnd - o.DateStart);
|
||||||
{
|
|
||||||
var current = operations[i];
|
|
||||||
var dateStart = current.DateStart.DateTime;
|
|
||||||
var dateEnd = current.DateEnd.DateTime;
|
|
||||||
|
|
||||||
var row = sheet.Row(5 + i + headerRowsCount);
|
sheet.Cell(cellDeltaDate).SetCellValue(deltaDate);
|
||||||
|
|
||||||
var categoryName = GetCategoryName(wellOperationCategories, current);
|
for (int i = 0; i < operations.Count; i++)
|
||||||
|
{
|
||||||
row.Cell(columnDateStart).SetCellValue(dateStart);
|
var current = operations[i];
|
||||||
row.Cell(columnOperationName).SetCellValue(categoryName);
|
var dateStart = current.DateStart.DateTime;
|
||||||
row.Cell(columnDateEnd).SetCellValue(dateEnd);
|
var dateEnd = current.DateEnd.DateTime;
|
||||||
row.Cell(columnDuration).SetCellValue((dateEnd - dateStart).TotalMinutes);
|
|
||||||
row.Cell(columnDepthStart).SetCellValue(current.DepthStart);
|
|
||||||
row.Cell(columnDepthEnd).SetCellValue(current.DepthEnd);
|
|
||||||
row.Cell(columnDepth).SetCellValue(current.DepthEnd - current.DepthStart);
|
|
||||||
|
|
||||||
if (current.ExtraData.TryGetValue("IdReasonOfEnd", out object? idReasonOfEndObject)
|
var row = sheet.Row(5 + i + headerRowsCount);
|
||||||
&& idReasonOfEndObject is int idReasonOfEnd)
|
|
||||||
{
|
|
||||||
var reasonOfEnd = GetIdReasonOfEnd(idReasonOfEnd);
|
|
||||||
row.Cell(columnIdReasonOfEnd).SetCellValue(reasonOfEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
var query = new QueryBuilder();
|
var categoryName = GetCategoryName(wellOperationCategories, current);
|
||||||
query.Add("end", dateStart.AddSeconds(1800 * 0.9).ToString("yyyy-MM-ddTHH:mm:ss.fff"));
|
|
||||||
query.Add("range", "1800");
|
|
||||||
|
|
||||||
var link = $"{host}/well/{well.Id}/telemetry/monitoring{query}";
|
row.Cell(columnDateStart).SetCellValue(dateStart);
|
||||||
row.Cell(columnDateStart).SetHyperlink(link);
|
row.Cell(columnOperationName).SetCellValue(categoryName);
|
||||||
|
row.Cell(columnDateEnd).SetCellValue(dateEnd);
|
||||||
var deltaDepth = i > 0 && i + 1 < operations.Count
|
row.Cell(columnDuration).SetCellValue((dateEnd - dateStart).TotalMinutes);
|
||||||
? current.DepthStart - operations[i - 1].DepthEnd
|
row.Cell(columnDepthStart).SetCellValue(current.DepthStart);
|
||||||
: 0;
|
row.Cell(columnDepthEnd).SetCellValue(current.DepthEnd);
|
||||||
|
row.Cell(columnDepth).SetCellValue(current.DepthEnd - current.DepthStart);
|
||||||
|
|
||||||
row.Cell(columnDeltaDepth).SetCellValue(deltaDepth);
|
if (current.ExtraData.TryGetValue("IdReasonOfEnd", out object? idReasonOfEndObject)
|
||||||
|
&& idReasonOfEndObject is int idReasonOfEnd)
|
||||||
|
{
|
||||||
|
var reasonOfEnd = GetIdReasonOfEnd(idReasonOfEnd);
|
||||||
|
row.Cell(columnIdReasonOfEnd).SetCellValue(reasonOfEnd);
|
||||||
|
}
|
||||||
|
|
||||||
var comment = CreateComment(operations[i]);
|
var query = new QueryBuilder();
|
||||||
row.Cell(columnComment).SetCellValue(comment);
|
query.Add("end", dateStart.AddSeconds(1800 * 0.9).ToString("yyyy-MM-ddTHH:mm:ss.fff"));
|
||||||
}
|
query.Add("range", "1800");
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetCategoryName(IEnumerable<WellOperationCategoryDto> wellOperationCategories, DetectedOperationDto current)
|
var link = $"{host}/well/{well.Id}/telemetry/monitoring{query}";
|
||||||
{
|
row.Cell(columnDateStart).SetHyperlink(link);
|
||||||
var idCategory = current.IdCategory;
|
|
||||||
if (idCategory == WellOperationCategory.IdSlide && current.EnabledSubsystems.IsAutoOscillation)
|
|
||||||
return "Бурение в слайде с осцилляцией";
|
|
||||||
|
|
||||||
var category = wellOperationCategories.FirstOrDefault(o => o.Id == current.IdCategory);
|
var deltaDepth = i > 0 && i + 1 < operations.Count
|
||||||
|
? current.DepthStart - operations[i - 1].DepthEnd
|
||||||
if(category is not null)
|
: 0;
|
||||||
return category.Name;
|
|
||||||
|
|
||||||
return $"Операция №{idCategory}";
|
row.Cell(columnDeltaDepth).SetCellValue(deltaDepth);
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetIdReasonOfEnd(int idReasonOfEnd)
|
var comment = CreateComment(operations[i]);
|
||||||
=> idReasonOfEnd switch {
|
row.Cell(columnComment).SetCellValue(comment);
|
||||||
0 => "Не определена",
|
}
|
||||||
1 => "Не определено начало операции",
|
}
|
||||||
101 => "Разница глубин забоя и положением долота",
|
|
||||||
300 => "Низкое давление",
|
|
||||||
301 => "Высокое давление",
|
|
||||||
700 => "Изменение глубины долота и осевая нагрузка < веса на крюке",
|
|
||||||
_ => idReasonOfEnd.ToString($"Причина № {idReasonOfEnd}"),
|
|
||||||
|
|
||||||
};
|
private static string GetCategoryName(IEnumerable<WellOperationCategoryDto> wellOperationCategories, DetectedOperationDto current)
|
||||||
|
{
|
||||||
|
var idCategory = current.IdCategory;
|
||||||
|
if (idCategory == WellOperationCategory.IdSlide && current.EnabledSubsystems.IsAutoOscillation)
|
||||||
|
return "Бурение в слайде с осцилляцией";
|
||||||
|
|
||||||
private async Task<Stream> GetExcelTemplateStreamAsync(CancellationToken cancellationToken)
|
var category = wellOperationCategories.FirstOrDefault(o => o.Id == current.IdCategory);
|
||||||
{
|
|
||||||
string resourceName = Assembly.GetExecutingAssembly()
|
|
||||||
.GetManifestResourceNames()
|
|
||||||
.FirstOrDefault(n => n.EndsWith("DetectOperations.xlsx"))!;
|
|
||||||
|
|
||||||
using var stream = Assembly.GetExecutingAssembly()
|
if (category is not null)
|
||||||
.GetManifestResourceStream(resourceName)!;
|
return category.Name;
|
||||||
|
|
||||||
var memoryStream = new MemoryStream();
|
return $"Операция №{idCategory}";
|
||||||
await stream.CopyToAsync(memoryStream, cancellationToken);
|
}
|
||||||
memoryStream.Position = 0;
|
|
||||||
|
|
||||||
return memoryStream;
|
private static string GetIdReasonOfEnd(int idReasonOfEnd)
|
||||||
}
|
=> idReasonOfEnd switch
|
||||||
|
{
|
||||||
private static string CreateComment(DetectedOperationDto operation)
|
0 => "Не определена",
|
||||||
{
|
1 => "Не определено начало операции",
|
||||||
switch (operation.IdCategory)
|
101 => "Разница глубин забоя и положением долота",
|
||||||
{
|
300 => "Низкое давление",
|
||||||
case WellOperationCategory.IdRotor:
|
301 => "Высокое давление",
|
||||||
case WellOperationCategory.IdSlide:
|
700 => "Изменение глубины долота и осевая нагрузка < веса на крюке",
|
||||||
var comment = "";
|
_ => idReasonOfEnd.ToString($"Причина № {idReasonOfEnd}"),
|
||||||
if (operation.ExtraData.TryGetValue(DetectorDrilling.ExtraDataKeyAvgRotorSpeed, out object? oAvgRotorSpeed))
|
};
|
||||||
comment += $"Средняя скорость оборотов ротора: {oAvgRotorSpeed}\r\n";
|
|
||||||
|
|
||||||
if (operation.ExtraData.TryGetValue(DetectorDrilling.ExtraDataKeyDispersionOfNormalizedRotorSpeed, out object? oDispersionOfNormalizedRotorSpeed))
|
private async Task<Stream> GetExcelTemplateStreamAsync(CancellationToken cancellationToken)
|
||||||
comment += $"Дисперсия нормированных оборотов ротора: {oDispersionOfNormalizedRotorSpeed}";
|
{
|
||||||
|
string resourceName = Assembly.GetExecutingAssembly()
|
||||||
return comment;
|
.GetManifestResourceNames()
|
||||||
|
.FirstOrDefault(n => n.EndsWith("DetectOperations.xlsx"))!;
|
||||||
default:
|
|
||||||
return string.Empty;
|
using var stream = Assembly.GetExecutingAssembly()
|
||||||
}
|
.GetManifestResourceStream(resourceName)!;
|
||||||
}
|
|
||||||
|
var memoryStream = new MemoryStream();
|
||||||
|
await stream.CopyToAsync(memoryStream, cancellationToken);
|
||||||
|
memoryStream.Position = 0;
|
||||||
|
|
||||||
|
return memoryStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string CreateComment(DetectedOperationDto 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -167,13 +167,13 @@ namespace AsbCloudWebApi.Controllers.SAUB
|
|||||||
public async Task<IActionResult> ExportAsync(int idWell, CancellationToken token)
|
public async Task<IActionResult> ExportAsync(int idWell, CancellationToken token)
|
||||||
{
|
{
|
||||||
var idCompany = User.GetCompanyId();
|
var idCompany = User.GetCompanyId();
|
||||||
|
|
||||||
if (idCompany is null)
|
if (idCompany is null)
|
||||||
return Forbid();
|
return Forbid();
|
||||||
|
|
||||||
var host = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}";
|
var host = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}";
|
||||||
var stream = await detectedOperationExportService.ExportAsync(idWell, host, token);
|
var stream = await detectedOperationExportService.ExportAsync(idWell, host, token);
|
||||||
|
|
||||||
return File(stream, "application/octet-stream", "operations.xlsx");
|
return File(stream, "application/octet-stream", "operations.xlsx");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user