using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using MembranePoreTester.Communication; using Modbus.Device; using System; using System.Net; using System.Threading.Tasks; using System.Windows.Threading; namespace MembranePoreTester.ViewModels { public partial class ParameterViewModel : ObservableObject { private readonly IPlcService _plcService; private readonly PlcConfiguration _config; private DispatcherTimer _autoRefreshTimer; private bool _isUpdatingFromPlc = false; // 防止定时器更新触发写入 // 构造函数(依赖注入) public ParameterViewModel() { _plcService = App.PlcService; _config = App.PlcConfig; // 启动定时器(每秒自动刷新) _autoRefreshTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) }; _autoRefreshTimer.Tick += AutoRefreshTimer_Tick; _autoRefreshTimer.Start(); } // ========== 设备数据属性(全部使用源生成器) ========== [ObservableProperty] private double _upperLampData1; [ObservableProperty] private double _upperLampData2; [ObservableProperty] private double _upperLampData3; [ObservableProperty] private double _upperLampData4; [ObservableProperty] private double _upperLampData5; [ObservableProperty] private double _upperLampData6; [ObservableProperty] private double _lowerLampData1; [ObservableProperty] private double _lowerLampData2; [ObservableProperty] private double _lowerLampData3; [ObservableProperty] private double _lowerLampData4; [ObservableProperty] private double _lowerLampData5; [ObservableProperty] private double _lowerLampData6; [ObservableProperty] private double _middleLamp1; [ObservableProperty] private double _middleLamp2; [ObservableProperty] private double _middleLamp3; [ObservableProperty] private double _middleLamp4; [ObservableProperty] private double _middleLamp5; [ObservableProperty] private double _middleLamp6; [ObservableProperty] private double _middleLamp7; [ObservableProperty] private double _leftEyeAreaCoeff; [ObservableProperty] private double _rightEyeAreaCoeff; [ObservableProperty] private double _saveRateCorrectionCoeff; [ObservableProperty] private double _motorLimit; [ObservableProperty] private double _resetCompensation; // 通信状态(用于界面显示) [ObservableProperty] private string _communicationStatus = "未连接"; [ObservableProperty] private System.Windows.Media.SolidColorBrush _statusColor = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Red); // ========== 属性变化时的自动写入(用户修改时立即写入PLC) ========== // 这里我们重写属性 setter,在值变化时写入PLC(排除定时器更新) //partial void OnUpperLampData1Changing(double value) => WriteParameterAsync(_config.UpperLampData1, value, false); //partial void OnUpperLampData2Changing(double value) => WriteParameterAsync(_config.UpperLampData2, value, false); // ... 其他灯条属性类似(为了简洁,仅展示几个,您需全部添加) // 您可以使用代码生成工具批量添加,或手动添加所有属性对应的 Changing 方法。 // 注意:只有需要自动写入的才添加,只读属性不添加。 // 为节省篇幅,下面仅示例部分,实际使用时请按此模式为所有需要读写的属性添加 partial void OnLowerLampData1Changing(double value) => WriteParameterAsync(_config.LowerLampData1, value, false); partial void OnMiddleLamp1Changing(double value) => WriteParameterAsync(_config.MiddleLamp1, value, false); partial void OnLeftEyeAreaCoeffChanging(double value) => WriteParameterAsync(_config.LeftEyeAreaCoeff, value, true); partial void OnRightEyeAreaCoeffChanging(double value) => WriteParameterAsync(_config.RightEyeAreaCoeff, value, true); partial void OnSaveRateCorrectionCoeffChanging(double value) => WriteParameterAsync(_config.SaveRateCorrectionCoeff, value, true); partial void OnMotorLimitChanging(double value) => WriteParameterAsync(_config.MotorLimit, value, true); partial void OnResetCompensationChanging(double value) => WriteParameterAsync(_config.ResetCompensation, value, true); // 写入PLC的通用方法 private async Task WriteParameterAsync(ushort address, double value, bool isFloat) { if (_isUpdatingFromPlc) return; // 定时器读取时跳过写入 try { if (isFloat) await WriteFloatAsync(address, (float)value); else await WriteInt16Async(address, (ushort)value); System.Diagnostics.Debug.WriteLine($"写入成功: 地址={address}, 值={value}"); } catch (Exception ex) { System.Windows.MessageBox.Show($"写入参数失败: {ex.Message}", "错误"); } } //// ========== 手动保存/读取命令 ========== //[RelayCommand] //private async Task SaveAllParameters() //{ // // 将所有当前值写入PLC // // 这里可以遍历所有属性,但为了简单,直接调用各个写入方法 // await WriteParameterAsync(_config.UpperLampData1, UpperLampData1, false); // // ... 所有其他属性 // CommunicationStatus = "参数已保存"; // StatusColor = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Green); //} //[RelayCommand] //private async Task LoadAllParameters() //{ // // 从PLC读取所有参数并刷新界面 // await RefreshAllFromPlc(); // CommunicationStatus = "参数已读取"; // StatusColor = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Green); //} // 定时刷新(从PLC读取,但不触发写入) private async void AutoRefreshTimer_Tick(object sender, EventArgs e) { await RefreshAllFromPlc(); } private async Task RefreshAllFromPlc() { _isUpdatingFromPlc = true; try { // 读取并更新每个属性(16位整数和浮点数) UpperLampData1 = await ReadInt16Async(_config.UpperLampData1); UpperLampData2 = await ReadInt16Async(_config.UpperLampData2); UpperLampData3 = await ReadInt16Async(_config.UpperLampData3); UpperLampData4 = await ReadInt16Async(_config.UpperLampData4); UpperLampData5 = await ReadInt16Async(_config.UpperLampData5); UpperLampData6 = await ReadInt16Async(_config.UpperLampData6); LowerLampData1 = await ReadInt16Async(_config.LowerLampData1); LowerLampData2 = await ReadInt16Async(_config.LowerLampData2); LowerLampData3 = await ReadInt16Async(_config.LowerLampData3); LowerLampData4 = await ReadInt16Async(_config.LowerLampData4); LowerLampData5 = await ReadInt16Async(_config.LowerLampData5); LowerLampData6 = await ReadInt16Async(_config.LowerLampData6); MiddleLamp1 = await ReadInt16Async(_config.MiddleLamp1); MiddleLamp2 = await ReadInt16Async(_config.MiddleLamp2); MiddleLamp3 = await ReadInt16Async(_config.MiddleLamp3); MiddleLamp4 = await ReadInt16Async(_config.MiddleLamp4); MiddleLamp5 = await ReadInt16Async(_config.MiddleLamp5); MiddleLamp6 = await ReadInt16Async(_config.MiddleLamp6); MiddleLamp7 = await ReadInt16Async(_config.MiddleLamp7); LeftEyeAreaCoeff = await ReadFloatAsync(_config.LeftEyeAreaCoeff); RightEyeAreaCoeff = await ReadFloatAsync(_config.RightEyeAreaCoeff); SaveRateCorrectionCoeff = await ReadFloatAsync(_config.SaveRateCorrectionCoeff); MotorLimit = await ReadFloatAsync(_config.MotorLimit); ResetCompensation = await ReadFloatAsync(_config.ResetCompensation); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"刷新参数失败: {ex.Message}"); CommunicationStatus = "通信异常"; StatusColor = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Red); } finally { _isUpdatingFromPlc = false; } } // 设备控制命令(供右侧按钮使用) [RelayCommand] private async Task Reset() { // 实现硬件复位逻辑 CommunicationStatus = "复位指令已发送"; await _plcService.WriteCoilAsync(91, true); Thread.Sleep(100); await _plcService.WriteCoilAsync(91, false); Thread.Sleep(100); } [RelayCommand] private async Task OpenLeftEye() { CommunicationStatus = "左眼开启指令已发送"; await _plcService.WriteCoilAsync(0, true); Thread.Sleep(100); } [RelayCommand] private async Task OpenRightEye() { CommunicationStatus = "右眼开启指令已发送"; await _plcService.WriteCoilAsync(1, true); Thread.Sleep(100); } [RelayCommand] private async Task Reverse() { CommunicationStatus = "反转指令已发送"; await _plcService.WriteCoilAsync(10, true); Thread.Sleep(100); await _plcService.WriteCoilAsync(10, false); Thread.Sleep(100); } [RelayCommand] private async Task Forward() { CommunicationStatus = "正转指令已发送"; await _plcService.WriteCoilAsync(11, true); Thread.Sleep(100); await _plcService.WriteCoilAsync(11, false); Thread.Sleep(100); } // 导航命令(可根据实际需求实现) [RelayCommand] private void NavigateHome() { // 切换页面逻辑 } [RelayCommand] private void NavigateTest() { // 切换页面逻辑 } [RelayCommand] private void NavigateDataRecord() { // 切换页面逻辑 } [RelayCommand] private void NavigateRecordScreen() { // 切换页面逻辑 } // ========== PLC 读写基础方法 ========== private async Task ReadInt16Async(ushort address) { var registers = await ((ModbusTcpPlcService)_plcService).ReadHoldingRegistersAsync(address, 1); return registers[0]; } private async Task WriteInt16Async(ushort address, ushort value) { await ((ModbusTcpPlcService)_plcService).WriteSingleRegisterAsync(address, value); } private async Task ReadFloatAsync(ushort address) { var registers = await ((ModbusTcpPlcService)_plcService).ReadHoldingRegistersAsync(address, 2); return ((ModbusTcpPlcService)_plcService).UshortToFloat(registers[1], registers[0]); } private async Task WriteFloatAsync(ushort address, float value) { await ((ModbusTcpPlcService)_plcService).WriteMultipleRegistersAsync(address, value); } // 释放定时器(在窗口关闭时调用) public void Dispose() { _autoRefreshTimer?.Stop(); } } }