DD.WellWorkover.Cloud/AsbCloudInfrastructure/Services/ProcessMaps/WellDrilling/ProcessMapPlanWellDrillingImportService.cs
Степанов Дмитрий 72bb234179 Правки после ревью
2023-10-16 13:45:29 +05:00

362 lines
17 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Data.ProcessMaps;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
using AsbCloudApp.Services.ProcessMaps;
using ClosedXML.Excel;
namespace AsbCloudInfrastructure.Services.ProcessMaps.WellDrillingProcessMap;
/*
* password for ProcessMapImportTemplate.xlsx is ASB2020!
*/
public class ProcessMapPlanWellDrillingImportService : IProcessMapPlanImportService
{
private readonly IProcessMapPlanRepository<ProcessMapPlanWellDrillingDto> processMapPlanWellDrillingRepository;
private readonly ICrudRepository<WellSectionTypeDto> wellSectionTypeRepository;
private readonly IWellService wellService;
private const string sheetNamePlan = "План";
private const int headerRowsCount = 2;
private const int columnWellSectionType = 1;
private const int columnMode = 2;
private const int columnDepthStart = 3;
private const int columnDepthEnd = 4;
private const int columnPressurePlan = 5;
private const int columnPressureLimitMax = 6;
private const int columnAxialLoadPlan = 7;
private const int columnAxialLoadLimitMax = 8;
private const int columnTopDriveTorquePlan = 9;
private const int columnTopDriveTorqueLimitMax = 10;
private const int columnTopDriveSpeedPlan = 11;
private const int columnTopDriveSpeedLimitMax = 12;
private const int columnFlowPlan = 13;
private const int columnFlowLimitMax = 14;
private const int columnRopPlan = 15;
private const int columnUsageSaub = 16;
private const int columnUsageSpin = 17;
private const int columnComment = 18;
private WellSectionTypeDto[] sections = null!;
public ProcessMapPlanWellDrillingImportService(IProcessMapPlanRepository<ProcessMapPlanWellDrillingDto> processMapPlanWellDrillingRepository,
ICrudRepository<WellSectionTypeDto> wellSectionTypeRepository,
IWellService wellService)
{
this.processMapPlanWellDrillingRepository = processMapPlanWellDrillingRepository;
this.wellSectionTypeRepository = wellSectionTypeRepository;
this.wellService = wellService;
}
public async Task ImportAsync(int idWell, int idUser, bool deleteProcessMapPlansBeforeImport, Stream stream,
CancellationToken cancellationToken)
{
sections = (await wellSectionTypeRepository.GetAllAsync(cancellationToken)).ToArray();
using var workBook = new XLWorkbook(stream);
var wellDrillingProcessMaps = ParseWorkBook(workBook);
if (deleteProcessMapPlansBeforeImport)
await processMapPlanWellDrillingRepository.RemoveByWellAsync(idWell);
foreach (var wellDrillingProcessMap in wellDrillingProcessMaps)
{
wellDrillingProcessMap.IdWell = idWell;
wellDrillingProcessMap.IdUser = idUser;
}
await processMapPlanWellDrillingRepository.InsertRangeAsync(wellDrillingProcessMaps, cancellationToken);
}
public async Task<(string Name, Stream File)> ExportAsync(int idWell, CancellationToken cancellationToken)
{
var well = await wellService.GetOrDefaultAsync(idWell, cancellationToken)
?? throw new ArgumentInvalidException(nameof(idWell), $"Скважины с {idWell} не существует");
sections = (await wellSectionTypeRepository.GetAllAsync(cancellationToken)).ToArray();
var processMapPlans = (await processMapPlanWellDrillingRepository.GetByIdWellAsync(idWell,
cancellationToken)).ToArray();
var file = await GenerateExcelFileStreamAsync(processMapPlans, cancellationToken);
var fileName = $"РТК-план бурение по скважине {well.Caption} куст {well.Cluster}.xlsx";
return (fileName, file);
}
public async Task<(string Name, Stream File)> GetExcelTemplateStreamAsync(CancellationToken cancellationToken)
{
var resourceName = Assembly.GetExecutingAssembly()
.GetManifestResourceNames()
.FirstOrDefault(n => n.EndsWith("DrillingProcessMapTemplate.xlsx"))!;
using var stream = Assembly.GetExecutingAssembly()
.GetManifestResourceStream(resourceName)!;
var memoryStream = new MemoryStream();
await stream.CopyToAsync(memoryStream, cancellationToken);
memoryStream.Position = 0;
var name = "ЕЦП_шаблон_файла_РТК_бурение.xlsx";
return (name, memoryStream);
}
private void AddToWorkbook(XLWorkbook workbook, IEnumerable<ProcessMapPlanWellDrillingDto> processMapPlanWellDrillings)
{
var sheet = workbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetNamePlan)
?? throw new FileFormatException($"Книга excel не содержит листа {sheetNamePlan}.");
AddToSheet(sheet, processMapPlanWellDrillings.ToArray());
}
private void AddToSheet(IXLWorksheet sheet, IList<ProcessMapPlanWellDrillingDto> processMapPlanWellDrillings)
{
if (!processMapPlanWellDrillings.Any())
return;
for (int i = 0; i < processMapPlanWellDrillings.Count; i++)
{
var row = sheet.Row(1 + i + headerRowsCount);
AddToRow(row, processMapPlanWellDrillings[i]);
}
}
private void AddToRow(IXLRow row, ProcessMapPlanWellDrillingDto processMapPlanWellDrillings)
{
row.Cell(columnWellSectionType).Value = sections.First(x => x.Id == processMapPlanWellDrillings.IdWellSectionType).Caption;
row.Cell(columnMode).Value = GetModeCaption(processMapPlanWellDrillings.IdMode);
row.Cell(columnDepthStart).Value = processMapPlanWellDrillings.DepthStart;
row.Cell(columnDepthEnd).Value = processMapPlanWellDrillings.DepthEnd;
row.Cell(columnPressurePlan).Value = processMapPlanWellDrillings.Pressure.Plan;
row.Cell(columnPressureLimitMax).Value = processMapPlanWellDrillings.Pressure.LimitMax;
row.Cell(columnAxialLoadPlan).Value = processMapPlanWellDrillings.AxialLoad.Plan;
row.Cell(columnAxialLoadLimitMax).Value = processMapPlanWellDrillings.AxialLoad.LimitMax;
row.Cell(columnTopDriveTorquePlan).Value = processMapPlanWellDrillings.TopDriveTorque.Plan;
row.Cell(columnTopDriveTorqueLimitMax).Value = processMapPlanWellDrillings.TopDriveTorque.LimitMax;
row.Cell(columnTopDriveSpeedPlan).Value = processMapPlanWellDrillings.TopDriveSpeed.Plan;
row.Cell(columnTopDriveSpeedLimitMax).Value = processMapPlanWellDrillings.TopDriveSpeed.LimitMax;
row.Cell(columnFlowPlan).Value = processMapPlanWellDrillings.Flow.Plan;
row.Cell(columnFlowLimitMax).Value = processMapPlanWellDrillings.Flow.LimitMax;
row.Cell(columnRopPlan).Value = processMapPlanWellDrillings.RopPlan;
row.Cell(columnUsageSaub).Value = processMapPlanWellDrillings.UsageSaub;
row.Cell(columnUsageSpin).Value = processMapPlanWellDrillings.UsageSpin;
row.Cell(columnComment).Value = processMapPlanWellDrillings.Comment;
}
private IEnumerable<ProcessMapPlanWellDrillingDto> ParseWorkBook(IXLWorkbook workbook)
{
var sheet = workbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetNamePlan)
?? throw new FileFormatException($"Книга excel не содержит листа {sheetNamePlan}.");
return ParseSheet(sheet);
}
private IEnumerable<ProcessMapPlanWellDrillingDto> ParseSheet(IXLWorksheet sheet)
{
const int columnsCount = 17;
if (sheet.RangeUsed().RangeAddress.LastAddress.ColumnNumber < columnsCount)
throw new FileFormatException($"Лист {sheet.Name} содержит меньшее количество столбцов.");
var rowsCount = sheet.RowsUsed().Count() - headerRowsCount;
if (rowsCount <= 0)
return Array.Empty<ProcessMapPlanWellDrillingDto>();
var processMapPlans = new ProcessMapPlanWellDrillingDto[rowsCount];
var parseErrors = new List<string>();
for (int i = 0; i < processMapPlans.Length; i++)
{
var row = sheet.Row(1 + i + headerRowsCount);
try
{
processMapPlans[i] = ParseRow(row);
}
catch (FileFormatException ex)
{
parseErrors.Add(ex.Message);
}
}
if (parseErrors.Any())
throw new FileFormatException(string.Join("\r\n", parseErrors));
return processMapPlans;
}
private ProcessMapPlanWellDrillingDto ParseRow(IXLRow row)
{
var wellSectionTypeCaption = row.Cell(columnWellSectionType).GetCellValue<string>()?.Trim().ToLower();
var modeName = row.Cell(columnMode).GetCellValue<string>()?.Trim().ToLower();
var depthStart = row.Cell(columnDepthStart).GetCellValue<double>();
var depthEnd = row.Cell(columnDepthEnd).GetCellValue<double>();
var pressurePlan = row.Cell(columnPressurePlan).GetCellValue<double>();
var pressureLimitMax = row.Cell(columnPressureLimitMax).GetCellValue<double>();
var axialLoadPlan = row.Cell(columnAxialLoadPlan).GetCellValue<double>();
var axialLoadLimitMax = row.Cell(columnAxialLoadLimitMax).GetCellValue<double>();
var topDriveTorquePlan = row.Cell(columnTopDriveTorquePlan).GetCellValue<double>();
var topDriveTorqueLimitMax = row.Cell(columnTopDriveTorqueLimitMax).GetCellValue<double>();
var topDriveSpeedPlan = row.Cell(columnTopDriveSpeedPlan).GetCellValue<double>();
var topDriveSpeedLimitMax = row.Cell(columnTopDriveSpeedLimitMax).GetCellValue<double>();
var flowPlan = row.Cell(columnFlowPlan).GetCellValue<double>();
var flowLimitMax = row.Cell(columnFlowLimitMax).GetCellValue<double>();
var ropPlan = row.Cell(columnRopPlan).GetCellValue<double>();
var usageSaub = row.Cell(columnUsageSaub).GetCellValue<double>();
var usageSpin = row.Cell(columnUsageSpin).GetCellValue<double>();
var comment = row.Cell(columnComment).GetCellValue<string?>();
var wellSection = sections.FirstOrDefault(s => s.Caption.Trim().ToLower() == wellSectionTypeCaption)
?? throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указана некорректная секция");
var idMode = GetIdMode(modeName)
?? throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указан некорректный режим");
if (depthStart is < 0 or > 99999.9)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указана некорректная стартовая глубина");
if (depthEnd is < 0 or > 99999.9)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указана некорректная конечная глубина");
if (pressurePlan is < 0 or > 50000)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указано некорректное плановое значение перепада давления");
if (pressureLimitMax is < 0 or > 50000)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указано некорректное ограничение перепада давления");
if (axialLoadPlan is < 0 or > 50000)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указано некорректное плановое значение нагрузки");
if (axialLoadLimitMax is < 0 or > 50000)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указано некорректное ограничение нагрузки");
if (topDriveTorquePlan is < 0 or > 50000)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указано некорректное плановое значение момента на ВСП");
if (topDriveTorqueLimitMax is < 0 or > 50000)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указано некорректное ограничение момента на ВСП");
if (topDriveSpeedPlan is < 0 or > 50000)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указано некорректное плановое значение оборотов на ВСП");
if (topDriveSpeedLimitMax is < 0 or > 50000)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указано некорректное ограничения оборота на ВСП");
if (flowPlan is < 0 or > 50000)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указано некорректное плановое значение расхода");
if (flowLimitMax is < 0 or > 50000)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указано некорректное ограничение расхода");
if (ropPlan is < 0 or > 99999.9)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указано некорректное плановое значение механической скорости");
if (usageSaub is < 0 or > 100)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указан некорректный плановый процент использования АКБ");
if (usageSpin is < 0 or > 100)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указан некорректные плановый процент использования spin master");
return new()
{
IdWellSectionType = wellSection.Id,
IdMode = idMode,
DepthStart = depthStart,
LastUpdate = DateTime.UtcNow,
DepthEnd = depthEnd,
Pressure = new()
{
Plan = pressurePlan,
LimitMax = pressureLimitMax
},
AxialLoad = new()
{
Plan = axialLoadPlan,
LimitMax = axialLoadLimitMax
},
TopDriveTorque = new()
{
Plan = topDriveTorquePlan,
LimitMax = topDriveTorqueLimitMax
},
TopDriveSpeed = new()
{
Plan = topDriveSpeedPlan,
LimitMax = topDriveSpeedLimitMax
},
Flow = new()
{
Plan = flowPlan,
LimitMax = flowLimitMax
},
RopPlan = ropPlan,
UsageSaub = usageSaub,
UsageSpin = usageSpin,
Comment = comment
};
}
private async Task<Stream> GenerateExcelFileStreamAsync(ProcessMapPlanWellDrillingDto[] processMapPlanWellDrillings,
CancellationToken cancellationToken)
{
using var excelTemplateStream = (await GetExcelTemplateStreamAsync(cancellationToken)).File;
using var workbook = new XLWorkbook(excelTemplateStream, XLEventTracking.Disabled);
AddToWorkbook(workbook, processMapPlanWellDrillings);
MemoryStream memoryStream = new();
workbook.SaveAs(memoryStream, new SaveOptions { });
memoryStream.Seek(0, SeekOrigin.Begin);
return memoryStream;
}
private static int? GetIdMode(string modeName) =>
modeName switch
{
"ручной" => 0,
"ротор" => 1,
"слайд" => 2,
_ => null
};
private static string GetModeCaption(int idMode)
=> idMode switch
{
1 => "Ротор",
2 => "Слайд",
_ => "Ручной",
};
}