From 07a652c65c4c35e017cb0bf722502a4b9f7f300c Mon Sep 17 00:00:00 2001 From: "GukSang.Jin" Date: Tue, 9 Jun 2026 14:18:33 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0121?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DentistryHandpieces/MainWindowViewModel.cs | 146 ++++++++++++++++----- 1 file changed, 111 insertions(+), 35 deletions(-) diff --git a/DentistryHandpieces/MainWindowViewModel.cs b/DentistryHandpieces/MainWindowViewModel.cs index 5d9ed2e..43d2e61 100644 --- a/DentistryHandpieces/MainWindowViewModel.cs +++ b/DentistryHandpieces/MainWindowViewModel.cs @@ -1834,16 +1834,19 @@ public sealed class MainWindowViewModel : ObservableObject private async Task SaveParameterConfigFromInputsAsync() { - if (!TryReadParameterConfig(out TestParameterConfig config, out string error)) - { - ParameterStatusText = error; - Log.Warning("参数保存被阻止:{ValidationError}", error); - return; - } - + ParameterStatusText = "正在检查并保存修改项..."; + await _testPageInputWriteLock.WaitAsync(); try { - await WriteParameterConfigToPlcAsync(config); + if (!TryReadParameterConfig(out TestParameterConfig config, out string error)) + { + ParameterStatusText = error; + Log.Warning("参数保存被阻止:{ValidationError}", error); + return; + } + + IReadOnlyList changedPlcItems = await WriteChangedParameterConfigToPlcAsync(_parameterConfig, config); + bool communicationConfigChanged = HasCommunicationConfigChanged(_parameterConfig, config); _parameterConfig = config; SaveParameterConfig(); UpdateParameterSummaries(); @@ -1853,45 +1856,103 @@ public sealed class MainWindowViewModel : ObservableObject await AutoStopIfSpeedTorqueProtectionReachedAsync(); _hasLoadedParameterConfigFromPlc = true; _parameterRetryTimer.Stop(); - ParameterStatusText = "PLC配置写入成功"; + + if (changedPlcItems.Count > 0) + { + ParameterStatusText = $"保存成功,仅写入 {changedPlcItems.Count} 项:{string.Join("、", changedPlcItems)}"; + } + else if (communicationConfigChanged) + { + ParameterStatusText = "保存成功,通信配置已更新,无需写入 PLC 参数。"; + } + else + { + ParameterStatusText = "参数未修改,无需写入 PLC。"; + } + Log.Information( - "PLC参数配置写入成功,地址 {IpAddress}:{Port},站号 {UnitId},轴向力模式 {AxialForceMode}", + "参数配置差异保存成功,PLC写入 {WriteCount} 项:{ChangedItems},通信配置修改 {CommunicationConfigChanged},地址 {IpAddress}:{Port},站号 {UnitId}", + changedPlcItems.Count, + changedPlcItems.Count > 0 ? string.Join("、", changedPlcItems) : "无", + communicationConfigChanged, config.PlcIpAddress, config.PlcPort, - config.PlcUnitId, - config.UseAxialPullForceSetpoint ? "轴向拉力" : "轴向跳动力"); + config.PlcUnitId); } catch (Exception ex) { ParameterStatusText = $"PLC配置写入失败:{ex.Message}"; Log.Error(ex, "PLC参数配置写入失败"); } + finally + { + _testPageInputWriteLock.Release(); + } } - private async Task WriteParameterConfigToPlcAsync(TestParameterConfig config) + private async Task> WriteChangedParameterConfigToPlcAsync(TestParameterConfig current, TestParameterConfig updated) { - PlcConnectionConfig plcConfig = config.ToPlcConnectionConfig(); - await _plcRegisterService.WriteFloatAsync(plcConfig, AxialDisplacementLimitRegister, (float)config.AxialDisplacementLimit); - await _plcRegisterService.WriteFloatAsync(plcConfig, AxialSpeedRegister, (float)config.AxialSpeed); - await _plcRegisterService.WriteFloatAsync(plcConfig, AxialManualDisplacementRegister, (float)config.AxialManualDisplacement); - await _plcCoilService.WriteCoilAsync(plcConfig, AxialForceModeCoil, config.UseAxialPullForceSetpoint); - await _plcRegisterService.WriteFloatAsync(plcConfig, AxialPullForceSetpointRegister, (float)config.AxialForceSetpoint); - await _plcRegisterService.WriteFloatAsync(plcConfig, AxialJumpForceSetpointRegister, (float)config.AxialJumpForceSetpoint); - await _plcRegisterService.WriteFloatAsync(plcConfig, AxialForceLowerLimitRegister, (float)config.AxialForceLowerLimit); - await _plcRegisterService.WriteFloatAsync(plcConfig, AxialForceUpperLimitRegister, (float)config.AxialForceUpperLimit); - await _plcRegisterService.WriteFloatAsync(plcConfig, AxialForceCoefficientRegister, (float)config.AxialForceCoefficient); - await _plcRegisterService.WriteFloatAsync(plcConfig, AxialForceProtectionRegister, (float)config.AxialForceProtection); - await _plcRegisterService.WriteUInt16Async(plcConfig, AxialForceHoldTimeRegister, ScaleTenthsToPlc(config.AxialForceHoldTime, "轴向力保持时间设置")); - await _plcRegisterService.WriteFloatAsync(plcConfig, SpeedTorqueDisplacementLimitRegister, (float)config.SpeedTorqueDisplacementLimit); - await _plcRegisterService.WriteFloatAsync(plcConfig, SpeedTorqueSpeedRegister, (float)config.SpeedTorqueSpeed); - await _plcRegisterService.WriteFloatAsync(plcConfig, SpeedTorqueManualDisplacementRegister, (float)config.SpeedTorqueManualDisplacement); - await _plcRegisterService.WriteFloatAsync(plcConfig, TorqueCoefficientRegister, (float)config.TorqueCoefficient); - await _plcRegisterService.WriteFloatAsync(plcConfig, TorqueProtectionRegister, (float)config.TorqueProtection); - await _plcRegisterService.WriteUInt16Async(plcConfig, HoldTorqueRegister, ScaleTenthsToPlc(config.HoldTorque, "保持扭矩设置")); - await _plcRegisterService.WriteUInt16Async(plcConfig, TorqueHoldTimeRegister, ScaleTenthsToPlc(config.TorqueHoldTime, "扭矩保持时间设置")); - await _plcRegisterService.WriteFloatAsync(plcConfig, SpeedCoefficientRegister, (float)config.SpeedCoefficient); - await _plcRegisterService.WriteFloatAsync(plcConfig, SpeedStopThresholdRegister, (float)config.SpeedStopThreshold); - await _plcRegisterService.WriteFloatAsync(plcConfig, PressureCoefficientRegister, (float)config.PressureCoefficient); + PlcConnectionConfig plcConfig = updated.ToPlcConnectionConfig(); + var changedItems = new List(); + + await WriteChangedFloatAsync(AxialDisplacementLimitRegister, "轴向位移极限", current.AxialDisplacementLimit, updated.AxialDisplacementLimit); + await WriteChangedFloatAsync(AxialSpeedRegister, "轴向手/自动速度", current.AxialSpeed, updated.AxialSpeed); + await WriteChangedFloatAsync(AxialManualDisplacementRegister, "轴向手动位移", current.AxialManualDisplacement, updated.AxialManualDisplacement); + await WriteChangedCoilAsync(AxialForceModeCoil, "轴向力模式", current.UseAxialPullForceSetpoint, updated.UseAxialPullForceSetpoint); + await WriteChangedFloatAsync(AxialPullForceSetpointRegister, "轴向拉力设置", current.AxialForceSetpoint, updated.AxialForceSetpoint); + await WriteChangedFloatAsync(AxialJumpForceSetpointRegister, "轴向跳动力设置", current.AxialJumpForceSetpoint, updated.AxialJumpForceSetpoint); + await WriteChangedFloatAsync(AxialForceLowerLimitRegister, "轴向力下限", current.AxialForceLowerLimit, updated.AxialForceLowerLimit); + await WriteChangedFloatAsync(AxialForceUpperLimitRegister, "轴向力上限", current.AxialForceUpperLimit, updated.AxialForceUpperLimit); + await WriteChangedFloatAsync(AxialForceCoefficientRegister, "轴向力系数", current.AxialForceCoefficient, updated.AxialForceCoefficient); + await WriteChangedFloatAsync(AxialForceProtectionRegister, "轴向力保护", current.AxialForceProtection, updated.AxialForceProtection); + await WriteChangedTenthsAsync(AxialForceHoldTimeRegister, "轴向力保持时间", current.AxialForceHoldTime, updated.AxialForceHoldTime); + await WriteChangedFloatAsync(SpeedTorqueDisplacementLimitRegister, "转速/扭矩位移极限", current.SpeedTorqueDisplacementLimit, updated.SpeedTorqueDisplacementLimit); + await WriteChangedFloatAsync(SpeedTorqueSpeedRegister, "转速/扭矩手/自动速度", current.SpeedTorqueSpeed, updated.SpeedTorqueSpeed); + await WriteChangedFloatAsync(SpeedTorqueManualDisplacementRegister, "转速/扭矩手动位移", current.SpeedTorqueManualDisplacement, updated.SpeedTorqueManualDisplacement); + await WriteChangedFloatAsync(TorqueCoefficientRegister, "扭矩系数", current.TorqueCoefficient, updated.TorqueCoefficient); + await WriteChangedFloatAsync(TorqueProtectionRegister, "扭矩保护", current.TorqueProtection, updated.TorqueProtection); + await WriteChangedTenthsAsync(HoldTorqueRegister, "保持扭矩", current.HoldTorque, updated.HoldTorque); + await WriteChangedTenthsAsync(TorqueHoldTimeRegister, "扭矩保持时间", current.TorqueHoldTime, updated.TorqueHoldTime); + await WriteChangedFloatAsync(SpeedCoefficientRegister, "转速系数", current.SpeedCoefficient, updated.SpeedCoefficient); + await WriteChangedFloatAsync(SpeedStopThresholdRegister, "低速停止", current.SpeedStopThreshold, updated.SpeedStopThreshold); + await WriteChangedFloatAsync(PressureCoefficientRegister, "压力系数", current.PressureCoefficient, updated.PressureCoefficient); + + return changedItems; + + async Task WriteChangedFloatAsync(ushort registerAddress, string fieldName, double oldValue, double newValue) + { + if (AreEquivalentFloatRegisterValues(oldValue, newValue)) + { + return; + } + + await _plcRegisterService.WriteFloatAsync(plcConfig, registerAddress, (float)newValue); + changedItems.Add($"{fieldName}(D{registerAddress})"); + } + + async Task WriteChangedTenthsAsync(ushort registerAddress, string fieldName, double oldValue, double newValue) + { + ushort oldRawValue = ScaleTenthsToPlc(oldValue, fieldName); + ushort newRawValue = ScaleTenthsToPlc(newValue, fieldName); + if (oldRawValue == newRawValue) + { + return; + } + + await _plcRegisterService.WriteUInt16Async(plcConfig, registerAddress, newRawValue); + changedItems.Add($"{fieldName}(D{registerAddress})"); + } + + async Task WriteChangedCoilAsync(ushort coilAddress, string fieldName, bool oldValue, bool newValue) + { + if (oldValue == newValue) + { + return; + } + + await _plcCoilService.WriteCoilAsync(plcConfig, coilAddress, newValue); + changedItems.Add($"{fieldName}(M{coilAddress})"); + } } private bool TryReadParameterConfig(out TestParameterConfig config, out string error) @@ -3541,6 +3602,21 @@ public sealed class MainWindowViewModel : ObservableObject return value.ToString("0.###", CultureInfo.InvariantCulture); } + private static bool AreEquivalentFloatRegisterValues(double left, double right) + { + return BitConverter.SingleToInt32Bits((float)left) == BitConverter.SingleToInt32Bits((float)right); + } + + private static bool HasCommunicationConfigChanged(TestParameterConfig current, TestParameterConfig updated) + { + return !string.Equals(current.PlcIpAddress, updated.PlcIpAddress, StringComparison.OrdinalIgnoreCase) + || current.PlcPort != updated.PlcPort + || current.PlcUnitId != updated.PlcUnitId + || current.PlcPulseMilliseconds != updated.PlcPulseMilliseconds + || current.PlcTimeoutMilliseconds != updated.PlcTimeoutMilliseconds + || current.FloatWordOrder != updated.FloatWordOrder; + } + private static double ScaleTenthsFromPlc(ushort registerValue) { return registerValue / TenthsRegisterScale;