186 lines
8.6 KiB
C#
186 lines
8.6 KiB
C#
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();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 为所有文本框注册焦点事件,用于检测编辑状态
|
||
/// </summary>
|
||
private void RegisterTextBoxEvents()
|
||
{
|
||
// 查找当前窗口中所有文本框(可根据实际布局调整)
|
||
var textBoxes = FindVisualChildren<System.Windows.Controls.TextBox>(this);
|
||
foreach (var tb in textBoxes)
|
||
{
|
||
tb.GotFocus += (s, e) => _isEditing = true;
|
||
tb.LostFocus += (s, e) => _isEditing = false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 定时器事件:如果用户未在编辑,则刷新参数
|
||
/// </summary>
|
||
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<float> 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();
|
||
|
||
/// <summary>
|
||
/// 辅助方法:查找视觉树中的所有指定类型子元素
|
||
/// </summary>
|
||
private static IEnumerable<T> FindVisualChildren<T>(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<T>(child)) yield return childOfChild;
|
||
}
|
||
}
|
||
}
|
||
} |