This commit is contained in:
GukSang.Jin
2026-05-26 20:12:08 +08:00
parent 0c4393bebc
commit 5477fdf210

View File

@@ -18,8 +18,12 @@ public sealed class ConeRadiationSettingsViewModel : PageViewModel
private const double CurrentTemperatureMaximumDropPerRefresh = 80; private const double CurrentTemperatureMaximumDropPerRefresh = 80;
private const double CurrentHeatFluxMinimum = 0; private const double CurrentHeatFluxMinimum = 0;
private const double CurrentHeatFluxMaximum = 1000; private const double CurrentHeatFluxMaximum = 1000;
private const double CurrentHeatFluxStableTolerance = 5; private const double CurrentHeatFluxZeroTolerance = 0.005;
private const double CurrentHeatFluxStableAbsoluteTolerance = 0.25;
private const double CurrentHeatFluxStableRelativeTolerance = 0.1;
private const double CurrentHeatFluxStableMaximumTolerance = 5;
private const int CurrentHeatFluxStableReadCount = 3; private const int CurrentHeatFluxStableReadCount = 3;
private const int CurrentHeatFluxZeroStableReadCount = 10;
private const double HeatTransferInputMinimum = 0; private const double HeatTransferInputMinimum = 0;
private const double HeatTransferReadMinimum = 0.01; private const double HeatTransferReadMinimum = 0.01;
private const double HeatTransferMaximum = 20000; private const double HeatTransferMaximum = 20000;
@@ -36,6 +40,14 @@ public sealed class ConeRadiationSettingsViewModel : PageViewModel
private readonly DispatcherTimer _refreshTimer; private readonly DispatcherTimer _refreshTimer;
private readonly HashSet<ushort> _loggedFloatDiagnostics = []; private readonly HashSet<ushort> _loggedFloatDiagnostics = [];
private readonly HashSet<ushort> _loggedInvalidFloatDiagnostics = []; private readonly HashSet<ushort> _loggedInvalidFloatDiagnostics = [];
private static readonly ModbusFloatByteOrder[] CurrentHeatFluxByteOrders =
[
ModbusFloatByteOrder.Abcd,
ModbusFloatByteOrder.Cdab,
ModbusFloatByteOrder.Badc,
ModbusFloatByteOrder.Dcba
];
private string _currentTemperatureText = ""; private string _currentTemperatureText = "";
private string _currentHeatFluxText = ""; private string _currentHeatFluxText = "";
private string _targetTemperatureText = ""; private string _targetTemperatureText = "";
@@ -49,6 +61,8 @@ public sealed class ConeRadiationSettingsViewModel : PageViewModel
private double? _lastStableCurrentHeatFlux; private double? _lastStableCurrentHeatFlux;
private double? _pendingCurrentHeatFlux; private double? _pendingCurrentHeatFlux;
private int _pendingCurrentHeatFluxReadCount; private int _pendingCurrentHeatFluxReadCount;
private int _pendingCurrentHeatFluxZeroReadCount;
private ModbusFloatByteOrder? _currentHeatFluxByteOrder;
private DateTime _parameterRefreshBlockedUntil = DateTime.MinValue; private DateTime _parameterRefreshBlockedUntil = DateTime.MinValue;
public ConeRadiationSettingsViewModel( public ConeRadiationSettingsViewModel(
@@ -190,14 +204,7 @@ public sealed class ConeRadiationSettingsViewModel : PageViewModel
private void RefreshDeviceValues() private void RefreshDeviceValues()
{ {
if (TryReadRangedFloatText( if (TryReadCurrentHeatFlux(out var currentHeatFlux))
"ConeCurrentHeatFlux",
CurrentHeatFluxRegister,
CurrentHeatFluxMinimum,
CurrentHeatFluxMaximum,
"0.00",
out _,
out var currentHeatFlux))
{ {
if (TryUpdateStableCurrentHeatFlux(currentHeatFlux, out var stableCurrentHeatFlux)) if (TryUpdateStableCurrentHeatFlux(currentHeatFlux, out var stableCurrentHeatFlux))
{ {
@@ -243,6 +250,46 @@ public sealed class ConeRadiationSettingsViewModel : PageViewModel
} }
} }
private bool TryReadCurrentHeatFlux(out double value)
{
value = double.NaN;
if (!_tcpDeviceConnectionService.TryReadFloatValues(CurrentHeatFluxRegister, out var result))
{
return false;
}
var preferredByteOrder = _currentHeatFluxByteOrder;
if (!ModbusFloatSelector.TrySelectRangedFloat(
result,
CurrentHeatFluxMinimum,
CurrentHeatFluxMaximum,
out var byteOrder,
out var selectedValue,
out var matchCount,
preferredByteOrder))
{
if (!TrySelectSingleNonZeroCurrentHeatFluxCandidate(result, out byteOrder, out selectedValue))
{
LogFloatDiagnostic("ConeCurrentHeatFlux", CurrentHeatFluxRegister, result, null, matchCount);
return false;
}
}
if (!preferredByteOrder.HasValue
&& IsZeroLikeCurrentHeatFlux(selectedValue)
&& TrySelectSingleNonZeroCurrentHeatFluxCandidate(result, out var nonZeroByteOrder, out var nonZeroValue))
{
byteOrder = nonZeroByteOrder;
selectedValue = nonZeroValue;
}
LogFloatDiagnostic("ConeCurrentHeatFlux", CurrentHeatFluxRegister, result, byteOrder, matchCount);
RememberCurrentHeatFluxByteOrder(byteOrder, selectedValue);
value = NormalizeDisplayValue(selectedValue);
return true;
}
private bool TryReadRangedFloatText( private bool TryReadRangedFloatText(
string label, string label,
ushort registerAddress, ushort registerAddress,
@@ -288,8 +335,26 @@ public sealed class ConeRadiationSettingsViewModel : PageViewModel
value = NormalizeDisplayValue(value); value = NormalizeDisplayValue(value);
stableValue = _lastStableCurrentHeatFlux ?? value; stableValue = _lastStableCurrentHeatFlux ?? value;
if (IsZeroLikeCurrentHeatFlux(value))
{
_pendingCurrentHeatFlux = null;
_pendingCurrentHeatFluxReadCount = 0;
_pendingCurrentHeatFluxZeroReadCount++;
if (_pendingCurrentHeatFluxZeroReadCount < CurrentHeatFluxZeroStableReadCount)
{
return false;
}
AcceptStableCurrentHeatFlux(0);
stableValue = 0;
return true;
}
_pendingCurrentHeatFluxZeroReadCount = 0;
if (_lastStableCurrentHeatFlux.HasValue if (_lastStableCurrentHeatFlux.HasValue
&& Math.Abs(value - _lastStableCurrentHeatFlux.Value) <= CurrentHeatFluxStableTolerance) && IsWithinCurrentHeatFluxStableTolerance(value, _lastStableCurrentHeatFlux.Value))
{ {
AcceptStableCurrentHeatFlux(value); AcceptStableCurrentHeatFlux(value);
stableValue = value; stableValue = value;
@@ -297,7 +362,7 @@ public sealed class ConeRadiationSettingsViewModel : PageViewModel
} }
if (!_pendingCurrentHeatFlux.HasValue if (!_pendingCurrentHeatFlux.HasValue
|| Math.Abs(value - _pendingCurrentHeatFlux.Value) > CurrentHeatFluxStableTolerance) || !IsWithinCurrentHeatFluxStableTolerance(value, _pendingCurrentHeatFlux.Value))
{ {
_pendingCurrentHeatFlux = value; _pendingCurrentHeatFlux = value;
_pendingCurrentHeatFluxReadCount = 1; _pendingCurrentHeatFluxReadCount = 1;
@@ -321,6 +386,62 @@ public sealed class ConeRadiationSettingsViewModel : PageViewModel
_lastStableCurrentHeatFlux = value; _lastStableCurrentHeatFlux = value;
_pendingCurrentHeatFlux = null; _pendingCurrentHeatFlux = null;
_pendingCurrentHeatFluxReadCount = 0; _pendingCurrentHeatFluxReadCount = 0;
_pendingCurrentHeatFluxZeroReadCount = 0;
}
private static bool TrySelectSingleNonZeroCurrentHeatFluxCandidate(
ModbusFloatReadResult result,
out ModbusFloatByteOrder byteOrder,
out double value)
{
var matches = CurrentHeatFluxByteOrders
.Select(candidate => new
{
ByteOrder = candidate,
Value = result.GetValue(candidate)
})
.Where(candidate => ModbusFloatSelector.IsInRange(
candidate.Value,
CurrentHeatFluxMinimum,
CurrentHeatFluxMaximum)
&& !IsZeroLikeCurrentHeatFlux(candidate.Value))
.ToArray();
if (matches.Length != 1)
{
byteOrder = default;
value = double.NaN;
return false;
}
byteOrder = matches[0].ByteOrder;
value = matches[0].Value;
return true;
}
private void RememberCurrentHeatFluxByteOrder(ModbusFloatByteOrder byteOrder, double value)
{
if (!IsZeroLikeCurrentHeatFlux(value))
{
_currentHeatFluxByteOrder = byteOrder;
}
}
private static bool IsWithinCurrentHeatFluxStableTolerance(double value, double referenceValue)
{
return Math.Abs(value - referenceValue) <= GetCurrentHeatFluxStableTolerance(referenceValue);
}
private static double GetCurrentHeatFluxStableTolerance(double referenceValue)
{
return Math.Min(
CurrentHeatFluxStableMaximumTolerance,
Math.Max(CurrentHeatFluxStableAbsoluteTolerance, Math.Abs(referenceValue) * CurrentHeatFluxStableRelativeTolerance));
}
private static bool IsZeroLikeCurrentHeatFlux(double value)
{
return double.IsFinite(value) && Math.Abs(value) < CurrentHeatFluxZeroTolerance;
} }
private string ReadScaledInt16Text(ushort registerAddress, double scale, string format) private string ReadScaledInt16Text(ushort registerAddress, double scale, string format)