using MembranePoreTester.Communication; using MembranePoreTester.ViewModels; using System; using System.Windows; using System.Windows.Input; using System.Windows.Media; using System.Windows.Threading; namespace MembranePoreTester.Views { public partial class ParameterWindow : Window { private readonly IPlcService _plcService; private readonly PlcConfiguration _config; private DispatcherTimer _autoRefreshTimer; private bool _isEditing = false; // 用户是否正在编辑任何文本框 public ParameterWindow() { InitializeComponent(); _plcService = App.PlcService; _config = App.PlcConfig; // 为所有文本框注册焦点事件(在XAML中设置事件,或在此处统一查找) RegisterTextBoxEvents(); Loaded += async (s, e) => { // 启动自动刷新定时器(每秒1次) _autoRefreshTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) }; _autoRefreshTimer.Tick += AutoRefreshTimer_Tick; _autoRefreshTimer.Start(); // 首次加载时读取一次 await ReadParametersAsync(); }; Closed += (s, e) => _autoRefreshTimer?.Stop(); } /// /// 为所有文本框注册焦点事件,用于检测编辑状态 /// private void RegisterTextBoxEvents() { // 查找当前窗口中所有文本框(可根据实际布局调整) var textBoxes = FindVisualChildren(this); foreach (var tb in textBoxes) { tb.GotFocus += (s, e) => _isEditing = true; tb.LostFocus += (s, e) => _isEditing = false; } } /// /// 定时器事件:如果用户未在编辑,则刷新参数 /// private async void AutoRefreshTimer_Tick(object sender, EventArgs e) { if (!_isEditing) { await ReadParametersAsync(); } } private async Task ReadParametersAsync() { try { // 加压控制 txtPressureUpper.Text = (await ReadFloatAsync(_config.PressureUpperLimit)).ToString("F3"); txtPressureRate.Text = (await ReadFloatAsync(_config.PressureRate)).ToString("F3"); txtPressureCoeff.Text = (await ReadFloatAsync(_config.PressureCoeff)).ToString("F3"); // 高压/低压系数 txtHPCoeff1.Text = (await ReadFloatAsync(_config.HPCoeff1)).ToString("F3"); txtLPCoeff1.Text = (await ReadFloatAsync(_config.LPCoeff1)).ToString("F3"); txtHPCoeff2.Text = (await ReadFloatAsync(_config.HPCoeff2)).ToString("F3"); txtLPCoeff2.Text = (await ReadFloatAsync(_config.LPCoeff2)).ToString("F3"); txtHPCoeff3.Text = (await ReadFloatAsync(_config.HPCoeff3)).ToString("F3"); txtLPCoeff3.Text = (await ReadFloatAsync(_config.LPCoeff3)).ToString("F3"); // 流量系数 txtLargeFlow1.Text = (await ReadFloatAsync(_config.LargeFlowCoeff1)).ToString("F3"); txtSmallFlow1.Text = (await ReadFloatAsync(_config.SmallFlowCoeff1)).ToString("F3"); txtLargeFlow2.Text = (await ReadFloatAsync(_config.LargeFlowCoeff2)).ToString("F3"); txtSmallFlow2.Text = (await ReadFloatAsync(_config.SmallFlowCoeff2)).ToString("F3"); txtLargeFlow3.Text = (await ReadFloatAsync(_config.LargeFlowCoeff3)).ToString("F3"); txtSmallFlow3.Text = (await ReadFloatAsync(_config.SmallFlowCoeff3)).ToString("F3"); // 校准参数 txtPressureZero.Text = (await ReadFloatAsync(_config.PressureCalibZero)).ToString("F3"); txtPressureSpan.Text = (await ReadFloatAsync(_config.PressureCalibSpan)).ToString("F3"); txtFlowZero.Text = (await ReadFloatAsync(_config.FlowCalibZero)).ToString("F3"); txtFlowSpan.Text = (await ReadFloatAsync(_config.FlowCalibSpan)).ToString("F3"); } catch (Exception ex) { // 静默处理,避免频繁弹窗干扰(可记录日志) System.Diagnostics.Debug.WriteLine($"自动读取参数失败: {ex.Message}"); } } private async void WriteParameters_Click(object sender, RoutedEventArgs e) { try { // 加压控制 await WriteFloatAsync(_config.PressureUpperLimit, ParseFloat(txtPressureUpper.Text)); await WriteFloatAsync(_config.PressureRate, ParseFloat(txtPressureRate.Text)); await WriteFloatAsync(_config.PressureCoeff, ParseFloat(txtPressureCoeff.Text)); // 高压/低压系数 await WriteFloatAsync(_config.HPCoeff1, ParseFloat(txtHPCoeff1.Text)); await WriteFloatAsync(_config.LPCoeff1, ParseFloat(txtLPCoeff1.Text)); await WriteFloatAsync(_config.HPCoeff2, ParseFloat(txtHPCoeff2.Text)); await WriteFloatAsync(_config.LPCoeff2, ParseFloat(txtLPCoeff2.Text)); await WriteFloatAsync(_config.HPCoeff3, ParseFloat(txtHPCoeff3.Text)); await WriteFloatAsync(_config.LPCoeff3, ParseFloat(txtLPCoeff3.Text)); // 流量系数 await WriteFloatAsync(_config.LargeFlowCoeff1, ParseFloat(txtLargeFlow1.Text)); await WriteFloatAsync(_config.SmallFlowCoeff1, ParseFloat(txtSmallFlow1.Text)); await WriteFloatAsync(_config.LargeFlowCoeff2, ParseFloat(txtLargeFlow2.Text)); await WriteFloatAsync(_config.SmallFlowCoeff2, ParseFloat(txtSmallFlow2.Text)); await WriteFloatAsync(_config.LargeFlowCoeff3, ParseFloat(txtLargeFlow3.Text)); await WriteFloatAsync(_config.SmallFlowCoeff3, ParseFloat(txtSmallFlow3.Text)); // 校准参数 await WriteFloatAsync(_config.PressureCalibZero, ParseFloat(txtPressureZero.Text)); await WriteFloatAsync(_config.PressureCalibSpan, ParseFloat(txtPressureSpan.Text)); await WriteFloatAsync(_config.FlowCalibZero, ParseFloat(txtFlowZero.Text)); await WriteFloatAsync(_config.FlowCalibSpan, ParseFloat(txtFlowSpan.Text)); MessageBox.Show("参数写入成功"); } catch (Exception ex) { MessageBox.Show($"写入参数失败: {ex.Message}"); } } private float ParseFloat(string text) => float.TryParse(text, out var val) ? val : 0; private async Task ReadFloatAsync(ushort address) { var registers = await ((ModbusTcpPlcService)_plcService).ReadHoldingRegistersAsync(address, 2); byte[] bytes = new byte[4]; bytes[0] = (byte)(registers[0] >> 8); bytes[1] = (byte)(registers[0] & 0xFF); bytes[2] = (byte)(registers[1] >> 8); bytes[3] = (byte)(registers[1] & 0xFF); if (BitConverter.IsLittleEndian) Array.Reverse(bytes); return BitConverter.ToSingle(bytes, 0); } private async Task WriteFloatAsync(ushort address, float value) { byte[] bytes = BitConverter.GetBytes(value); if (BitConverter.IsLittleEndian) Array.Reverse(bytes); ushort high = (ushort)((bytes[0] << 8) | bytes[1]); ushort low = (ushort)((bytes[2] << 8) | bytes[3]); await ((ModbusTcpPlcService)_plcService).WriteRegisterAsync(address, high); await ((ModbusTcpPlcService)_plcService).WriteRegisterAsync((ushort)(address + 1), low); } private void Close_Click(object sender, RoutedEventArgs e) => Close(); /// /// 辅助方法:查找视觉树中的所有指定类型子元素 /// private static IEnumerable FindVisualChildren(DependencyObject depObj) where T : DependencyObject { if (depObj == null) yield break; for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++) { var child = VisualTreeHelper.GetChild(depObj, i); if (child is T t) yield return t; foreach (var childOfChild in FindVisualChildren(child)) yield return childOfChild; } } } }