Экспорт автоопределённых операций

This commit is contained in:
Степанов Дмитрий 2024-07-25 16:53:48 +03:00
parent acfe9ab000
commit ae79426973
2 changed files with 174 additions and 165 deletions

View File

@ -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;
}
}
} }

View File

@ -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");
} }