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

View File

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