This commit is contained in:
GukSang.Jin
2026-05-06 16:21:27 +08:00
parent f28e213e22
commit 126bba29c0
2 changed files with 116 additions and 36 deletions

View File

@@ -19,6 +19,7 @@ public sealed class ModbusRealtimeDataService : IRealtimeDataService
private const ushort Qa180Register = 366;
private const ushort Qa300Register = 370;
private const ushort SmokeProductionRegister = 392;
private const ushort IrradianceRegister = 410;
private const ushort IgnitionSecondsRegister = 1014;
private const ushort TestSecondsRegister = 1015;
private const ushort FlameDetectedCoil = 3;
@@ -38,7 +39,7 @@ public sealed class ModbusRealtimeDataService : IRealtimeDataService
OrificeTemperature: ReadFloatOrEmpty(OrificeTemperatureRegister),
ConeTemperature: ReadFloatOrEmpty(ConeTemperatureRegister),
SampleTemperature: ReadFloatOrEmpty(SampleTemperatureRegister),
Irradiance: double.NaN,
Irradiance: ReadFloatOrEmpty(IrradianceRegister),
FlameDetected: ReadCoilOrFalse(FlameDetectedCoil),
Oxygen: ReadFloatOrEmpty(OxygenRegister),
CarbonDioxide: ReadFloatOrEmpty(CarbonDioxideRegister),

View File

@@ -12,16 +12,18 @@ public sealed class NpoiReportExportService : IReportExportService
private static readonly string[] DataHeaders =
[
"Time(s)",
"时间(s)",
"热释放(30)(kW/m²)",
"产烟率(30)(m²/s)",
"总热释放 (MJ/m²)",
"总产烟量(m²)",
"火焰增长指数(W/㎡·s)",
"烟气增长指数A(m²/s²)",
"O2 (%)",
"CO2 (%)",
"CO (%)",
"孔板压差 (Pa)",
"孔板温度 (℃)",
"HRR",
"THR (MJ/m2)",
"SPR",
"TSR (m2)",
"MLR (g/s)",
"热释放KW/m2",
"EHC",
@@ -122,10 +124,17 @@ public sealed class NpoiReportExportService : IReportExportService
SetValueBesideLabel(sheet, "基线CO2氧含量", input.BaselineCO2Oxygen);
SetRequiredValueBesideLabel(sheet, "总热释放", summary.TotalHeatRelease);
SetRequiredValueBesideLabel(sheet, "总产烟量", summary.TotalSmoke);
SetRequiredValueBesideLabel(sheet, "质量损失", summary.MassLoss);
SetRequiredValueBesideLabel(sheet, "热释放(30)最大", summary.PeakHeatReleaseRate);
SetRequiredValueBesideLabel(sheet, "产烟率(30)最大", summary.PeakSmokeProduction);
SetValueBesideLabel(sheet, "总产烟量", summary.TotalSmoke);
SetValueBesideLabel(sheet, "质量损失", summary.MassLoss);
if (!SetValueBesideLabel(sheet, "热释放(30)最大", summary.PeakHeatReleaseRate))
{
SetPeakValueInResultMatrix(sheet, "热释放率", summary.PeakHeatReleaseRate);
}
if (!SetValueBesideLabel(sheet, "产烟率(30)最大", summary.PeakSmokeProduction))
{
SetPeakValueInResultMatrix(sheet, "总产烟率", summary.PeakSmokeProduction);
}
}
private static void FillDataSheet(ISheet sheet, IReadOnlyList<RealtimeDataRecord> records)
@@ -138,37 +147,42 @@ public sealed class NpoiReportExportService : IReportExportService
headerRow.CreateCell(i).SetCellValue(DataHeaders[i]);
}
var figra = CalculatePeakGrowthIndex(records, record => record.HeatReleaseRate, 1000);
var smogra = CalculatePeakGrowthIndex(records, record => record.SmokeProduction, 1);
for (var i = 0; i < records.Count; i++)
{
var row = sheet.CreateRow(i + 1);
var record = records[i];
// Keep columns A-O fixed; the Excel template charts reference these positions.
// Keep columns A-G fixed; the Excel template charts reference these positions.
SetNumeric(row, 0, record.TestSeconds >= 0 ? record.TestSeconds : double.NaN);
SetNumeric(row, 1, record.Oxygen);
SetNumeric(row, 2, record.CarbonDioxide);
SetNumeric(row, 3, record.CarbonMonoxide);
SetNumeric(row, 4, record.OrificePressure);
SetNumeric(row, 5, record.OrificeTemperature);
SetNumeric(row, 6, record.HeatReleaseRate);
SetNumeric(row, 7, record.TotalHeatRelease);
SetNumeric(row, 8, record.SmokeProduction);
SetNumeric(row, 9, record.TotalSmoke);
SetNumeric(row, 10, record.MassLossRate);
SetNumeric(row, 11, record.HeatReleaseRateKw);
SetNumeric(row, 12, record.EffectiveHeatOfCombustion);
SetNumeric(row, 13, record.MassLoss);
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);
SetNumeric(row, 1, record.HeatReleaseRate);
SetNumeric(row, 2, record.SmokeProduction);
SetNumeric(row, 3, record.TotalHeatRelease);
SetNumeric(row, 4, record.TotalSmoke);
SetNumeric(row, 5, figra);
SetNumeric(row, 6, smogra);
SetNumeric(row, 7, record.Oxygen);
SetNumeric(row, 8, record.CarbonDioxide);
SetNumeric(row, 9, record.CarbonMonoxide);
SetNumeric(row, 10, record.OrificePressure);
SetNumeric(row, 11, record.OrificeTemperature);
SetNumeric(row, 12, record.MassLossRate);
SetNumeric(row, 13, record.HeatReleaseRateKw);
SetNumeric(row, 14, record.EffectiveHeatOfCombustion);
SetNumeric(row, 15, record.MassLoss);
SetNumeric(row, 16, record.SampleTemperature);
row.CreateCell(17).SetCellValue(record.Timestamp.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.CurrentCulture));
SetNumeric(row, 18, record.IgnitionSeconds >= 0 ? record.IgnitionSeconds : double.NaN);
SetNumeric(row, 19, record.FlameDetected ? 1 : 0);
SetNumeric(row, 20, record.OrificeFlow);
SetNumeric(row, 21, record.ConeTemperature);
SetNumeric(row, 22, record.CurrentMass);
SetNumeric(row, 23, record.InitialMass);
SetNumeric(row, 24, record.PeakHeatReleaseRate);
SetNumeric(row, 25, record.Qa180);
SetNumeric(row, 26, record.Qa300);
SetNumeric(row, 27, record.CFactor);
}
for (var i = 0; i < DataHeaders.Length; i++)
@@ -177,6 +191,34 @@ public sealed class NpoiReportExportService : IReportExportService
}
}
private static double CalculatePeakGrowthIndex(
IReadOnlyList<RealtimeDataRecord> records,
Func<RealtimeDataRecord, double> selector,
double multiplier)
{
double? peak = null;
var peakSeconds = 0;
foreach (var record in records)
{
var value = selector(record);
if (record.TestSeconds <= 0 || !double.IsFinite(value) || value < 0)
{
continue;
}
if (!peak.HasValue || value > peak.Value)
{
peak = value;
peakSeconds = record.TestSeconds;
}
}
return peak.HasValue && peakSeconds > 0
? peak.Value * multiplier / peakSeconds
: double.NaN;
}
private static bool SetValueBesideLabel(ISheet sheet, string label, string value)
{
if (string.IsNullOrWhiteSpace(value))
@@ -228,6 +270,43 @@ public sealed class NpoiReportExportService : IReportExportService
}
}
private static bool SetPeakValueInResultMatrix(ISheet sheet, string rowLabel, string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return false;
}
for (var rowIndex = sheet.FirstRowNum; rowIndex <= sheet.LastRowNum; rowIndex++)
{
var row = sheet.GetRow(rowIndex);
if (row is null)
{
continue;
}
for (var columnIndex = row.FirstCellNum; columnIndex < row.LastCellNum; columnIndex++)
{
if (columnIndex < 0)
{
continue;
}
var cell = row.GetCell(columnIndex);
if (!CellText(cell).Contains(rowLabel, StringComparison.CurrentCultureIgnoreCase))
{
continue;
}
var target = row.GetCell(7) ?? row.CreateCell(7);
target.SetCellValue(value);
return true;
}
}
return false;
}
private static int FindTargetColumn(ISheet sheet, int rowIndex, int columnIndex)
{
for (var i = 0; i < sheet.NumMergedRegions; i++)