using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; using AsbCloudApp.Data.ProcessMaps.Report; using AsbCloudApp.Services; using AsbCloudApp.Services.ProcessMaps.WellDrilling; using ClosedXML.Excel; namespace AsbCloudInfrastructure.Services.ProcessMaps.Report; public class ProcessMapReportWellDrillingExportService : IProcessMapReportWellDrillingExportService { const int firstColumn = 2; const int lastColumn = 42; const int headerRowsCount = 5; private readonly IWellService wellService; private readonly IProcessMapReportWellDrillingService processMapReportWellDrillingService; public ProcessMapReportWellDrillingExportService(IWellService wellService, IProcessMapReportWellDrillingService processMapReportWellDrillingService) { this.wellService = wellService; this.processMapReportWellDrillingService = processMapReportWellDrillingService; } public async Task<(string Name, Stream File)?> ExportAsync(int idWell, CancellationToken cancellationToken) { var well = await wellService.GetOrDefaultAsync(idWell, cancellationToken); if (well is null) return null; var stream = GetExcelTemplateStream(); using var workbook = new XLWorkbook(stream, XLEventTracking.Disabled); var data = await processMapReportWellDrillingService.GetAsync(idWell, cancellationToken); FillProcessMapToWorkbook(workbook, data); MemoryStream memoryStream = new(); workbook.SaveAs(memoryStream, new SaveOptions { }); memoryStream.Seek(0, SeekOrigin.Begin); var name = $"РТК бурение. Отчёт по скважине {well.Caption} куст {well.Cluster}.xlsx"; return (name, memoryStream); } private static void FillProcessMapToWorkbook(XLWorkbook workbook, IEnumerable data) { var sheet = workbook.Worksheets.FirstOrDefault(); if (sheet is null) return; var dataBySections = data.GroupBy(p => p.IdWellSectionType); FillSheet(sheet, dataBySections); } private static void FillSheet(IXLWorksheet sheet, IEnumerable> dataBySections) { var startRow = headerRowsCount + 1; foreach (var sectionData in dataBySections) { if (sectionData.Any()) startRow = FillSection(sheet, sectionData, startRow); } } private static int FillSection(IXLWorksheet sheet, IGrouping sectionData, int row) { var rowStart = row; var sectionName = sectionData.FirstOrDefault()?.WellSectionTypeName ?? sectionData.Key.ToString(); sheet.Range(row, firstColumn, row, lastColumn) .Merge() .FirstCell() .SetVal(sectionName) .Style .Fill.SetBackgroundColor(XLColor.LightGray); row++; foreach (var interval in sectionData) row = FillIntervalData(sheet, interval, row); var sectionStyle = sheet.Range(rowStart, firstColumn, row - 1, lastColumn).Style; SetBorders(sectionStyle); return row; } private static int FillIntervalData(IXLWorksheet sheet, ProcessMapReportWellDrillingDto interval, int row) { const int columnDepth = firstColumn + 1; const int columnDate = firstColumn + 2; const int columnRopTime = firstColumn + 3; const int columnMode = firstColumn + 4; sheet.Cell(row, firstColumn) .SetVal(interval.DepthStart, "0.0"); sheet.Cell(row, columnDepth) .SetVal(interval.DepthEnd, "0.0"); sheet.Cell(row, columnDate) .SetVal(interval.DateStart); sheet.Cell(row, columnRopTime) .SetVal(interval.MechDrillingHours); row = FillIntervalModeData(sheet, interval, columnMode, row); return row; } private static int FillIntervalModeData(IXLWorksheet sheet, ProcessMapReportWellDrillingDto modeData, int column, int row) { int columnDeltaDepth = column + 1; int columnPressure = columnDeltaDepth + 1; int columnLoad = columnPressure + 5; int columnTorque = columnLoad + 5; int columnSpeed = columnTorque + 5; int columnUsagePlan = columnSpeed + 5; int columnUsageFact = columnUsagePlan + 1; int columnRop = columnUsageFact + 12; sheet.Cell(row, column) .SetVal(modeData.DrillingMode); sheet.Cell(row, columnDeltaDepth) .SetVal(modeData.DeltaDepth); FillIntervalModeDataParam(sheet, modeData.PressureDiff, columnPressure, row); FillIntervalModeDataParam(sheet, modeData.AxialLoad, columnLoad, row); FillIntervalModeDataParam(sheet, modeData.TopDriveTorque, columnTorque, row); FillIntervalModeDataSpeed(sheet, modeData.SpeedLimit, columnSpeed, row); sheet.Cell(row, columnUsagePlan) .SetVal(modeData.UsagePlan); sheet.Cell(row, columnUsageFact) .SetVal(modeData.UsageFact); sheet.Cell(row, columnRop) .SetVal(modeData.Rop.Fact); return row + 1; } private static void FillIntervalModeDataParam(IXLWorksheet sheet, ProcessMapReportWellDrillingParamsDto dataParam, int column, int row) { const int columnOffsetSpPlan = 0; const int columnOffsetSpFact = 1; const int columnOffsetFact = 2; const int columnOffsetLimit = 3; const int columnOffsetPercent = 4; sheet.Cell(row, column + columnOffsetSpPlan) .SetVal(dataParam.SetpointPlan); sheet.Cell(row, column + columnOffsetSpFact) .SetVal(dataParam.SetpointFact); sheet.Cell(row, column + columnOffsetFact) .SetVal(dataParam.Fact); sheet.Cell(row, column + columnOffsetLimit) .SetVal(dataParam.Limit); sheet.Cell(row, column + columnOffsetPercent) .SetVal(dataParam.SetpointUsage, format: "0.0"); } private static void FillIntervalModeDataSpeed(IXLWorksheet sheet, ProcessMapReportWellDrillingParamsDto dataParam, int column, int row) { const int columnOffsetSpPlan = 0; const int columnOffsetSpFact = 1; const int columnOffsetFact = 2; const int columnOffsetLimit = 3; const int columnOffsetPercent = 4; sheet.Cell(row, column + columnOffsetSpPlan) .SetVal(dataParam.SetpointPlan); sheet.Cell(row, column + columnOffsetSpFact) .SetVal(dataParam.SetpointFact); sheet.Cell(row, column + columnOffsetFact) .SetVal(dataParam.Fact); sheet.Cell(row, column + columnOffsetLimit) .SetVal(dataParam.Limit); sheet.Cell(row, column + columnOffsetPercent) .SetVal(dataParam.SetpointUsage, format: "0.0"); } private static Stream GetExcelTemplateStream() { var stream = Assembly.GetExecutingAssembly() .GetManifestResourceStream( "AsbCloudInfrastructure.Services.ProcessMaps.Report.ProcessMapReportTemplate.xlsx"); return stream!; } private static void SetBorders(IXLStyle style) { style.Border.RightBorder = XLBorderStyleValues.Thin; style.Border.LeftBorder = XLBorderStyleValues.Thin; style.Border.TopBorder = XLBorderStyleValues.Thin; style.Border.BottomBorder = XLBorderStyleValues.Thin; style.Border.InsideBorder = XLBorderStyleValues.Thin; } }