更新
This commit is contained in:
@@ -2,7 +2,7 @@ namespace ConeCalorimeter.Models;
|
|||||||
|
|
||||||
public sealed class ReportInput
|
public sealed class ReportInput
|
||||||
{
|
{
|
||||||
public string LaboratoryName { get; set; } = "广东安拓普检测中心";
|
public string LaboratoryName { get; set; } = "检测中心";
|
||||||
|
|
||||||
public string Operator { get; set; } = string.Empty;
|
public string Operator { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
|||||||
@@ -26,31 +26,54 @@ public sealed class NpoiReportExportService : IReportExportService
|
|||||||
"热释放KW/m2",
|
"热释放KW/m2",
|
||||||
"EHC",
|
"EHC",
|
||||||
"损失质量",
|
"损失质量",
|
||||||
"试样温度(℃)"
|
"试样温度(℃)",
|
||||||
|
"Timestamp",
|
||||||
|
"点火计时(s)",
|
||||||
|
"火焰监测",
|
||||||
|
"孔板流量",
|
||||||
|
"辐射锥温度 (℃)",
|
||||||
|
"当前质量",
|
||||||
|
"初始质量",
|
||||||
|
"最大热释放",
|
||||||
|
"热释放速率180",
|
||||||
|
"热释放速率300",
|
||||||
|
"C-系数"
|
||||||
];
|
];
|
||||||
|
|
||||||
public void Export(string outputPath, ReportInput input, IReadOnlyList<RealtimeDataRecord> records)
|
public void Export(string outputPath, ReportInput input, IReadOnlyList<RealtimeDataRecord> records)
|
||||||
{
|
{
|
||||||
|
var exportRecords = records.Where(IsValidExperimentRecord).ToList();
|
||||||
|
if (exportRecords.Count == 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("请先点击“测试开始”,产生有效实验数据后再导出报表。");
|
||||||
|
}
|
||||||
|
|
||||||
var templatePath = FindTemplatePath();
|
var templatePath = FindTemplatePath();
|
||||||
using var templateStream = File.OpenRead(templatePath);
|
using var templateStream = File.OpenRead(templatePath);
|
||||||
var workbook = new HSSFWorkbook(templateStream);
|
var workbook = new HSSFWorkbook(templateStream);
|
||||||
|
|
||||||
FillReportSheet(workbook.GetSheet("Result_ISO"), input, records);
|
FillReportSheet(workbook.GetSheet("Result_ISO"), "Result_ISO", input, exportRecords);
|
||||||
FillReportSheet(workbook.GetSheet("Result_FTP"), input, records);
|
FillReportSheet(workbook.GetSheet("Result_FTP"), "Result_FTP", input, exportRecords);
|
||||||
FillDataSheet(workbook.GetSheet("Data") ?? workbook.CreateSheet("Data"), records);
|
FillDataSheet(workbook.GetSheet("Data") ?? workbook.CreateSheet("Data"), exportRecords);
|
||||||
|
|
||||||
using var outputStream = File.Create(outputPath);
|
using var outputStream = File.Create(outputPath);
|
||||||
workbook.Write(outputStream);
|
workbook.Write(outputStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void FillReportSheet(ISheet? sheet, ReportInput input, IReadOnlyList<RealtimeDataRecord> records)
|
private static void FillReportSheet(
|
||||||
|
ISheet? sheet,
|
||||||
|
string sheetName,
|
||||||
|
ReportInput input,
|
||||||
|
IReadOnlyList<RealtimeDataRecord> records)
|
||||||
{
|
{
|
||||||
if (sheet is null)
|
if (sheet is null)
|
||||||
{
|
{
|
||||||
return;
|
throw new InvalidOperationException($"报表模板缺少工作表:{sheetName}");
|
||||||
}
|
}
|
||||||
|
|
||||||
var summary = BuildSummary(records);
|
var summary = BuildSummary(records);
|
||||||
|
ValidateSummary(summary);
|
||||||
|
|
||||||
SetValueBesideLabel(sheet, "实验室名称", input.LaboratoryName);
|
SetValueBesideLabel(sheet, "实验室名称", input.LaboratoryName);
|
||||||
SetValueBesideLabel(sheet, "实验员", input.Operator);
|
SetValueBesideLabel(sheet, "实验员", input.Operator);
|
||||||
SetValueBesideLabel(sheet, "文件名", input.FileName);
|
SetValueBesideLabel(sheet, "文件名", input.FileName);
|
||||||
@@ -59,7 +82,7 @@ public sealed class NpoiReportExportService : IReportExportService
|
|||||||
SetValueBesideLabel(sheet, "材料", input.Material);
|
SetValueBesideLabel(sheet, "材料", input.Material);
|
||||||
SetValueBesideLabel(sheet, "样品", input.SampleName);
|
SetValueBesideLabel(sheet, "样品", input.SampleName);
|
||||||
SetValueBesideLabel(sheet, "厚度", input.Thickness);
|
SetValueBesideLabel(sheet, "厚度", input.Thickness);
|
||||||
SetValueBesideLabel(sheet, "初始质量", FirstNonEmpty(input.InitialMass, summary.InitialMass));
|
SetRequiredValueBesideLabel(sheet, "初始质量", FirstNonEmpty(input.InitialMass, summary.InitialMass));
|
||||||
SetValueBesideLabel(sheet, "辐射面积", input.IrradiatedArea);
|
SetValueBesideLabel(sheet, "辐射面积", input.IrradiatedArea);
|
||||||
SetValueBesideLabel(sheet, "热辐射值", input.Irradiance);
|
SetValueBesideLabel(sheet, "热辐射值", input.Irradiance);
|
||||||
SetValueBesideLabel(sheet, "辐射距离", input.IrradianceDistance);
|
SetValueBesideLabel(sheet, "辐射距离", input.IrradianceDistance);
|
||||||
@@ -87,7 +110,7 @@ public sealed class NpoiReportExportService : IReportExportService
|
|||||||
SetValueBesideLabel(sheet, "结束标准", input.EndCriteria);
|
SetValueBesideLabel(sheet, "结束标准", input.EndCriteria);
|
||||||
SetValueBesideLabel(sheet, "结束时间", FirstNonEmpty(input.EndTime, summary.EndTime));
|
SetValueBesideLabel(sheet, "结束时间", FirstNonEmpty(input.EndTime, summary.EndTime));
|
||||||
SetValueBesideLabel(sheet, "E等价热值", input.EquivalentHeatValue);
|
SetValueBesideLabel(sheet, "E等价热值", input.EquivalentHeatValue);
|
||||||
SetValueBesideLabel(sheet, "C-系数", FirstNonEmpty(input.CFactor, summary.CFactor));
|
SetRequiredValueBesideLabel(sheet, "C-系数", FirstNonEmpty(input.CFactor, summary.CFactor));
|
||||||
SetValueBesideLabel(sheet, "光程", input.LightPath);
|
SetValueBesideLabel(sheet, "光程", input.LightPath);
|
||||||
SetValueBesideLabel(sheet, "O2延迟时间", input.O2DelayTime);
|
SetValueBesideLabel(sheet, "O2延迟时间", input.O2DelayTime);
|
||||||
SetValueBesideLabel(sheet, "CO2延迟时间", input.CO2DelayTime);
|
SetValueBesideLabel(sheet, "CO2延迟时间", input.CO2DelayTime);
|
||||||
@@ -98,11 +121,11 @@ public sealed class NpoiReportExportService : IReportExportService
|
|||||||
SetValueBesideLabel(sheet, "基线C2氧含量", input.BaselineC2Oxygen);
|
SetValueBesideLabel(sheet, "基线C2氧含量", input.BaselineC2Oxygen);
|
||||||
SetValueBesideLabel(sheet, "基线CO2氧含量", input.BaselineCO2Oxygen);
|
SetValueBesideLabel(sheet, "基线CO2氧含量", input.BaselineCO2Oxygen);
|
||||||
|
|
||||||
SetValueBesideLabel(sheet, "总热释放", summary.TotalHeatRelease);
|
SetRequiredValueBesideLabel(sheet, "总热释放", summary.TotalHeatRelease);
|
||||||
SetValueBesideLabel(sheet, "总产烟量", summary.TotalSmoke);
|
SetRequiredValueBesideLabel(sheet, "总产烟量", summary.TotalSmoke);
|
||||||
SetValueBesideLabel(sheet, "质量损失", summary.MassLoss);
|
SetRequiredValueBesideLabel(sheet, "质量损失", summary.MassLoss);
|
||||||
SetValueBesideLabel(sheet, "热释放(30)最大", summary.PeakHeatReleaseRate);
|
SetRequiredValueBesideLabel(sheet, "热释放(30)最大", summary.PeakHeatReleaseRate);
|
||||||
SetValueBesideLabel(sheet, "产烟率(30)最大", summary.PeakSmokeProduction);
|
SetRequiredValueBesideLabel(sheet, "产烟率(30)最大", summary.PeakSmokeProduction);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void FillDataSheet(ISheet sheet, IReadOnlyList<RealtimeDataRecord> records)
|
private static void FillDataSheet(ISheet sheet, IReadOnlyList<RealtimeDataRecord> records)
|
||||||
@@ -119,6 +142,7 @@ public sealed class NpoiReportExportService : IReportExportService
|
|||||||
{
|
{
|
||||||
var row = sheet.CreateRow(i + 1);
|
var row = sheet.CreateRow(i + 1);
|
||||||
var record = records[i];
|
var record = records[i];
|
||||||
|
// Keep columns A-O fixed; the Excel template charts reference these positions.
|
||||||
SetNumeric(row, 0, record.TestSeconds >= 0 ? record.TestSeconds : double.NaN);
|
SetNumeric(row, 0, record.TestSeconds >= 0 ? record.TestSeconds : double.NaN);
|
||||||
SetNumeric(row, 1, record.Oxygen);
|
SetNumeric(row, 1, record.Oxygen);
|
||||||
SetNumeric(row, 2, record.CarbonDioxide);
|
SetNumeric(row, 2, record.CarbonDioxide);
|
||||||
@@ -134,6 +158,17 @@ public sealed class NpoiReportExportService : IReportExportService
|
|||||||
SetNumeric(row, 12, record.EffectiveHeatOfCombustion);
|
SetNumeric(row, 12, record.EffectiveHeatOfCombustion);
|
||||||
SetNumeric(row, 13, record.MassLoss);
|
SetNumeric(row, 13, record.MassLoss);
|
||||||
SetNumeric(row, 14, record.SampleTemperature);
|
SetNumeric(row, 14, record.SampleTemperature);
|
||||||
|
row.CreateCell(15).SetCellValue(record.Timestamp.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.CurrentCulture));
|
||||||
|
SetNumeric(row, 16, record.IgnitionSeconds >= 0 ? record.IgnitionSeconds : double.NaN);
|
||||||
|
SetNumeric(row, 17, record.FlameDetected ? 1 : 0);
|
||||||
|
SetNumeric(row, 18, record.OrificeFlow);
|
||||||
|
SetNumeric(row, 19, record.ConeTemperature);
|
||||||
|
SetNumeric(row, 20, record.CurrentMass);
|
||||||
|
SetNumeric(row, 21, record.InitialMass);
|
||||||
|
SetNumeric(row, 22, record.PeakHeatReleaseRate);
|
||||||
|
SetNumeric(row, 23, record.Qa180);
|
||||||
|
SetNumeric(row, 24, record.Qa300);
|
||||||
|
SetNumeric(row, 25, record.CFactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < DataHeaders.Length; i++)
|
for (var i = 0; i < DataHeaders.Length; i++)
|
||||||
@@ -142,11 +177,11 @@ public sealed class NpoiReportExportService : IReportExportService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SetValueBesideLabel(ISheet sheet, string label, string value)
|
private static bool SetValueBesideLabel(ISheet sheet, string label, string value)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(value))
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
{
|
{
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var rowIndex = sheet.FirstRowNum; rowIndex <= sheet.LastRowNum; rowIndex++)
|
for (var rowIndex = sheet.FirstRowNum; rowIndex <= sheet.LastRowNum; rowIndex++)
|
||||||
@@ -173,9 +208,24 @@ public sealed class NpoiReportExportService : IReportExportService
|
|||||||
var targetColumn = FindTargetColumn(sheet, rowIndex, columnIndex);
|
var targetColumn = FindTargetColumn(sheet, rowIndex, columnIndex);
|
||||||
var target = row.GetCell(targetColumn) ?? row.CreateCell(targetColumn);
|
var target = row.GetCell(targetColumn) ?? row.CreateCell(targetColumn);
|
||||||
target.SetCellValue(value);
|
target.SetCellValue(value);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetRequiredValueBesideLabel(ISheet sheet, string label, string value)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"缺少关键导出数据:{label}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SetValueBesideLabel(sheet, label, value))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"报表模板缺少字段:{label}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int FindTargetColumn(ISheet sheet, int rowIndex, int columnIndex)
|
private static int FindTargetColumn(ISheet sheet, int rowIndex, int columnIndex)
|
||||||
@@ -269,8 +319,28 @@ public sealed class NpoiReportExportService : IReportExportService
|
|||||||
MassLoss: FormatWithUnit(LastFinite(records, record => record.MassLoss), "g"),
|
MassLoss: FormatWithUnit(LastFinite(records, record => record.MassLoss), "g"),
|
||||||
InitialMass: FormatWithUnit(LastFinite(records, record => record.InitialMass), "g"),
|
InitialMass: FormatWithUnit(LastFinite(records, record => record.InitialMass), "g"),
|
||||||
CFactor: FormatValue(LastFinite(records, record => record.CFactor)),
|
CFactor: FormatValue(LastFinite(records, record => record.CFactor)),
|
||||||
IgnitionTime: ignition is null ? "" : $"{ignition.Value} s",
|
IgnitionTime: FormatSeconds(ignition),
|
||||||
EndTime: $"{last.TestSeconds} s");
|
EndTime: FormatSeconds(last.TestSeconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ValidateSummary(ReportSummary summary)
|
||||||
|
{
|
||||||
|
RequireSummaryValue("总热释放", summary.TotalHeatRelease);
|
||||||
|
RequireSummaryValue("总产烟量", summary.TotalSmoke);
|
||||||
|
RequireSummaryValue("质量损失", summary.MassLoss);
|
||||||
|
RequireSummaryValue("初始质量", summary.InitialMass);
|
||||||
|
RequireSummaryValue("C-系数", summary.CFactor);
|
||||||
|
RequireSummaryValue("热释放(30)最大", summary.PeakHeatReleaseRate);
|
||||||
|
RequireSummaryValue("产烟率(30)最大", summary.PeakSmokeProduction);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RequireSummaryValue(string label, string value)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"缺少关键导出数据:{label}。请确认已点击测试开始并采集到有效实验数据。");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string FormatWithUnit(double? value, string unit)
|
private static string FormatWithUnit(double? value, string unit)
|
||||||
@@ -283,6 +353,18 @@ public sealed class NpoiReportExportService : IReportExportService
|
|||||||
return value is null || !double.IsFinite(value.Value) ? "" : $"{value.Value:0.00}";
|
return value is null || !double.IsFinite(value.Value) ? "" : $"{value.Value:0.00}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string FormatSeconds(int? value)
|
||||||
|
{
|
||||||
|
return value.HasValue && value.Value >= 0 ? $"{value.Value} s" : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsValidExperimentRecord(RealtimeDataRecord record)
|
||||||
|
{
|
||||||
|
return record.TestSeconds >= 0
|
||||||
|
&& double.IsFinite(record.TotalHeatRelease)
|
||||||
|
&& double.IsFinite(record.TotalSmoke);
|
||||||
|
}
|
||||||
|
|
||||||
private static double? LastFinite(IReadOnlyList<RealtimeDataRecord> records, Func<RealtimeDataRecord, double> selector)
|
private static double? LastFinite(IReadOnlyList<RealtimeDataRecord> records, Func<RealtimeDataRecord, double> selector)
|
||||||
{
|
{
|
||||||
for (var i = records.Count - 1; i >= 0; i--)
|
for (var i = records.Count - 1; i >= 0; i--)
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ public sealed class ReportPageViewModel : PageViewModel
|
|||||||
[
|
[
|
||||||
new ReportSectionViewModel("报告信息",
|
new ReportSectionViewModel("报告信息",
|
||||||
[
|
[
|
||||||
new ReportFieldViewModel("LaboratoryName", "实验室名称", "广东安拓普检测中心"),
|
new ReportFieldViewModel("LaboratoryName", "实验室名称", "检测中心"),
|
||||||
new ReportFieldViewModel("Operator", "实验员"),
|
new ReportFieldViewModel("Operator", "实验员"),
|
||||||
new ReportFieldViewModel("FileName", "文件名"),
|
new ReportFieldViewModel("FileName", "文件名"),
|
||||||
new ReportFieldViewModel("ReportName", "报告名")
|
new ReportFieldViewModel("ReportName", "报告名")
|
||||||
@@ -143,14 +143,21 @@ public sealed class ReportPageViewModel : PageViewModel
|
|||||||
SummaryItems[2].Value = records.Last().Timestamp.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.CurrentCulture);
|
SummaryItems[2].Value = records.Last().Timestamp.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.CurrentCulture);
|
||||||
var last = records.Last();
|
var last = records.Last();
|
||||||
SummaryItems[3].Value = FormatWithUnit(last.PeakHeatReleaseRate, "kW/㎡");
|
SummaryItems[3].Value = FormatWithUnit(last.PeakHeatReleaseRate, "kW/㎡");
|
||||||
SummaryItems[4].Value = FormatWithUnit(last.TotalHeatRelease, "MJ/㎡");
|
SummaryItems[4].Value = FormatWithUnit(LastFinite(records, record => record.TotalHeatRelease), "MJ/㎡");
|
||||||
SummaryItems[5].Value = FormatWithUnit(last.TotalSmoke, "m²");
|
SummaryItems[5].Value = FormatWithUnit(LastFinite(records, record => record.TotalSmoke), "m²");
|
||||||
SummaryItems[6].Value = FormatWithUnit(last.MassLoss, "g");
|
SummaryItems[6].Value = FormatWithUnit(LastFinite(records, record => record.MassLoss), "g");
|
||||||
FillCollectedReportFields(records);
|
FillCollectedReportFields(records);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExportReport()
|
private void ExportReport()
|
||||||
{
|
{
|
||||||
|
var records = _experimentDataService.Records.ToList();
|
||||||
|
if (!records.Any(IsValidExperimentRecord))
|
||||||
|
{
|
||||||
|
StatusText = "请先点击“测试开始”,产生有效实验数据后再导出报表";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var input = BuildInput();
|
var input = BuildInput();
|
||||||
var defaultName = BuildDefaultFileName(input);
|
var defaultName = BuildDefaultFileName(input);
|
||||||
var dialog = new SaveFileDialog
|
var dialog = new SaveFileDialog
|
||||||
@@ -171,7 +178,7 @@ public sealed class ReportPageViewModel : PageViewModel
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_reportExportService.Export(dialog.FileName, input, _experimentDataService.Records.ToList());
|
_reportExportService.Export(dialog.FileName, input, records);
|
||||||
StatusText = $"已导出:{dialog.FileName}";
|
StatusText = $"已导出:{dialog.FileName}";
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -262,4 +269,11 @@ public sealed class ReportPageViewModel : PageViewModel
|
|||||||
{
|
{
|
||||||
return double.IsFinite(value) ? $"{value:0.00}" : string.Empty;
|
return double.IsFinite(value) ? $"{value:0.00}" : string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool IsValidExperimentRecord(RealtimeDataRecord record)
|
||||||
|
{
|
||||||
|
return record.TestSeconds >= 0
|
||||||
|
&& double.IsFinite(record.TotalHeatRelease)
|
||||||
|
&& double.IsFinite(record.TotalSmoke);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
ConeCalorimeter/纯POE-EVA-样12603271445.xls
Normal file
BIN
ConeCalorimeter/纯POE-EVA-样12603271445.xls
Normal file
Binary file not shown.
Reference in New Issue
Block a user