diff --git a/DentistryHandpieces/MainWindow.xaml b/DentistryHandpieces/MainWindow.xaml index 291d7e2..8ceab88 100644 --- a/DentistryHandpieces/MainWindow.xaml +++ b/DentistryHandpieces/MainWindow.xaml @@ -482,7 +482,7 @@ - @@ -505,9 +505,9 @@ diff --git a/DentistryHandpieces/MainWindowViewModel.cs b/DentistryHandpieces/MainWindowViewModel.cs index 56f48a3..08c8eda 100644 --- a/DentistryHandpieces/MainWindowViewModel.cs +++ b/DentistryHandpieces/MainWindowViewModel.cs @@ -151,6 +151,7 @@ public sealed class MainWindowViewModel : ObservableObject private static readonly ushort[] RealtimeCoilAddresses = [ VentValveCoil, + AxialForceModeCoil, AxialDoneCoil, SpeedTorqueDoneCoil, SpeedTorqueResetEnabledCoil, @@ -215,6 +216,7 @@ public sealed class MainWindowViewModel : ObservableObject private bool _isSpeedTorqueCompletionArmed; private bool _hasLoggedStaleSpeedTorqueDone; private bool _isReadingRealtime; + private bool _isWritingAxialForceMode; private bool _isReadingParameterConfig; private bool _hasLoadedParameterConfigFromPlc; private bool _lastRealtimeReadFailed; @@ -298,6 +300,7 @@ public sealed class MainWindowViewModel : ObservableObject RecordNoLoadSpeedCommand = new AsyncRelayCommand(RecordNoLoadSpeedAsync); _parameterConfig = LoadParameterConfig(); + _parameterConfig = WithAxialForceSetpointMode(false); ApplyParameterConfigToInputs(); UpdateDisplacementDisplay(); UpdateSpeedTorqueDisplay(); @@ -317,6 +320,7 @@ public sealed class MainWindowViewModel : ObservableObject }; _parameterRetryTimer.Tick += ParameterRetryTimer_Tick; _parameterRetryTimer.Start(); + _ = ReadAxialForceModeFromPlcAsync(); _ = ReadParameterConfigFromPlcAsync(); Log.Information("主视图模型初始化完成,参数文件 {ParameterConfigPath}", _parameterConfigPath); } @@ -786,6 +790,7 @@ public sealed class MainWindowViewModel : ObservableObject IReadOnlyDictionary coilValues = await coilReadTask; ApplyResetCoilValues(coilValues); + ApplyAxialForceModeState(ReadCoilValue(coilValues, AxialForceModeCoil)); bool isVentValveOpen = ReadCoilValue(coilValues, VentValveCoil); VentValveButtonText = isVentValveOpen ? "关闭气阀" : "通气阀"; @@ -1172,6 +1177,42 @@ public sealed class MainWindowViewModel : ObservableObject OnPropertyChanged(nameof(IsAxialJumpForceSetpointSelected)); } + private void ApplyAxialForceModeState(bool usePullForceSetpoint) + { + if (_isWritingAxialForceMode || _parameterConfig.UseAxialPullForceSetpoint == usePullForceSetpoint) + { + return; + } + + _parameterConfig = WithAxialForceSetpointMode(usePullForceSetpoint); + UpdateAxialForceSetpointModeText(); + ApplyActiveAxialForceSetpointInput(); + UpdateParameterSummaries(); + Log.Information( + "轴向力模式已同步 PLC 实际状态,M{CoilAddress}={Value},当前模式 {Mode}", + AxialForceModeCoil, + usePullForceSetpoint ? 1 : 0, + GetActiveAxialForceSetpointName()); + } + + private async Task ReadAxialForceModeFromPlcAsync() + { + try + { + IReadOnlyDictionary values = await _plcCoilService.ReadCoilValuesAsync( + _parameterConfig.ToPlcConnectionConfig(), + [AxialForceModeCoil]); + ApplyAxialForceModeState(ReadCoilValue(values, AxialForceModeCoil)); + } + catch (Exception ex) + { + Log.Warning( + ex, + "启动时读取轴向力模式失败,暂按默认 M{CoilAddress}=0 显示轴向跳动力,实时轮询将继续同步", + AxialForceModeCoil); + } + } + private void ApplyActiveAxialForceSetpointInput() { _isApplyingParameterConfigToInputs = true; @@ -2139,27 +2180,46 @@ public sealed class MainWindowViewModel : ObservableObject private async Task SelectAxialForceSetpointModeAsync(bool usePullForceSetpoint) { - if (_parameterConfig.UseAxialPullForceSetpoint == usePullForceSetpoint) - { - UpdateAxialForceSetpointModeText(); - ApplyActiveAxialForceSetpointInput(); - return; - } - + _isWritingAxialForceMode = true; try { + PlcConnectionConfig config = _parameterConfig.ToPlcConnectionConfig(); await _plcCoilService.WriteCoilAsync( - _parameterConfig.ToPlcConnectionConfig(), + config, AxialForceModeCoil, usePullForceSetpoint); + await Task.Delay(100); - _parameterConfig = WithAxialForceSetpointMode(usePullForceSetpoint); + IReadOnlyDictionary values = await _plcCoilService.ReadCoilValuesAsync( + config, + [AxialForceModeCoil]); + bool actualUsePullForceSetpoint = ReadCoilValue(values, AxialForceModeCoil); + + _parameterConfig = WithAxialForceSetpointMode(actualUsePullForceSetpoint); SaveParameterConfig(); UpdateAxialForceSetpointModeText(); ApplyActiveAxialForceSetpointInput(); UpdateParameterSummaries(); + + if (actualUsePullForceSetpoint != usePullForceSetpoint) + { + ParameterStatusText = + $"轴向力模式切换未生效:M{AxialForceModeCoil}实际为{(actualUsePullForceSetpoint ? 1 : 0)},已按实际状态高亮。"; + Log.Error( + "轴向力模式切换读回不一致,M{CoilAddress}要求 {Expected},实际 {Actual},高亮已同步实际模式 {Mode}", + AxialForceModeCoil, + usePullForceSetpoint ? 1 : 0, + actualUsePullForceSetpoint ? 1 : 0, + GetActiveAxialForceSetpointName()); + return; + } + ParameterStatusText = $"已切换到{GetActiveAxialForceSetpointName()}。"; - Log.Information("轴向力模式切换成功,M{CoilAddress}={Value},当前模式 {Mode}", AxialForceModeCoil, usePullForceSetpoint ? 1 : 0, GetActiveAxialForceSetpointName()); + Log.Information( + "轴向力模式切换并读回确认成功,M{CoilAddress}={Value},当前模式 {Mode}", + AxialForceModeCoil, + usePullForceSetpoint ? 1 : 0, + GetActiveAxialForceSetpointName()); await AutoStopIfSetpointReachedAsync(); } catch (Exception ex) @@ -2167,6 +2227,10 @@ public sealed class MainWindowViewModel : ObservableObject ParameterStatusText = $"轴向力切换失败:{OperatorMessageFormatter.FromException(ex)}"; Log.Error(ex, "轴向力模式切换失败,M{CoilAddress}={Value}", AxialForceModeCoil, usePullForceSetpoint ? 1 : 0); } + finally + { + _isWritingAxialForceMode = false; + } } private async Task ForwardSpeedTorqueAsync()