diff --git a/DentistryHandpieces/MainWindow.xaml b/DentistryHandpieces/MainWindow.xaml index 63a02c9..5e3ec02 100644 --- a/DentistryHandpieces/MainWindow.xaml +++ b/DentistryHandpieces/MainWindow.xaml @@ -503,7 +503,7 @@ + Text="{Binding AxialForceSetpointInput, UpdateSourceTrigger=PropertyChanged, Delay=600}" /> + Text="{Binding AxialForceHoldTimeInput, UpdateSourceTrigger=PropertyChanged, Delay=600}" /> + Text="{Binding HoldTorqueInput, UpdateSourceTrigger=PropertyChanged, Delay=600}" /> + Text="{Binding TorqueHoldTimeInput, UpdateSourceTrigger=PropertyChanged, Delay=600}" /> _axialForceHoldTimeInput; + set + { + if (!SetProperty(ref _axialForceHoldTimeInput, value) || _isApplyingParameterConfigToInputs) + { + return; + } + + QueueTenthsTestPageInputWrite( + TestPageInputParameter.AxialForceHoldTime, + AxialForceHoldTimeRegister, + "轴向力保持时间设置", + value); + } + } public string SpeedTorqueDisplacementLimitInput { get; set; } = "0"; @@ -412,9 +447,33 @@ public sealed class MainWindowViewModel : ObservableObject public string TorqueProtectionInput { get; set; } = "0"; - public string HoldTorqueInput { get; set; } = "0"; + public string HoldTorqueInput + { + get => _holdTorqueInput; + set + { + if (!SetProperty(ref _holdTorqueInput, value) || _isApplyingParameterConfigToInputs) + { + return; + } - public string TorqueHoldTimeInput { get; set; } = "0"; + QueueTenthsTestPageInputWrite(TestPageInputParameter.HoldTorque, HoldTorqueRegister, "保持扭矩设置", value); + } + } + + public string TorqueHoldTimeInput + { + get => _torqueHoldTimeInput; + set + { + if (!SetProperty(ref _torqueHoldTimeInput, value) || _isApplyingParameterConfigToInputs) + { + return; + } + + QueueTenthsTestPageInputWrite(TestPageInputParameter.TorqueHoldTime, TorqueHoldTimeRegister, "扭矩保持时间设置", value); + } + } public string SpeedCoefficientInput { get; set; } = "1"; @@ -425,7 +484,15 @@ public sealed class MainWindowViewModel : ObservableObject public string NoLoadSpeedSettingInput { get => _noLoadSpeedSettingInput; - set => SetProperty(ref _noLoadSpeedSettingInput, value); + set + { + if (!SetProperty(ref _noLoadSpeedSettingInput, value) || _isApplyingParameterConfigToInputs) + { + return; + } + + QueueFloatTestPageInputWrite(TestPageInputParameter.NoLoadSpeedSetting, NoLoadSpeedSettingRegister, "空载转速设置", value); + } } public string PlcIpAddressInput { get; set; } = "192.168.1.10"; @@ -865,41 +932,6 @@ public sealed class MainWindowViewModel : ObservableObject return options; } - private TestParameterConfig WithActiveAxialForceSetpoint(double axialForceSetpoint) - { - return new TestParameterConfig - { - AxialDisplacementLimit = _parameterConfig.AxialDisplacementLimit, - AxialSpeed = _parameterConfig.AxialSpeed, - AxialManualDisplacement = _parameterConfig.AxialManualDisplacement, - AxialForceLowerLimit = _parameterConfig.AxialForceLowerLimit, - AxialForceUpperLimit = _parameterConfig.AxialForceUpperLimit, - AxialForceCoefficient = _parameterConfig.AxialForceCoefficient, - AxialForceSetpoint = _parameterConfig.UseAxialPullForceSetpoint ? axialForceSetpoint : _parameterConfig.AxialForceSetpoint, - AxialJumpForceSetpoint = _parameterConfig.UseAxialPullForceSetpoint ? _parameterConfig.AxialJumpForceSetpoint : axialForceSetpoint, - UseAxialPullForceSetpoint = _parameterConfig.UseAxialPullForceSetpoint, - AxialForceProtection = _parameterConfig.AxialForceProtection, - AxialForceHoldTime = _parameterConfig.AxialForceHoldTime, - SpeedTorqueDisplacementLimit = _parameterConfig.SpeedTorqueDisplacementLimit, - SpeedTorqueSpeed = _parameterConfig.SpeedTorqueSpeed, - SpeedTorqueManualDisplacement = _parameterConfig.SpeedTorqueManualDisplacement, - TorqueCoefficient = _parameterConfig.TorqueCoefficient, - TorqueProtection = _parameterConfig.TorqueProtection, - HoldTorque = _parameterConfig.HoldTorque, - TorqueHoldTime = _parameterConfig.TorqueHoldTime, - SpeedCoefficient = _parameterConfig.SpeedCoefficient, - SpeedStopThreshold = _parameterConfig.SpeedStopThreshold, - PressureCoefficient = _parameterConfig.PressureCoefficient, - NoLoadSpeedSetting = _parameterConfig.NoLoadSpeedSetting, - PlcIpAddress = _parameterConfig.PlcIpAddress, - PlcPort = _parameterConfig.PlcPort, - PlcUnitId = _parameterConfig.PlcUnitId, - PlcPulseMilliseconds = _parameterConfig.PlcPulseMilliseconds, - PlcTimeoutMilliseconds = _parameterConfig.PlcTimeoutMilliseconds, - FloatWordOrder = _parameterConfig.FloatWordOrder - }; - } - private TestParameterConfig WithAxialForceSetpointMode(bool usePullForceSetpoint) { return new TestParameterConfig @@ -935,9 +967,9 @@ public sealed class MainWindowViewModel : ObservableObject }; } - private TestParameterConfig WithNoLoadSpeedSetting(double noLoadSpeedSetting) + private TestParameterConfig WithTestPageInputParameter(TestPageInputParameter parameter, double value) { - TestParameterConfig config = CloneParameterConfig(_parameterConfig); + TestParameterConfig config = _parameterConfig; return new TestParameterConfig { AxialDisplacementLimit = config.AxialDisplacementLimit, @@ -946,22 +978,22 @@ public sealed class MainWindowViewModel : ObservableObject AxialForceLowerLimit = config.AxialForceLowerLimit, AxialForceUpperLimit = config.AxialForceUpperLimit, AxialForceCoefficient = config.AxialForceCoefficient, - AxialForceSetpoint = config.AxialForceSetpoint, - AxialJumpForceSetpoint = config.AxialJumpForceSetpoint, + AxialForceSetpoint = parameter == TestPageInputParameter.AxialPullForceSetpoint ? value : config.AxialForceSetpoint, + AxialJumpForceSetpoint = parameter == TestPageInputParameter.AxialJumpForceSetpoint ? value : config.AxialJumpForceSetpoint, UseAxialPullForceSetpoint = config.UseAxialPullForceSetpoint, AxialForceProtection = config.AxialForceProtection, - AxialForceHoldTime = config.AxialForceHoldTime, + AxialForceHoldTime = parameter == TestPageInputParameter.AxialForceHoldTime ? value : config.AxialForceHoldTime, SpeedTorqueDisplacementLimit = config.SpeedTorqueDisplacementLimit, SpeedTorqueSpeed = config.SpeedTorqueSpeed, SpeedTorqueManualDisplacement = config.SpeedTorqueManualDisplacement, TorqueCoefficient = config.TorqueCoefficient, TorqueProtection = config.TorqueProtection, - HoldTorque = config.HoldTorque, - TorqueHoldTime = config.TorqueHoldTime, + HoldTorque = parameter == TestPageInputParameter.HoldTorque ? value : config.HoldTorque, + TorqueHoldTime = parameter == TestPageInputParameter.TorqueHoldTime ? value : config.TorqueHoldTime, SpeedCoefficient = config.SpeedCoefficient, SpeedStopThreshold = config.SpeedStopThreshold, PressureCoefficient = config.PressureCoefficient, - NoLoadSpeedSetting = noLoadSpeedSetting, + NoLoadSpeedSetting = parameter == TestPageInputParameter.NoLoadSpeedSetting ? value : config.NoLoadSpeedSetting, PlcIpAddress = config.PlcIpAddress, PlcPort = config.PlcPort, PlcUnitId = config.PlcUnitId, @@ -2023,6 +2055,178 @@ public sealed class MainWindowViewModel : ObservableObject } } + private void QueueFloatTestPageInputWrite(TestPageInputParameter parameter, ushort registerAddress, string fieldName, string input) + { + if (string.IsNullOrWhiteSpace(input)) + { + return; + } + + if (!TryReadNonNegative(input, fieldName, out double value, out string error)) + { + SetTestPageInputStatus(parameter, error); + return; + } + + _ = WriteFloatTestPageInputAsync(parameter, registerAddress, fieldName, value); + } + + private void QueueTenthsTestPageInputWrite(TestPageInputParameter parameter, ushort registerAddress, string fieldName, string input) + { + if (string.IsNullOrWhiteSpace(input)) + { + return; + } + + if (!TryReadNonNegative(input, fieldName, out double value, out string error) + || !CanScaleTenthsToPlc(value, fieldName, out error)) + { + SetTestPageInputStatus(parameter, error); + return; + } + + _ = WriteTenthsTestPageInputAsync(parameter, registerAddress, fieldName, value); + } + + private async Task WriteFloatTestPageInputAsync(TestPageInputParameter parameter, ushort registerAddress, string fieldName, double value) + { + await _testPageInputWriteLock.WaitAsync(); + try + { + PlcConnectionConfig config = _parameterConfig.ToPlcConnectionConfig(); + await _plcRegisterService.WriteFloatAsync(config, registerAddress, (float)value); + float confirmedValue = await _plcRegisterService.ReadFloatAsync(config, registerAddress); + if (float.IsNaN(confirmedValue) || float.IsInfinity(confirmedValue)) + { + throw new InvalidOperationException($"{fieldName} D{registerAddress} 回读无效。"); + } + + await ApplyConfirmedTestPageInputAsync(parameter, fieldName, registerAddress, value, confirmedValue); + } + catch (Exception ex) + { + SetTestPageInputStatus(parameter, $"{fieldName}自动写入 D{registerAddress} 失败:{ex.Message}"); + Log.Error(ex, "{FieldName}自动写入失败,D{RegisterAddress}={Value}", fieldName, registerAddress, value); + } + finally + { + _testPageInputWriteLock.Release(); + } + } + + private async Task WriteTenthsTestPageInputAsync(TestPageInputParameter parameter, ushort registerAddress, string fieldName, double value) + { + await _testPageInputWriteLock.WaitAsync(); + try + { + ushort rawValue = ScaleTenthsToPlc(value, fieldName); + PlcConnectionConfig config = _parameterConfig.ToPlcConnectionConfig(); + await _plcRegisterService.WriteUInt16Async(config, registerAddress, rawValue); + ushort confirmedRawValue = await _plcRegisterService.ReadUInt16Async(config, registerAddress); + double confirmedValue = ScaleTenthsFromPlc(confirmedRawValue); + await ApplyConfirmedTestPageInputAsync(parameter, fieldName, registerAddress, value, confirmedValue); + } + catch (Exception ex) + { + SetTestPageInputStatus(parameter, $"{fieldName}自动写入 D{registerAddress} 失败:{ex.Message}"); + Log.Error(ex, "{FieldName}自动写入失败,D{RegisterAddress}={Value}", fieldName, registerAddress, value); + } + finally + { + _testPageInputWriteLock.Release(); + } + } + + private async Task ApplyConfirmedTestPageInputAsync( + TestPageInputParameter parameter, + string fieldName, + ushort registerAddress, + double requestedValue, + double confirmedValue) + { + _parameterConfig = WithTestPageInputParameter(parameter, confirmedValue); + SaveParameterConfig(); + UpdateParameterSummaries(); + ApplyConfirmedTestPageInputToEditor(parameter, requestedValue, confirmedValue); + + if (parameter is TestPageInputParameter.HoldTorque or TestPageInputParameter.TorqueHoldTime) + { + _cachedTorqueCurve = null; + UpdateTorqueCurveStatus(); + } + + SetTestPageInputStatus(parameter, $"{fieldName}已自动写入并回读确认 D{registerAddress}={FormatConfigNumber(confirmedValue)}"); + Log.Information("{FieldName}自动写入并回读成功,D{RegisterAddress}={Value}", fieldName, registerAddress, confirmedValue); + + if (parameter is TestPageInputParameter.AxialPullForceSetpoint or TestPageInputParameter.AxialJumpForceSetpoint) + { + await AutoStopIfSetpointReachedAsync(); + } + } + + private void ApplyConfirmedTestPageInputToEditor(TestPageInputParameter parameter, double requestedValue, double confirmedValue) + { + string? currentInput = parameter switch + { + TestPageInputParameter.AxialPullForceSetpoint when _parameterConfig.UseAxialPullForceSetpoint => AxialForceSetpointInput, + TestPageInputParameter.AxialJumpForceSetpoint when !_parameterConfig.UseAxialPullForceSetpoint => AxialForceSetpointInput, + TestPageInputParameter.AxialForceHoldTime => AxialForceHoldTimeInput, + TestPageInputParameter.HoldTorque => HoldTorqueInput, + TestPageInputParameter.TorqueHoldTime => TorqueHoldTimeInput, + TestPageInputParameter.NoLoadSpeedSetting => NoLoadSpeedSettingInput, + _ => null + }; + + if (currentInput is null + || !TryReadNumber(currentInput, "自动写入输入", out double currentValue, out _) + || Math.Abs(currentValue - requestedValue) > 0.000001) + { + return; + } + + _isApplyingParameterConfigToInputs = true; + try + { + string confirmedText = FormatConfigNumber(confirmedValue); + switch (parameter) + { + case TestPageInputParameter.AxialPullForceSetpoint: + case TestPageInputParameter.AxialJumpForceSetpoint: + AxialForceSetpointInput = confirmedText; + break; + case TestPageInputParameter.AxialForceHoldTime: + AxialForceHoldTimeInput = confirmedText; + break; + case TestPageInputParameter.HoldTorque: + HoldTorqueInput = confirmedText; + break; + case TestPageInputParameter.TorqueHoldTime: + TorqueHoldTimeInput = confirmedText; + break; + case TestPageInputParameter.NoLoadSpeedSetting: + NoLoadSpeedSettingInput = confirmedText; + break; + } + } + finally + { + _isApplyingParameterConfigToInputs = false; + } + } + + private void SetTestPageInputStatus(TestPageInputParameter parameter, string message) + { + ParameterStatusText = message; + if (parameter == TestPageInputParameter.NoLoadSpeedSetting) + { + NoLoadSpeedStatusText = $"状态:{message}"; + } + else + { + StatusText = message; + } + } + private async Task SaveNoLoadSpeedSettingAsync() { if (!TryReadNonNegative(NoLoadSpeedSettingInput, "空载转速设置", out double setting, out string error)) @@ -2032,22 +2236,11 @@ public sealed class MainWindowViewModel : ObservableObject return; } - try - { - PlcConnectionConfig config = _parameterConfig.ToPlcConnectionConfig(); - await _plcRegisterService.WriteFloatAsync(config, NoLoadSpeedSettingRegister, (float)setting); - float confirmedSetting = await _plcRegisterService.ReadFloatAsync(config, NoLoadSpeedSettingRegister); - _parameterConfig = WithNoLoadSpeedSetting(confirmedSetting); - SaveParameterConfig(); - NoLoadSpeedSettingInput = FormatConfigNumber(confirmedSetting); - NoLoadSpeedStatusText = $"状态:空载转速设置已写入 D{NoLoadSpeedSettingRegister}"; - Log.Information("空载转速设置写入并回读成功,D{RegisterAddress}={Value}", NoLoadSpeedSettingRegister, confirmedSetting); - } - catch (Exception ex) - { - NoLoadSpeedStatusText = $"状态:空载转速设置写入失败:{ex.Message}"; - Log.Error(ex, "空载转速设置写入失败,D{RegisterAddress}={Value}", NoLoadSpeedSettingRegister, setting); - } + await WriteFloatTestPageInputAsync( + TestPageInputParameter.NoLoadSpeedSetting, + NoLoadSpeedSettingRegister, + "空载转速设置", + setting); } private async Task RecordNoLoadSpeedAsync()