diff --git a/AsbCloudApp/Services/ProcessMaps/WellDrilling/IProcessMapReportDataSaubStatExportService.cs b/AsbCloudApp/Services/ProcessMaps/WellDrilling/IProcessMapReportDataSaubStatExportService.cs new file mode 100644 index 00000000..2f36fed0 --- /dev/null +++ b/AsbCloudApp/Services/ProcessMaps/WellDrilling/IProcessMapReportDataSaubStatExportService.cs @@ -0,0 +1,26 @@ +using AsbCloudApp.Requests; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudApp.Services.ProcessMaps.WellDrilling +{ + /// + /// Сервис экспорт РТК + /// + public interface IProcessMapReportDataSaubStatExportService + { + /// + /// Сформировать файл с данными + /// + /// + /// параметры запроса + /// + /// + Task<(string Name, Stream File)?> ExportAsync(int idWell, DataSaubStatRequest request, CancellationToken cancellationToken); + } +} diff --git a/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj b/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj index ba012eb5..5579d2b2 100644 --- a/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj +++ b/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj @@ -14,6 +14,7 @@ + @@ -37,6 +38,7 @@ + diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index 7cfc86f9..ce742684 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -205,6 +205,7 @@ namespace AsbCloudInfrastructure services.AddScoped(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/AsbCloudInfrastructure/Services/ProcessMaps/Report/ProcessMapReportDataSaubStatExportService.cs b/AsbCloudInfrastructure/Services/ProcessMaps/Report/ProcessMapReportDataSaubStatExportService.cs new file mode 100644 index 00000000..286ec122 --- /dev/null +++ b/AsbCloudInfrastructure/Services/ProcessMaps/Report/ProcessMapReportDataSaubStatExportService.cs @@ -0,0 +1,254 @@ +using AsbCloudApp.Data.ProcessMaps.Report; +using AsbCloudApp.Requests; +using AsbCloudApp.Services; +using AsbCloudApp.Services.ProcessMaps.WellDrilling; +using ClosedXML.Excel; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudInfrastructure.Services.ProcessMaps.Report; + +public class ProcessMapReportDataSaubStatExportService : IProcessMapReportDataSaubStatExportService +{ + private const int firstColumn = 2; + private const int lastColumn = 35; + private const int headerRowsCount = 5; + private const string TemplateName = "ProcessMapReportDataSaubStatTemplate.xlsx"; + private const string sheetName = "Отчёт"; + + private readonly IWellService wellService; + private readonly IProcessMapReportDataSaubStatService processMapReportDataSaubStatService; + + public ProcessMapReportDataSaubStatExportService(IWellService wellService, + IProcessMapReportDataSaubStatService processMapReportDataSaubStatService) + { + this.wellService = wellService; + this.processMapReportDataSaubStatService = processMapReportDataSaubStatService; + } + + public async Task<(string Name, Stream File)?> ExportAsync(int idWell, DataSaubStatRequest request, CancellationToken cancellationToken) + { + var well = await wellService.GetOrDefaultAsync(idWell, cancellationToken); + + if (well is null) + return null; + + var stream = Assembly.GetExecutingAssembly().GetTemplateCopyStream(TemplateName); + using var workbook = new XLWorkbook(stream); + + var data = await processMapReportDataSaubStatService.GetAsync(idWell, request, 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.GetWorksheet(sheetName); + + var startRow = headerRowsCount + 1; + foreach (var item in data) + { + startRow = FillRow(sheet, item, startRow); + } + } + + + private static int FillRow(IXLWorksheet sheet, ProcessMapReportDataSaubStatDto item, int startRow) + { + var endRow = FillIntervalData(sheet, item, startRow); + + var sectionStyle = sheet.Range(startRow, firstColumn, endRow - 1, lastColumn).Style; + SetStyle(sectionStyle); + return endRow; + } + + private static int FillIntervalData(IXLWorksheet sheet, ProcessMapReportDataSaubStatDto interval, int row) + { + const int columnSection = firstColumn + 1; + const int columnDepthStart = firstColumn + 2; + const int columnDepthEnd = firstColumn + 3; + const int columnDeltaDepth = firstColumn + 4; + const int columnDrilledTime = firstColumn + 5; + const int columnMode = firstColumn + 6; + + double? deltaDepth = interval.DeltaDepth.HasValue + ? Math.Round(interval.DeltaDepth.Value, 1) + : null; + + sheet.Cell(row, firstColumn).SetCellValue(interval.DateStart, "DD.MM.YYYY HH:MM"); + sheet.Cell(row, columnSection).SetCellValue(interval.WellSectionTypeName); + sheet.Cell(row, columnDepthStart).SetCellValue(Math.Round(interval.DepthStart, 2)); + sheet.Cell(row, columnDepthEnd).SetCellValue(Math.Round(interval.DepthEnd, 2)); + sheet.Cell(row, columnDeltaDepth).SetCellValue(deltaDepth); + sheet.Cell(row, columnDrilledTime).SetCellValue(Math.Round(interval.DrilledTime, 2)); + sheet.Cell(row, columnMode).SetCellValue(interval.DrillingMode); + + row = FillIntervalModeData(sheet, interval, columnMode, row); + + return row; + } + + private static int FillIntervalModeData(IXLWorksheet sheet, ProcessMapReportDataSaubStatDto modeData, + int column, int row) + { + int columnPressure = column + 1; + int columnLoad = columnPressure + 5; + int columnTorque = columnLoad + 5; + int columnSpeed = columnTorque + 5; + int columnTopDriveSpeed = columnSpeed + 4; + int columnFlow = columnTopDriveSpeed + 2; + int columnRopPlan = columnFlow + 3; + int columnRopFact = columnRopPlan + 1; + + FillIntervalModeDataParam(sheet, modeData.PressureDiff, columnPressure, row); + FillIntervalModeDataParam(sheet, modeData.AxialLoad, columnLoad, row); + FillIntervalModeDataParam(sheet, modeData.TopDriveTorque, columnTorque, row); + FillIntervalModeDataSpeed(sheet, modeData.SpeedLimit, columnSpeed, row); + FillIntervalModeTopDriveSpeed(sheet, modeData.TopDriveSpeed, columnTopDriveSpeed, row); + FillIntervalModeFlow(sheet, modeData.Flow, columnFlow, row); + + double? ropPlan = modeData.Rop.Plan.HasValue + ? Math.Round(modeData.Rop.Plan.Value, 1) + : null; + + double? ropFact = modeData.Rop.Fact.HasValue + ? Math.Round(modeData.Rop.Fact.Value, 1) + : null; + + sheet.Cell(row, columnRopPlan).SetCellValue(ropPlan); + sheet.Cell(row, columnRopFact).SetCellValue(ropFact); + + return row + 1; + } + + private static void FillIntervalModeDataParam(IXLWorksheet sheet, + ProcessMapReportDataSaubStatParamsDto 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; + + double? setpointPlan = dataParam.SetpointPlan.HasValue + ? Math.Round(dataParam.SetpointPlan.Value) + : null; + + double? setpointFact = dataParam.SetpointFact.HasValue + ? Math.Round(dataParam.SetpointFact.Value, 1) + : null; + + double? factWavg = dataParam.FactWavg.HasValue + ? Math.Round(dataParam.FactWavg.Value, 1) + : null; + + double? limit = dataParam.Limit.HasValue + ? Math.Round(dataParam.Limit.Value, 1) + : null; + + double? setpointUsage = dataParam.SetpointUsage.HasValue + ? Math.Round(dataParam.SetpointUsage.Value, 1) + : null; + + sheet.Cell(row, column + columnOffsetSpPlan).SetCellValue(setpointPlan); + sheet.Cell(row, column + columnOffsetSpFact).SetCellValue(setpointFact); + sheet.Cell(row, column + columnOffsetFact).SetCellValue(factWavg); + sheet.Cell(row, column + columnOffsetLimit).SetCellValue(limit); + sheet.Cell(row, column + columnOffsetPercent).SetCellValue(setpointUsage); + } + + private static void FillIntervalModeDataSpeed(IXLWorksheet sheet, + ProcessMapReportDataSaubStatParamsDto dataParam, int column, int row) + { + const int columnOffsetSpPlan = 0; + const int columnOffsetSpFact = 1; + const int columnOffsetFact = 2; + const int columnOffsetPercent = 3; + + double? setpointPlan = dataParam.SetpointPlan.HasValue + ? Math.Round(dataParam.SetpointPlan.Value) + : null; + + double? setpointFact = dataParam.SetpointFact.HasValue + ? Math.Round(dataParam.SetpointFact.Value, 1) + : null; + + double? factWavg = dataParam.FactWavg.HasValue + ? Math.Round(dataParam.FactWavg.Value, 1) + : null; + + double? setpointUsage = dataParam.SetpointUsage.HasValue + ? Math.Round(dataParam.SetpointUsage.Value, 1) + : null; + + sheet.Cell(row, column + columnOffsetSpPlan).SetCellValue(setpointPlan); + sheet.Cell(row, column + columnOffsetSpFact).SetCellValue(setpointFact); + sheet.Cell(row, column + columnOffsetFact).SetCellValue(factWavg); + sheet.Cell(row, column + columnOffsetPercent).SetCellValue(setpointUsage); + } + + private static void FillIntervalModeTopDriveSpeed(IXLWorksheet sheet, + ProcessMapReportDataSaubStatParamsDto dataParam, int column, int row) + { + const int columnOffsetSpPlan = 0; + const int columnOffsetFact = 1; + + double? setpointPlan = dataParam.SetpointPlan.HasValue + ? Math.Round(dataParam.SetpointPlan.Value) + : null; + + double? factWavg = dataParam.FactWavg.HasValue + ? Math.Round(dataParam.FactWavg.Value, 1) + : null; + + sheet.Cell(row, column + columnOffsetSpPlan).SetCellValue(setpointPlan); + sheet.Cell(row, column + columnOffsetFact).SetCellValue(factWavg); + } + + private static void FillIntervalModeFlow(IXLWorksheet sheet, + ProcessMapReportDataSaubStatParamsDto dataParam, int column, int row) + { + const int columnOffsetSpPlan = 0; + const int columnOffsetFact = 1; + const int columnOffsetLimit = 2; + + double? setpointPlan = dataParam.SetpointPlan.HasValue + ? Math.Round(dataParam.SetpointPlan.Value) + : null; + + double? factWavg = dataParam.FactWavg.HasValue + ? Math.Round(dataParam.FactWavg.Value, 1) + : null; + + double? limit = dataParam.Limit.HasValue + ? Math.Round(dataParam.Limit.Value, 1) + : null; + + sheet.Cell(row, column + columnOffsetSpPlan).SetCellValue(setpointPlan); + sheet.Cell(row, column + columnOffsetFact).SetCellValue(factWavg); + sheet.Cell(row, column + columnOffsetLimit).SetCellValue(limit); + } + + private static void SetStyle(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; + + style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center; + } +} diff --git a/AsbCloudInfrastructure/Services/ProcessMaps/Report/ProcessMapReportDataSaubStatTemplate.xlsx b/AsbCloudInfrastructure/Services/ProcessMaps/Report/ProcessMapReportDataSaubStatTemplate.xlsx new file mode 100644 index 00000000..063659a8 Binary files /dev/null and b/AsbCloudInfrastructure/Services/ProcessMaps/Report/ProcessMapReportDataSaubStatTemplate.xlsx differ diff --git a/AsbCloudInfrastructure/XLExtentions.cs b/AsbCloudInfrastructure/XLExtentions.cs index 927c90a8..e9cf11f5 100644 --- a/AsbCloudInfrastructure/XLExtentions.cs +++ b/AsbCloudInfrastructure/XLExtentions.cs @@ -15,29 +15,31 @@ public static class XLExtentions workbook.Worksheets.FirstOrDefault(ws => string.Equals(ws.Name.Trim(), sheetName.Trim(), StringComparison.CurrentCultureIgnoreCase)) ?? throw new FileFormatException(string.Format(NotFoundSheetTemplate, sheetName)); - public static IXLCell SetCellValue(this IXLCell cell, T value) - { - if (typeof(T) == typeof(DateTime)) - cell.Style.DateFormat.Format = "DD.MM.YYYY HH:MM:SS"; + public static IXLCell SetCellValue(this IXLCell cell, T value, string? format = null) + { + if (typeof(T) == typeof(DateTime)) + { + cell.Style.DateFormat.Format = format ?? "DD.MM.YYYY HH:MM:SS"; + } - cell.Value = XLCellValue.FromObject(value); + cell.Value = XLCellValue.FromObject(value); - return cell; - } + return cell; + } - public static IXLCell SetHyperlink(this IXLCell cell, string link) - { - cell.SetHyperlink(new XLHyperlink(link)); + public static IXLCell SetHyperlink(this IXLCell cell, string link) + { + cell.SetHyperlink(new XLHyperlink(link)); - return cell; - } + return cell; + } - public static T? GetCellValue(this IXLCell cell) - { - try - { - if (cell.IsEmpty() && default(T) == null) - return default; + public static T? GetCellValue(this IXLCell cell) + { + try + { + if (cell.IsEmpty() && default(T) == null) + return default; return cell.GetValue(); } diff --git a/AsbCloudWebApi/Controllers/ProcessMaps/ProcessMapWellDrillingController.cs b/AsbCloudWebApi/Controllers/ProcessMaps/ProcessMapWellDrillingController.cs index 857e4a68..9c284220 100644 --- a/AsbCloudWebApi/Controllers/ProcessMaps/ProcessMapWellDrillingController.cs +++ b/AsbCloudWebApi/Controllers/ProcessMaps/ProcessMapWellDrillingController.cs @@ -11,6 +11,7 @@ using AsbCloudWebApi.SignalR.Clients; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.SignalR; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.IO; @@ -24,9 +25,8 @@ namespace AsbCloudWebApi.Controllers.ProcessMaps; /// public class ProcessMapWellDrillingController : ProcessMapBaseController { - private readonly IProcessMapReportWellDrillingService processMapReportWellDrillingService; private readonly IProcessMapReportDataSaubStatService processMapReportDataSaubStatService; - private readonly IProcessMapReportWellDrillingExportService processMapReportWellDrillingExportService; + private readonly IProcessMapReportDataSaubStatExportService processMapReportDataSaubStatExportService; private readonly IProcessMapPlanImportService processMapPlanImportService; protected override string SignalRGroup => "ProcessMapWellDrilling"; @@ -34,9 +34,8 @@ public class ProcessMapWellDrillingController : ProcessMapBaseController repository, IUserRepository userRepository, - IProcessMapReportWellDrillingExportService processMapReportWellDrillingExportService, + IProcessMapReportDataSaubStatExportService processMapReportDataSaubStatExportService, IProcessMapPlanImportService processMapPlanImportService, - IProcessMapReportWellDrillingService processMapReportWellDrillingService, IProcessMapReportDataSaubStatService processMapReportDataSaubStatService, ICrudRepository wellSectionRepository, IHubContext telemetryHubContext, @@ -44,10 +43,9 @@ public class ProcessMapWellDrillingController : ProcessMapBaseController service) : base(wellService, repository, userRepository, wellSectionRepository, telemetryHubContext, telemetryService, service) { - this.processMapReportWellDrillingExportService = processMapReportWellDrillingExportService; + this.processMapReportDataSaubStatExportService = processMapReportDataSaubStatExportService; this.processMapPlanImportService = processMapPlanImportService; this.processMapReportDataSaubStatService = processMapReportDataSaubStatService; - this.processMapReportWellDrillingService = processMapReportWellDrillingService; } /// @@ -70,14 +68,15 @@ public class ProcessMapWellDrillingController : ProcessMapBaseController /// Id скважины + /// Параметры запроса /// /// [HttpGet("report/export")] [ProducesResponseType(typeof(PhysicalFileResult), StatusCodes.Status200OK, "application/octet-stream")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task ExportReportAsync(int idWell, CancellationToken cancellationToken) + public async Task ExportReportAsync(int idWell, [FromQuery] DataSaubStatRequest request, CancellationToken cancellationToken) { - var report = await processMapReportWellDrillingExportService.ExportAsync(idWell, cancellationToken); + var report = await processMapReportDataSaubStatExportService.ExportAsync(idWell, request, cancellationToken); if (report is null) return NoContent(); @@ -93,6 +92,7 @@ public class ProcessMapWellDrillingController : ProcessMapBaseController /// /// + [Obsolete] [HttpPost("import/{options}")] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] public async Task ImportAsync(int idWell, @@ -131,6 +131,7 @@ public class ProcessMapWellDrillingController : ProcessMapBaseControllerId скважины /// /// + [Obsolete] [HttpGet("export")] [ProducesResponseType(typeof(PhysicalFileResult), StatusCodes.Status200OK, "application/octet-stream")] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] @@ -145,6 +146,7 @@ public class ProcessMapWellDrillingController : ProcessMapBaseController /// Запрашиваемый файл + [Obsolete] [HttpGet("template")] [ProducesResponseType(typeof(PhysicalFileResult), StatusCodes.Status200OK, "application/octet-stream")] public async Task GetTemplateAsync(CancellationToken cancellationToken)