using System; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Threading; using MembranePoreTester.Communication; using MembranePoreTester.ViewModels; namespace MembranePoreTester.Views { public partial class ParameterWindow : Window { private readonly IPlcService _plcService; private readonly PlcConfiguration _config; private DispatcherTimer _autoRefreshTimer; // 用于快速查找文本框对应的写入地址和方法(可根据需要扩展) private readonly Dictionary _textBoxMapping; public ParameterWindow() { InitializeComponent(); _plcService = App.PlcService; _config = App.PlcConfig; // 初始化文本框到寄存器地址的映射 _textBoxMapping = new Dictionary { //[txtPressureUpper] = (_config.PressureUpperLimit, "加压上限"), //[txtPressureRate] = (_config.PressureRate, "加压速率"), //[txtPressureCoeff] = (_config.PressureCoeff, "加压系数"), //[txtHPCoeff11] = (_config.HPCoeff11, "工位1加压速率"), [txtHPCoeff1] = (_config.HPCoeff1, "工位1高压系数"), [txtLPCoeff1] = (_config.LPCoeff1, "工位1低压系数"), [txtHPCoeff2] = (_config.HPCoeff2, "工位2高压系数"), [txtLPCoeff2] = (_config.LPCoeff2, "工位2低压系数"), [txtHPCoeff3] = (_config.HPCoeff3, "工位3高压系数"), [txtLPCoeff3] = (_config.LPCoeff3, "工位3低压系数"), [txtLargeFlow1] = (_config.LargeFlowCoeff1, "工位1大流量系数"), [txtSmallFlow1] = (_config.SmallFlowCoeff1, "工位1小流量系数"), [txtLargeFlow2] = (_config.LargeFlowCoeff2, "工位2大流量系数"), [txtSmallFlow2] = (_config.SmallFlowCoeff2, "工位2小流量系数"), [txtLargeFlow3] = (_config.LargeFlowCoeff3, "工位3大流量系数"), [txtSmallFlow3] = (_config.SmallFlowCoeff3, "工位3小流量系数"), //[txtPressureZero] = (_config.PressureCalibZero, "压力零点"), //[txtPressureSpan] = (_config.PressureCalibSpan, "压力量程"), //[txtFlowZero] = (_config.FlowCalibZero, "流量零点"), //[txtFlowSpan] = (_config.FlowCalibSpan, "流量量程"), }; // 为所有文本框注册焦点事件 RegisterTextBoxEvents(); Loaded += async (s, e) => { // 启动定时器 _autoRefreshTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) }; _autoRefreshTimer.Tick += AutoRefreshTimer_Tick; _autoRefreshTimer.Start(); // 首次加载时读取一次 await LoadParameters(); }; Closed += (s, e) => _autoRefreshTimer?.Stop(); } private void RegisterTextBoxEvents() { foreach (var tb in _textBoxMapping.Keys) { tb.GotFocus += (s, e) => { /* 什么都不做,只是为了让自动刷新跳过当前焦点的文本框 */ }; tb.LostFocus += async (s, e) => { // 失去焦点时,将当前文本框的值写入PLC var (address, name) = _textBoxMapping[tb]; if (float.TryParse(tb.Text, out float value)) { try { await WriteFloatAsync(address, value); // 可选:显示短暂提示(避免频繁弹窗) System.Diagnostics.Debug.WriteLine($"{name} 已写入: {value}"); } catch (Exception ex) { MessageBox.Show($"写入 {name} 失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } else { // 输入无效,可提示或恢复原值 MessageBox.Show($"请输入有效的数值", "输入错误", MessageBoxButton.OK, MessageBoxImage.Warning); // 重新从PLC读取该参数恢复原值 await UpdateSingleTextBox(tb, address); } }; } } /// /// 更新单个文本框的值(从PLC读取) /// private async Task UpdateSingleTextBox(TextBox tb, ushort address) { try { float val = await ReadFloatAsync(address); tb.Text = val.ToString("F3"); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"更新文本框失败: {ex.Message}"); } } private async void AutoRefreshTimer_Tick(object sender, EventArgs e) { // 只更新当前没有获得焦点的文本框 await Dispatcher.InvokeAsync(async () => { foreach (var kv in _textBoxMapping) { var tb = kv.Key; var address = kv.Value.address; if (!tb.IsFocused) { try { float val = await ReadFloatAsync(address); tb.Text = val.ToString("F3"); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"自动刷新 {kv.Value.paramName} 失败: {ex.Message}"); } } } }); } /// /// 一次性加载所有参数(用于窗口启动) /// private async Task LoadParameters() { foreach (var kv in _textBoxMapping) { await UpdateSingleTextBox(kv.Key, kv.Value.address); } } // 以下方法与之前相同,但保留用于写入(已在LostFocus中调用) 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); } private void Close_Click(object sender, RoutedEventArgs e) => Close(); } }