diff --git a/ConeCalorimeter/ViewModels/CValueCalibrationViewModel.cs b/ConeCalorimeter/ViewModels/CValueCalibrationViewModel.cs index 80fdf00..caaf2e8 100644 --- a/ConeCalorimeter/ViewModels/CValueCalibrationViewModel.cs +++ b/ConeCalorimeter/ViewModels/CValueCalibrationViewModel.cs @@ -23,6 +23,7 @@ public sealed class CValueCalibrationViewModel : PageViewModel private const double PressureDifferenceMaximum = 5000; private const double CValueMinimum = 0; private const double CValueMaximum = 1000; + private const double BaselineOxygenDisplayScale = 100; private const ushort CirculatingWaterCoil = 49; private const ushort SamplingPumpCoil = 50; private const ushort IgniterCoil = 53; @@ -32,6 +33,7 @@ public sealed class CValueCalibrationViewModel : PageViewModel private const ushort BaselineEndCoil = 62; private const ushort CalibrationStartCoil = 70; private const ushort CalibrationEndCoil = 72; + private static readonly TimeSpan PairedActionPulseDuration = TimeSpan.FromMilliseconds(300); private const string CirculatingWaterAction = "循环水"; private const string SamplingPumpAction = "取样泵"; private const string IgniterAction = "点火器"; @@ -86,6 +88,8 @@ public sealed class CValueCalibrationViewModel : PageViewModel CreateAction(CalibrationStartAction, CalibrationStartAction, "标定结束"), CreateAction(BaselineCollectionAction, BaselineCollectionAction, "基线结束") ]; + _actionsByLabel[CalibrationStartAction].UpdateStatus(false); + _actionsByLabel[BaselineCollectionAction].UpdateStatus(false); RefreshDeviceValues(); _refreshTimer = new DispatcherTimer @@ -168,8 +172,9 @@ public sealed class CValueCalibrationViewModel : PageViewModel "BaselineOxygen", BaselineOxygenRegister, OxygenMinimum, - OxygenMaximum, - "0.00"); + OxygenMaximum / BaselineOxygenDisplayScale, + "0.00", + BaselineOxygenDisplayScale); TemperatureText = ReadRangedFloatText( "Temperature", TemperatureRegister, @@ -194,7 +199,7 @@ public sealed class CValueCalibrationViewModel : PageViewModel CValueMinimum, CValueMaximum, "0.00"); - RefreshActionStates(); + RefreshHoldActionStates(); } private string ReadRangedFloatText( @@ -202,7 +207,8 @@ public sealed class CValueCalibrationViewModel : PageViewModel ushort registerAddress, double minimum, double maximum, - string format) + string format, + double displayScale = 1) { if (!_tcpDeviceConnectionService.TryReadFloatValues(registerAddress, out var result)) { @@ -216,7 +222,7 @@ public sealed class CValueCalibrationViewModel : PageViewModel } LogFloatDiagnostic(label, registerAddress, result, byteOrder, matchCount); - return NormalizeDisplayValue(value).ToString(format, CultureInfo.InvariantCulture); + return NormalizeDisplayValue(value * displayScale).ToString(format, CultureInfo.InvariantCulture); } private void LogFloatDiagnostic( @@ -262,15 +268,13 @@ public sealed class CValueCalibrationViewModel : PageViewModel return action; } - private void RefreshActionStates() + private void RefreshHoldActionStates() { UpdateActionStatus(CirculatingWaterAction, CirculatingWaterCoil); UpdateActionStatus(MethaneValveAction, MethaneValveCoil); UpdateActionStatus(FanAction, FanCoil); UpdateActionStatus(IgniterAction, IgniterCoil); UpdateActionStatus(SamplingPumpAction, SamplingPumpCoil); - UpdateActionStatus(BaselineCollectionAction, BaselineCollectionCoil); - UpdateActionStatus(CalibrationStartAction, CalibrationStartCoil); } private bool UpdateActionStatus(string action, ushort coilAddress) @@ -327,27 +331,23 @@ public sealed class CValueCalibrationViewModel : PageViewModel return; } - if (!_tcpDeviceConnectionService.TryReadCoil(startCoilAddress, out var isActive)) + if (!_actionsByLabel.TryGetValue(action, out var actionViewModel)) { - LastAction = $"{action}状态读取失败"; - Debug.WriteLine($"C value calibration action '{action}' state read failed."); return; } + var isActive = actionViewModel.IsActive; var coilAddress = isActive ? endCoilAddress : startCoilAddress; var actionText = isActive ? endActionText : action; - if (_tcpDeviceConnectionService.TryWriteCoil(coilAddress, true)) + if (TryStartCoilPulse(coilAddress, actionText)) { var completedBaselineCollection = action == BaselineCollectionAction && isActive; var completedCalibration = action == CalibrationStartAction && isActive; LastAction = completedBaselineCollection ? "基线采集完成" : completedCalibration ? "标定完成" : actionText; - if (_actionsByLabel.TryGetValue(action, out var actionViewModel)) - { - actionViewModel.UpdateStatus(!isActive); - } + actionViewModel.UpdateStatus(!isActive); if (completedBaselineCollection) { @@ -366,6 +366,26 @@ public sealed class CValueCalibrationViewModel : PageViewModel Debug.WriteLine($"C value calibration action '{actionText}' write failed."); } + private bool TryStartCoilPulse(ushort coilAddress, string actionText) + { + if (!_tcpDeviceConnectionService.TryWriteCoil(coilAddress, true)) + { + return false; + } + + _ = ReleaseCoilAfterPulseAsync(coilAddress, actionText); + return true; + } + + private async Task ReleaseCoilAfterPulseAsync(ushort coilAddress, string actionText) + { + await Task.Delay(PairedActionPulseDuration); + if (!_tcpDeviceConnectionService.TryWriteCoil(coilAddress, false)) + { + Debug.WriteLine($"C value calibration action '{actionText}' pulse release failed."); + } + } + private static bool TryGetHoldActionCoil(string action, out ushort coilAddress) { switch (action) diff --git a/ConeCalorimeter/ViewModels/ConeRadiationSettingsViewModel.cs b/ConeCalorimeter/ViewModels/ConeRadiationSettingsViewModel.cs index 5d88e0d..b7186f5 100644 --- a/ConeCalorimeter/ViewModels/ConeRadiationSettingsViewModel.cs +++ b/ConeCalorimeter/ViewModels/ConeRadiationSettingsViewModel.cs @@ -18,6 +18,8 @@ public sealed class ConeRadiationSettingsViewModel : PageViewModel private const double CurrentTemperatureMaximumDropPerRefresh = 80; private const double CurrentHeatFluxMinimum = 0; private const double CurrentHeatFluxMaximum = 1000; + private const double CurrentHeatFluxStableTolerance = 5; + private const int CurrentHeatFluxStableReadCount = 3; private const double HeatTransferInputMinimum = 0; private const double HeatTransferReadMinimum = 0.01; private const double HeatTransferMaximum = 20000; @@ -44,6 +46,9 @@ public sealed class ConeRadiationSettingsViewModel : PageViewModel private bool _heatingActive; private bool _isEditingConeParameters; private double? _lastStableCurrentTemperature; + private double? _lastStableCurrentHeatFlux; + private double? _pendingCurrentHeatFlux; + private int _pendingCurrentHeatFluxReadCount; private DateTime _parameterRefreshBlockedUntil = DateTime.MinValue; public ConeRadiationSettingsViewModel( @@ -191,10 +196,17 @@ public sealed class ConeRadiationSettingsViewModel : PageViewModel CurrentHeatFluxMinimum, CurrentHeatFluxMaximum, "0.00", - out var currentHeatFluxText, - out _)) + out _, + out var currentHeatFlux)) { - CurrentHeatFluxText = currentHeatFluxText; + if (TryUpdateStableCurrentHeatFlux(currentHeatFlux, out var stableCurrentHeatFlux)) + { + CurrentHeatFluxText = stableCurrentHeatFlux.ToString("0.00", CultureInfo.InvariantCulture); + } + } + else if (!_lastStableCurrentHeatFlux.HasValue) + { + CurrentHeatFluxText = string.Empty; } if (CanRefreshConeParameters()) @@ -271,6 +283,46 @@ public sealed class ConeRadiationSettingsViewModel : PageViewModel || value >= _lastStableCurrentTemperature.Value - CurrentTemperatureMaximumDropPerRefresh; } + private bool TryUpdateStableCurrentHeatFlux(double value, out double stableValue) + { + value = NormalizeDisplayValue(value); + stableValue = _lastStableCurrentHeatFlux ?? value; + + if (_lastStableCurrentHeatFlux.HasValue + && Math.Abs(value - _lastStableCurrentHeatFlux.Value) <= CurrentHeatFluxStableTolerance) + { + AcceptStableCurrentHeatFlux(value); + stableValue = value; + return true; + } + + if (!_pendingCurrentHeatFlux.HasValue + || Math.Abs(value - _pendingCurrentHeatFlux.Value) > CurrentHeatFluxStableTolerance) + { + _pendingCurrentHeatFlux = value; + _pendingCurrentHeatFluxReadCount = 1; + return false; + } + + _pendingCurrentHeatFluxReadCount++; + + if (_pendingCurrentHeatFluxReadCount < CurrentHeatFluxStableReadCount) + { + return false; + } + + AcceptStableCurrentHeatFlux(value); + stableValue = value; + return true; + } + + private void AcceptStableCurrentHeatFlux(double value) + { + _lastStableCurrentHeatFlux = value; + _pendingCurrentHeatFlux = null; + _pendingCurrentHeatFluxReadCount = 0; + } + private string ReadScaledInt16Text(ushort registerAddress, double scale, string format) { return _tcpDeviceConnectionService.TryReadInt16(registerAddress, out var rawValue)