This commit is contained in:
GukSang.Jin
2026-05-27 17:59:56 +08:00
parent cdb2190728
commit 0139c4475b
8 changed files with 80 additions and 43 deletions

View File

@@ -12,6 +12,7 @@ public sealed record RealtimeDataRecord(
double Oxygen,
double CarbonDioxide,
double CarbonMonoxide,
double Absorbance,
double HeatReleaseRate,
double PeakHeatReleaseRate,
double CFactor,
@@ -43,6 +44,7 @@ public sealed record RealtimeDataRecord(
snapshot.Oxygen,
snapshot.CarbonDioxide,
snapshot.CarbonMonoxide,
snapshot.Absorbance,
snapshot.HeatReleaseRate,
snapshot.PeakHeatReleaseRate,
snapshot.CFactor,

View File

@@ -11,6 +11,7 @@ public sealed record RealtimeSnapshot(
double Oxygen,
double CarbonDioxide,
double CarbonMonoxide,
double Absorbance,
double HeatReleaseRate,
double PeakHeatReleaseRate,
double CFactor,

View File

@@ -16,6 +16,9 @@ public sealed class ModbusRealtimeDataService : IRealtimeDataService
private const ushort ConeTemperatureRegister = 26;
private const ushort OrificeTemperatureRegister = 30;
private const ushort SampleTemperatureRegister = 36;
private const ushort AbsorbanceRegister = 120;
private const double AbsorbanceStableTolerance = 0.25;
private const int AbsorbanceStableReadCount = 2;
private const ushort CFactorRegister = 312;
private const ushort HeatReleaseRateRegister = 354;
private const ushort PeakHeatReleaseRateRegister = 376;
@@ -40,6 +43,9 @@ public sealed class ModbusRealtimeDataService : IRealtimeDataService
private readonly HashSet<ushort> _loggedInvalidFloatDiagnostics = [];
private readonly Dictionary<int, double> _stableRealtimeValues = [];
private readonly Dictionary<int, int> _pendingZeroReadCounts = [];
private double? _stableAbsorbance;
private double? _pendingAbsorbance;
private int _pendingAbsorbanceReadCount;
private ModbusFloatByteOrder? _preferredFloatByteOrder;
public ModbusRealtimeDataService(
@@ -69,6 +75,7 @@ public sealed class ModbusRealtimeDataService : IRealtimeDataService
Oxygen: ReadRangedFloatOrEmpty("O2", OxygenRegister, 0, 30),
CarbonDioxide: ReadRangedFloatOrEmpty("CO2", CarbonDioxideRegister, 0, 20),
CarbonMonoxide: ReadRangedFloatOrEmpty("CO", CarbonMonoxideRegister, 0, 10),
Absorbance: ReadAbsorbanceOrEmpty(),
HeatReleaseRate: ReadRangedFloatOrEmpty("HeatReleaseRate", HeatReleaseRateRegister, 0, 5000),
PeakHeatReleaseRate: ReadRangedFloatOrEmpty("PeakHeatReleaseRate", PeakHeatReleaseRateRegister, 0, 5000),
CFactor: ReadRangedFloatOrEmpty("CFactor", CFactorRegister, 0, 1000),
@@ -102,6 +109,49 @@ public sealed class ModbusRealtimeDataService : IRealtimeDataService
return StabilizeRealtimeValue(registerAddress, value);
}
private double ReadAbsorbanceOrEmpty()
{
var value = ReadRangedFloatOrEmpty("Absorbance", AbsorbanceRegister, 0, 100);
return double.IsFinite(value) && TryUpdateStableAbsorbance(value, out var stableValue)
? stableValue
: double.NaN;
}
private bool TryUpdateStableAbsorbance(double value, out double stableValue)
{
stableValue = _stableAbsorbance ?? value;
if (_stableAbsorbance.HasValue
&& Math.Abs(value - _stableAbsorbance.Value) <= AbsorbanceStableTolerance)
{
_pendingAbsorbance = null;
_pendingAbsorbanceReadCount = 0;
stableValue = _stableAbsorbance.Value;
return true;
}
if (!_pendingAbsorbance.HasValue
|| Math.Abs(value - _pendingAbsorbance.Value) > AbsorbanceStableTolerance)
{
_pendingAbsorbance = value;
_pendingAbsorbanceReadCount = 1;
return false;
}
_pendingAbsorbanceReadCount++;
if (_pendingAbsorbanceReadCount < AbsorbanceStableReadCount)
{
return false;
}
_stableAbsorbance = value;
_pendingAbsorbance = null;
_pendingAbsorbanceReadCount = 0;
stableValue = value;
return true;
}
private bool TrySelectRealtimeFloat(
ModbusFloatReadResult result,
double minimum,

View File

@@ -13,19 +13,17 @@ public sealed class NpoiRealtimeDataExportService : IRealtimeDataExportService
"O2 (%)",
"CO2 (%)",
"CO (%)",
"吸光率%",
"孔板压差 (Pa)",
"孔板温度 (K)",
"HRR",
"热释放速率180",
"热释放速率300",
"THR (MJ/m2)",
"SPR",
"TSR (m2)",
"MLR (g/s)",
"热释放KW/m2",
"EHC",
"损失质量",
"试样温度(℃)"
"损失质量"
];
public void Export(string outputPath, IReadOnlyList<RealtimeDataRecord> records)
@@ -60,19 +58,17 @@ public sealed class NpoiRealtimeDataExportService : IRealtimeDataExportService
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.Qa180);
SetNumeric(row, 8, record.Qa300);
SetNumeric(row, 9, record.TotalHeatRelease);
SetNumeric(row, 10, record.SmokeProduction);
SetNumeric(row, 11, record.TotalSmoke);
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);
SetNumeric(row, 4, record.Absorbance);
SetNumeric(row, 5, record.OrificePressure);
SetNumeric(row, 6, record.OrificeTemperature);
SetNumeric(row, 7, record.HeatReleaseRate);
SetNumeric(row, 8, record.TotalHeatRelease);
SetNumeric(row, 9, record.SmokeProduction);
SetNumeric(row, 10, record.TotalSmoke);
SetNumeric(row, 11, record.MassLossRate);
SetNumeric(row, 12, record.HeatReleaseRateKw);
SetNumeric(row, 13, record.EffectiveHeatOfCombustion);
SetNumeric(row, 14, record.MassLoss);
}
for (var i = 0; i < Headers.Length; i++)

View File

@@ -22,13 +22,13 @@ public sealed class NpoiReportExportService : IReportExportService
"O2 (%)",
"CO2 (%)",
"CO (%)",
"吸光率%",
"孔板压差 (Pa)",
"孔板温度 (K)",
"MLR (g/s)",
"热释放KW/m2",
"EHC",
"损失质量",
"试样温度(℃)",
"Timestamp",
"点火计时(s)",
"火焰监测",
@@ -37,8 +37,6 @@ public sealed class NpoiReportExportService : IReportExportService
"当前质量",
"初始质量",
"最大热释放",
"热释放速率180",
"热释放速率300",
"C-系数"
];
@@ -176,13 +174,13 @@ public sealed class NpoiReportExportService : IReportExportService
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);
SetNumeric(row, 10, record.Absorbance);
SetNumeric(row, 11, record.OrificePressure);
SetNumeric(row, 12, record.OrificeTemperature);
SetNumeric(row, 13, record.MassLossRate);
SetNumeric(row, 14, record.HeatReleaseRateKw);
SetNumeric(row, 15, record.EffectiveHeatOfCombustion);
SetNumeric(row, 16, record.MassLoss);
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);
@@ -191,9 +189,7 @@ public sealed class NpoiReportExportService : IReportExportService
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);
SetNumeric(row, 25, record.CFactor);
}
for (var i = 0; i < DataHeaders.Length; i++)

View File

@@ -20,7 +20,7 @@ public sealed record SerialScaleOptions(
public const string RegisterEnvironmentVariable = "CONE_SCALE_REGISTER";
public static SerialScaleOptions Default { get; } = new(
"COM1",
"COM3",
9600,
Parity.None,
8,

View File

@@ -17,9 +17,8 @@ public sealed class RealtimeDataRowViewModel
CarbonMonoxideText = Format(record.CarbonMonoxide);
OrificePressureText = Format(record.OrificePressure);
OrificeTemperatureText = Format(record.OrificeTemperature);
AbsorbanceText = Format(record.Absorbance);
Hrr50Text = Format(record.HeatReleaseRate);
Qa180Text = Format(record.Qa180);
Qa300Text = Format(record.Qa300);
ThrText = Format(record.TotalHeatRelease);
Spr50Text = Format(record.SmokeProduction);
TsrText = Format(record.TotalSmoke);
@@ -27,7 +26,6 @@ public sealed class RealtimeDataRowViewModel
HeatReleaseText = Format(record.HeatReleaseRateKw);
EhcText = Format(record.EffectiveHeatOfCombustion);
MassLossText = Format(record.MassLoss);
SampleTemperatureText = Format(record.SampleTemperature, "0.0");
}
public string TimeText { get; init; } = string.Empty;
@@ -42,12 +40,10 @@ public sealed class RealtimeDataRowViewModel
public string OrificeTemperatureText { get; init; } = string.Empty;
public string AbsorbanceText { get; init; } = string.Empty;
public string Hrr50Text { get; init; } = string.Empty;
public string Qa180Text { get; init; } = string.Empty;
public string Qa300Text { get; init; } = string.Empty;
public string ThrText { get; init; } = string.Empty;
public string Spr50Text { get; init; } = string.Empty;
@@ -62,8 +58,6 @@ public sealed class RealtimeDataRowViewModel
public string MassLossText { get; init; } = string.Empty;
public string SampleTemperatureText { get; init; } = string.Empty;
private static string Format(double value, string format = "0.00")
{
return double.IsFinite(value) ? value.ToString(format, CultureInfo.InvariantCulture) : string.Empty;

View File

@@ -138,11 +138,10 @@
<DataGridTextColumn Header="O2 (%)" Binding="{Binding OxygenText}" Width="82" ElementStyle="{StaticResource RealtimeTextElementStyle}" />
<DataGridTextColumn Header="CO2 (%)" Binding="{Binding CarbonDioxideText}" Width="88" ElementStyle="{StaticResource RealtimeTextElementStyle}" />
<DataGridTextColumn Header="CO (%)" Binding="{Binding CarbonMonoxideText}" Width="82" ElementStyle="{StaticResource RealtimeTextElementStyle}" />
<DataGridTextColumn Header="吸光率%" Binding="{Binding AbsorbanceText}" Width="88" ElementStyle="{StaticResource RealtimeTextElementStyle}" />
<DataGridTextColumn Header="孔板压差 (Pa)" Binding="{Binding OrificePressureText}" Width="116" ElementStyle="{StaticResource RealtimeTextElementStyle}" />
<DataGridTextColumn Header="孔板温度 (K)" Binding="{Binding OrificeTemperatureText}" Width="116" ElementStyle="{StaticResource RealtimeTextElementStyle}" />
<DataGridTextColumn Header="HRR" Binding="{Binding Hrr50Text}" Width="78" ElementStyle="{StaticResource RealtimeTextElementStyle}" />
<DataGridTextColumn Header="热释放速率180" Binding="{Binding Qa180Text}" Width="118" ElementStyle="{StaticResource RealtimeTextElementStyle}" />
<DataGridTextColumn Header="热释放速率300" Binding="{Binding Qa300Text}" Width="118" ElementStyle="{StaticResource RealtimeTextElementStyle}" />
<DataGridTextColumn Header="THR (MJ/m2)" Binding="{Binding ThrText}" Width="108" ElementStyle="{StaticResource RealtimeTextElementStyle}" />
<DataGridTextColumn Header="SPR" Binding="{Binding Spr50Text}" Width="78" ElementStyle="{StaticResource RealtimeTextElementStyle}" />
<DataGridTextColumn Header="TSR (m2)" Binding="{Binding TsrText}" Width="88" ElementStyle="{StaticResource RealtimeTextElementStyle}" />
@@ -150,7 +149,6 @@
<DataGridTextColumn Header="热释放KW/m2" Binding="{Binding HeatReleaseText}" Width="122" ElementStyle="{StaticResource RealtimeTextElementStyle}" />
<DataGridTextColumn Header="EHC" Binding="{Binding EhcText}" Width="70" ElementStyle="{StaticResource RealtimeTextElementStyle}" />
<DataGridTextColumn Header="损失质量" Binding="{Binding MassLossText}" Width="84" ElementStyle="{StaticResource RealtimeTextElementStyle}" />
<DataGridTextColumn Header="试样温度(℃)" Binding="{Binding SampleTemperatureText}" Width="124" ElementStyle="{StaticResource RealtimeTextElementStyle}" />
</DataGrid.Columns>
</DataGrid>
</Grid>