This commit is contained in:
xyy
2026-03-26 19:43:52 +08:00
parent 7bbf829224
commit b161e884e7
14 changed files with 452 additions and 268 deletions

View File

@@ -58,7 +58,11 @@
<TextBox Text="{Binding Record.LiquidManufacturer}" Margin="5"/>-->
</StackPanel>
</GroupBox>
<GroupBox Header="当前压力" Margin="0,0,0,10">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Record.BubbleCurrentPressure}" Width="150" Margin="5"/>
</StackPanel>
</GroupBox>
<GroupBox Header="泡点压力" Margin="0,0,0,10">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Record.BubblePointPressure}" Width="150" Margin="5"/>

View File

@@ -29,7 +29,7 @@
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="5">
<ComboBox SelectedItem="{Binding HighLowPressure}" Width="80" Margin="5">
<ComboBoxItem IsSelected="True">低压</ComboBoxItem>
<ComboBoxItem>高压</ComboBoxItem>
<ComboBoxItem >高压</ComboBoxItem>
</ComboBox>
<Button Content="启动" Command="{Binding StartCommand}" Width="80" Margin="5"/>
<Button Content="停止" Command="{Binding StopCommand}" Width="80" Margin="5"/>

View File

@@ -87,7 +87,7 @@
</Grid>
</GroupBox>
<GroupBox Header="校准参数">
<!--<GroupBox Header="校准参数">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
@@ -108,11 +108,11 @@
<Label Grid.Row="3" Grid.Column="0">流量量程校准:</Label>
<TextBox x:Name="txtFlowSpan" Grid.Row="3" Grid.Column="1"/>
</Grid>
</GroupBox>
</GroupBox>-->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,20,0,0">
<!--<Button Content="读取参数" Click="ReadParameters_Click" Width="100" Margin="5"/>-->
<Button Content="写入参数" Click="WriteParameters_Click" Width="100" Margin="5"/>
<Button Content="关闭" Click="Close_Click" Width="100" Margin="5"/>
</StackPanel>
</StackPanel>

View File

@@ -1,10 +1,12 @@
using MembranePoreTester.Communication;
using MembranePoreTester.ViewModels;
using System;
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
{
@@ -13,7 +15,9 @@ namespace MembranePoreTester.Views
private readonly IPlcService _plcService;
private readonly PlcConfiguration _config;
private DispatcherTimer _autoRefreshTimer;
private bool _isEditing = false; // 用户是否正在编辑任何文本框
// 用于快速查找文本框对应的写入地址和方法(可根据需要扩展)
private readonly Dictionary<TextBox, (ushort address, string paramName)> _textBoxMapping;
public ParameterWindow()
{
@@ -21,12 +25,39 @@ namespace MembranePoreTester.Views
_plcService = App.PlcService;
_config = App.PlcConfig;
// 为所有文本框注册焦点事件在XAML中设置事件或在此处统一查找
// 初始化文本框到寄存器地址的映射
_textBoxMapping = new Dictionary<TextBox, (ushort, string)>
{
[txtPressureUpper] = (_config.PressureUpperLimit, "加压上限"),
[txtPressureRate] = (_config.PressureRate, "加压速率"),
[txtPressureCoeff] = (_config.PressureCoeff, "加压系数"),
[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) =>
{
// 启动自动刷新定时器每秒1次
// 启动定时器
_autoRefreshTimer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(1)
@@ -35,152 +66,109 @@ namespace MembranePoreTester.Views
_autoRefreshTimer.Start();
// 首次加载时读取一次
await ReadParametersAsync();
await LoadParameters();
};
Closed += (s, e) => _autoRefreshTimer?.Stop();
}
/// <summary>
/// 为所有文本框注册焦点事件,用于检测编辑状态
/// </summary>
private void RegisterTextBoxEvents()
{
// 查找当前窗口中所有文本框(可根据实际布局调整)
var textBoxes = FindVisualChildren<System.Windows.Controls.TextBox>(this);
foreach (var tb in textBoxes)
foreach (var tb in _textBoxMapping.Keys)
{
tb.GotFocus += (s, e) => _isEditing = true;
tb.LostFocus += (s, e) => _isEditing = false;
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);
}
};
}
}
/// <summary>
/// 定时器事件:如果用户未在编辑,则刷新参数
/// 更新单个文本框的值从PLC读取
/// </summary>
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)
{
if (!_isEditing)
// 只更新当前没有获得焦点的文本框
await Dispatcher.InvokeAsync(async () =>
{
await ReadParametersAsync();
}
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 ReadParametersAsync()
/// <summary>
/// 一次性加载所有参数(用于窗口启动)
/// </summary>
private async Task LoadParameters()
{
try
foreach (var kv in _textBoxMapping)
{
// 加压控制
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}");
await UpdateSingleTextBox(kv.Key, kv.Value.address);
}
}
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;
// 以下方法与之前相同但保留用于写入已在LostFocus中调用
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);
return ((ModbusTcpPlcService)_plcService).UshortToFloat(registers[1], registers[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);
await ((ModbusTcpPlcService)_plcService).WriteMultipleRegistersAsync(address, value);
}
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;
}
}
}
}