From c8298ab004b709ed40f76883f59c9857458958b2 Mon Sep 17 00:00:00 2001 From: "GukSang.Jin" Date: Sat, 6 Jun 2026 14:54:02 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B02019?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DentistryHandpieces/COMMENT.csv | Bin 5074 -> 6934 bytes .../HiddenSpeedSettingsViewModel.cs | 199 +++++++++++++++++ .../HiddenSpeedSettingsWindow.xaml | 143 ++++++++++++ .../HiddenSpeedSettingsWindow.xaml.cs | 27 +++ DentistryHandpieces/MainWindow.xaml | 209 ++++++++++++++---- DentistryHandpieces/MainWindow.xaml.cs | 103 ++++++++- DentistryHandpieces/MainWindowViewModel.cs | 101 ++++++++- .../ModbusTcpPlcCoilService.cs | 33 +++ 8 files changed, 769 insertions(+), 46 deletions(-) create mode 100644 DentistryHandpieces/HiddenSpeedSettingsViewModel.cs create mode 100644 DentistryHandpieces/HiddenSpeedSettingsWindow.xaml create mode 100644 DentistryHandpieces/HiddenSpeedSettingsWindow.xaml.cs diff --git a/DentistryHandpieces/COMMENT.csv b/DentistryHandpieces/COMMENT.csv index 68faa2d7d43c2b621d7768da2ed2fe6c08cb5726..8160eb4dfe058b8c9378f61b0de9e45338a18177 100644 GIT binary patch literal 6934 zcma)BO>C4^6n(e=!JuMTu|ZLT#$Y=?olc2y!)&nWO#4kW)B-ir)Yg=#jnrUK14I){ z2AC2f3z`rY#B^!gxp8GAy74EX3&WBGS>OUw77glmxO3ip_sutnize;6_uO~xx%b`o zX8P|xd)xt6cL&`*_qywMc{h;$R(BKc{JcEe*grcov1xis@=9{^$ceGNZ`?n9y>;&L z?do$b=bm?a+_v=R?e>AO_3HNVJ#K6I*R5keT$J^ZG@7jC+`amv<8FoPxnO-H`J>JC zd}lo$`4@uq(7)7K-?g&yn7hO%gPqlf&OUu*?lrCsbyg45Hz#wSY_xu>Z<+gjiq@Pf zcI>y_o0|SSJq zm_d(OYVR2D8(&M*LPcOXRl9#$Ez|^-bCZuZ)IwEYIXv2!R||7lYJZ+wYd^K17Ur_l z{&CD&s3meye`;Acd3iF_tL$@WHvT*j=2A{OL#hr-m`geBEwzNXlpS*cCd{Sm>(Xr4 zT*_&mGMde$?0T^t@~pQ{jMTsyoyly6JabsV+*-*VR|iuf+gLs`J9=sF#DyyNhYza~`W$@U`Y^-L=dr`q zhk3^FnPD80x!E|#N~&k$<9dCxQSv7B`VSq%$4I2U?wE_lvyoNNsXo}1-U?4eZKR;iMYC?1wy$;TC|RjuT+#q7(9%_`6yN9PHn z-Nx5j$8sL@oNc^8&o++3&c>AL<~iuALwg*TJZZP_^+vHg^**uj^frm(kY^l+I>vFx z%f`{W!p6~C!p0fO)D81Y=eN{9nOgU66|r+Rqk`6oGtS9`so!lSwpCwEx~-h(ab&2i zk;_MS`TImH#BHk%ylZVbp^;hB9d}#Ld^N_K256ntl6$rfZ~UBzP)X5zz2FwAPmLra zwS`j>A68Zob1}B+iGO0{${}L({QlB;)m73c)NOO;6^1oty*{H@^1KSI$>c_5I zI;v~rI+wnU@lvj&lZw?uFU)k_Y*^>s*Dii{X>n#@I+wn}!I3!b8QOuNoqGX$=g3Q= ziq(@fGym=6p-E!&1i9J06X+1crd9hN=heQ3DsISEiB{Bu*s$mMUcuWXv@>6H^o&?OyrfXwLOLP*;?K2loy=ps#vR?a%`X ziw77mM)+3xv^~=~36=Ck+wV0B?{MoAo|Sv`ALpCtiNbkW^xcNroV7)qY#)8UfX{HF z&)q(=mLPlVhp2t&&s%OG$U)yQz`$4hjvF5;p{E?V=^KVv)kf6LI>SWMtUO9ahvA0!gn5*F9$}O)4?CY$6HI|;10Fi3f^(QI>G1e`L!g=$L^w^V=L+Y{ayvxZTM+sOmAUNOjH_j&nD7RTVZr5)#QDack&EvIA{0X;>QaR#`B^`?|enH?+| zIlKwQ!d@2C#s@lUVAsO^0; z74bgW_nFq`g z{eVH8voNs+^aBPp&cehR&<_~Quq;fh0sVkst$@=W!HpSrfO-l}|F*d9ZlI^&Sm}7b zSPkhd@Dd!W9php}mZ#de}{cm^ix&$ z&4K5EUf;aRqt`!)cIOscgD)m;-i4g>#8a(NQp>4$F{@J8LTdFvDvy=>^1hbkELIIQH5=OI(8Hxe+tPM8+ diff --git a/DentistryHandpieces/HiddenSpeedSettingsViewModel.cs b/DentistryHandpieces/HiddenSpeedSettingsViewModel.cs new file mode 100644 index 0000000..dfb0831 --- /dev/null +++ b/DentistryHandpieces/HiddenSpeedSettingsViewModel.cs @@ -0,0 +1,199 @@ +using System.Collections.ObjectModel; +using System.Globalization; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; + +namespace DentistryHandpieces; + +public sealed class SpeedCoefficientSettingRow : ObservableObject +{ + private string _valueText = string.Empty; + + public required ushort Address { get; init; } + + public required string RangeText { get; init; } + + public string ValueText + { + get => _valueText; + set => SetProperty(ref _valueText, value); + } +} + +public sealed class HiddenSpeedSettingsViewModel : ObservableObject +{ + private const ushort LoadSpeedSettingRegister = 414; + private const ushort FirstSpeedCoefficientRegister = 1200; + private const int SpeedCoefficientCount = 30; + private readonly IPlcRegisterService _plcRegisterService; + private readonly PlcConnectionConfig _plcConfig; + private string _loadSpeedSettingInput = string.Empty; + private string _statusText = "打开后将从 PLC 读取参数。"; + private bool _isBusy; + + public HiddenSpeedSettingsViewModel(IPlcRegisterService plcRegisterService, PlcConnectionConfig plcConfig) + { + _plcRegisterService = plcRegisterService; + _plcConfig = plcConfig; + ReadCommand = new AsyncRelayCommand(ReadAsync); + SaveLoadSpeedSettingCommand = new AsyncRelayCommand(SaveLoadSpeedSettingAsync); + SaveSpeedCoefficientCommand = new AsyncRelayCommand(SaveSpeedCoefficientAsync); + + for (int index = 0; index < SpeedCoefficientCount; index++) + { + int lower = index == 0 ? 0 : index * 10000 + 1; + int upper = (index + 1) * 10000; + SpeedCoefficientSettings.Add(new SpeedCoefficientSettingRow + { + Address = checked((ushort)(FirstSpeedCoefficientRegister + index * 2)), + RangeText = $"转速系数{lower}-{upper}" + }); + } + } + + public ObservableCollection SpeedCoefficientSettings { get; } = []; + + public IAsyncRelayCommand ReadCommand { get; } + + public IAsyncRelayCommand SaveLoadSpeedSettingCommand { get; } + + public IAsyncRelayCommand SaveSpeedCoefficientCommand { get; } + + public string LoadSpeedSettingInput + { + get => _loadSpeedSettingInput; + set => SetProperty(ref _loadSpeedSettingInput, value); + } + + public string StatusText + { + get => _statusText; + private set => SetProperty(ref _statusText, value); + } + + public async Task ReadAsync() + { + if (_isBusy) + { + return; + } + + _isBusy = true; + StatusText = "正在读取 PLC 隐藏参数..."; + try + { + await ReadValuesAsync(); + StatusText = "隐藏参数读取成功。"; + } + catch (Exception ex) + { + StatusText = $"读取失败:{ex.Message}"; + } + finally + { + _isBusy = false; + } + } + + private async Task SaveLoadSpeedSettingAsync() + { + if (_isBusy) + { + return; + } + + if (!TryParseInt32(LoadSpeedSettingInput, out int loadSpeedSetting)) + { + StatusText = "负载转速设置必须是有效的 32 位整数。"; + return; + } + + _isBusy = true; + StatusText = "正在保存负载转速设置..."; + try + { + await _plcRegisterService.WriteInt32Async(_plcConfig, LoadSpeedSettingRegister, loadSpeedSetting); + int confirmedValue = await _plcRegisterService.ReadInt32Async(_plcConfig, LoadSpeedSettingRegister); + LoadSpeedSettingInput = confirmedValue.ToString(CultureInfo.InvariantCulture); + StatusText = "负载转速设置已保存并回读确认。"; + } + catch (Exception ex) + { + StatusText = $"负载转速设置保存失败:{ex.Message}"; + } + finally + { + _isBusy = false; + } + } + + private async Task SaveSpeedCoefficientAsync(SpeedCoefficientSettingRow? row) + { + if (_isBusy || row is null) + { + return; + } + + if (!TryParseFloat(row.ValueText, out float value)) + { + StatusText = $"{row.RangeText} 必须是有效数字。"; + return; + } + + _isBusy = true; + StatusText = $"正在保存{row.RangeText}..."; + try + { + await _plcRegisterService.WriteFloatAsync(_plcConfig, row.Address, value); + float confirmedValue = await _plcRegisterService.ReadFloatAsync(_plcConfig, row.Address); + if (float.IsNaN(confirmedValue) || float.IsInfinity(confirmedValue)) + { + throw new InvalidOperationException("PLC 回读值无效。"); + } + + row.ValueText = confirmedValue.ToString("0.########", CultureInfo.InvariantCulture); + StatusText = $"{row.RangeText}已保存并回读确认。"; + } + catch (Exception ex) + { + StatusText = $"{row.RangeText}保存失败:{ex.Message}"; + } + finally + { + _isBusy = false; + } + } + + private async Task ReadValuesAsync() + { + int loadSpeedSetting = await _plcRegisterService.ReadInt32Async(_plcConfig, LoadSpeedSettingRegister); + ushort[] addresses = SpeedCoefficientSettings.Select(static row => row.Address).ToArray(); + IReadOnlyDictionary values = await _plcRegisterService.ReadFloatValuesAsync(_plcConfig, addresses); + + LoadSpeedSettingInput = loadSpeedSetting.ToString(CultureInfo.InvariantCulture); + foreach (SpeedCoefficientSettingRow row in SpeedCoefficientSettings) + { + if (!values.TryGetValue(row.Address, out float value) + || float.IsNaN(value) + || float.IsInfinity(value)) + { + throw new InvalidOperationException($"{row.RangeText}读取无效。"); + } + + row.ValueText = value.ToString("0.########", CultureInfo.InvariantCulture); + } + } + + private static bool TryParseInt32(string input, out int value) + { + return int.TryParse(input, NumberStyles.Integer, CultureInfo.CurrentCulture, out value) + || int.TryParse(input, NumberStyles.Integer, CultureInfo.InvariantCulture, out value); + } + + private static bool TryParseFloat(string input, out float value) + { + bool parsed = float.TryParse(input, NumberStyles.Float, CultureInfo.CurrentCulture, out value) + || float.TryParse(input, NumberStyles.Float, CultureInfo.InvariantCulture, out value); + return parsed && !float.IsNaN(value) && !float.IsInfinity(value); + } +} diff --git a/DentistryHandpieces/HiddenSpeedSettingsWindow.xaml b/DentistryHandpieces/HiddenSpeedSettingsWindow.xaml new file mode 100644 index 0000000..310a2b2 --- /dev/null +++ b/DentistryHandpieces/HiddenSpeedSettingsWindow.xaml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + +