This commit is contained in:
xyy
2026-03-24 19:33:35 +08:00
parent 9fd2f13b1c
commit 7bbf829224
15 changed files with 542 additions and 9 deletions

View File

@@ -25,6 +25,36 @@
public ushort PressureUpperLimit { get; set; } = 300;
public ushort PressureRate { get; set; } = 280;
public ushort PressureCoeff { get; set; } = 282;
public ushort HPCoeff1 { get; set; } = 3120;
public ushort LPCoeff1 { get; set; } = 3122;
public ushort HPCoeff2 { get; set; } = 3124;
public ushort LPCoeff2 { get; set; } = 3126;
public ushort HPCoeff3 { get; set; } = 3128;
public ushort LPCoeff3 { get; set; } = 3130;
public ushort LargeFlowCoeff1 { get; set; } = 3048;
public ushort SmallFlowCoeff1 { get; set; } = 380;
public ushort LargeFlowCoeff2 { get; set; } = 1218;
public ushort SmallFlowCoeff2 { get; set; } = 1318;
public ushort LargeFlowCoeff3 { get; set; } = 1418;
public ushort SmallFlowCoeff3 { get; set; } = 1468;
public ushort FlowModeRegister1 { get; set; } = 5; // 工位1流量模式 (0=大,1=小)
public ushort FlowModeRegister2 { get; set; } = 6;
public ushort FlowModeRegister3 { get; set; } = 7;
// 校准系数地址需与PLC约定
public ushort PressureCalibZero { get; set; } = 3200;
public ushort PressureCalibSpan { get; set; } = 3202;
public ushort FlowCalibZero { get; set; } = 3204;
public ushort FlowCalibSpan { get; set; } = 3206;
}
@@ -40,6 +70,10 @@
Task WriteRegisterAsync(ushort registerAddress, ushort value); // 写寄存器
Task<bool> ReadCoilAsync(ushort coilAddress); // 新增:读取线圈状态
Task<ushort[]> ReadHoldingRegistersAsync(ushort startAddress, ushort count);
Task WriteSingleRegisterAsync(ushort registerAddress, ushort value);
}
}

View File

@@ -5,5 +5,6 @@
public double Pressure { get; set; } // 压力
public double WetFlow { get; set; } // 湿膜流量(L/min)
public double DryFlow { get; set; } // 干膜流量(L/min)
}
}

View File

@@ -77,6 +77,20 @@ namespace MembranePoreTester.Communication
return result[0];
}
public async Task<ushort[]> ReadHoldingRegistersAsync(ushort startAddress, ushort count)
{
await EnsureConnectedAsync();
return await _master.ReadHoldingRegistersAsync(_config.SlaveId, startAddress, count);
}
public async Task WriteSingleRegisterAsync(ushort registerAddress, ushort value)
{
await EnsureConnectedAsync();
await _master.WriteSingleRegisterAsync(_config.SlaveId, registerAddress, value);
}
// 新增读取压力(根据工位)
public async Task<float> ReadPressureAsync(int stationId)
{

View File

@@ -85,6 +85,8 @@ namespace MembranePoreTester.ViewModels
public ICommand CalculateCommand { get; }
public ICommand GenerateReportCommand { get; }
public ICommand OpenPressureCalibCommand { get; }
public BubblePointViewModel()
{
@@ -96,6 +98,7 @@ namespace MembranePoreTester.ViewModels
ReadPlcCommand = new RelayCommand(async () => await ReadPlcAsync());
SaveCommand = new RelayCommand(SaveToDatabase);
ExportCommand = new RelayCommand(ExportToExcel);
OpenPressureCalibCommand = new RelayCommand(OpenPressureCalibration);
}
@@ -194,7 +197,21 @@ namespace MembranePoreTester.ViewModels
public ICommand ExportCommand { get; }
private void OpenPressureCalibration()
{
// 使用简单的输入框获取新零点系数和量程系数
string zeroStr = Microsoft.VisualBasic.Interaction.InputBox("请输入压力零点系数", "压力校准", "0");
string spanStr = Microsoft.VisualBasic.Interaction.InputBox("请输入压力量程系数", "压力校准", "1");
if (float.TryParse(zeroStr, out float zero) && float.TryParse(spanStr, out float span))
{
Task.Run(async () =>
{
await WriteFloatAsync(_plcConfig.PressureCalibZero, zero);
await WriteFloatAsync(_plcConfig.PressureCalibSpan, span);
MessageBox.Show("压力校准系数已写入", "完成");
});
}
}
private void ExportToExcel()
{

View File

@@ -127,8 +127,21 @@ namespace MembranePoreTester.ViewModels
MessageBox.Show($"写压力模式失败: {ex.Message}");
}
}
}
public MainViewModel()
{
for (int i = 1; i <= 3; i++)

View File

@@ -9,6 +9,7 @@ using System.Linq;
using System.Windows;
using System.Windows.Input;
namespace MembranePoreTester.ViewModels
{
public class PoreDistributionViewModel : ViewModelBase, IStationViewModel
@@ -122,6 +123,10 @@ namespace MembranePoreTester.ViewModels
ReadPlcCommand = new RelayCommand(async () => await ReadPlcAsync());
SaveCommand = new RelayCommand(SaveToDatabase);
ExportCommand = new RelayCommand(ExportToExcel);
OpenFlowCalibCommand = new RelayCommand(OpenFlowCalibration);
Record.DataPoints.CollectionChanged += (s, e) => UpdatePlot();
}
@@ -220,8 +225,20 @@ namespace MembranePoreTester.ViewModels
ReportGenerator.GeneratePoreDistributionReport(Record);
}
private void OpenFlowCalibration()
{
string zeroStr = Microsoft.VisualBasic.Interaction.InputBox("请输入流量零点系数", "流量校准", "0");
string spanStr = Microsoft.VisualBasic.Interaction.InputBox("请输入流量量程系数", "流量校准", "1");
if (float.TryParse(zeroStr, out float zero) && float.TryParse(spanStr, out float span))
{
Task.Run(async () =>
{
await WriteFloatAsync(_plcConfig.FlowCalibZero, zero);
await WriteFloatAsync(_plcConfig.FlowCalibSpan, span);
MessageBox.Show("流量校准系数已写入", "完成");
});
}
}
private int _stationId;
public int StationId
@@ -301,7 +318,7 @@ namespace MembranePoreTester.ViewModels
public ICommand SaveCommand { get; }
public ICommand OpenFlowCalibCommand { get; }
public ICommand ExportCommand { get; }
@@ -330,6 +347,63 @@ namespace MembranePoreTester.ViewModels
set => SetProperty(ref _testMode, value);
}
private int _selectedFlowModeIndex; // 0=大流量1=小流量
public int SelectedFlowModeIndex
{
get => _selectedFlowModeIndex;
set
{
if (SetProperty(ref _selectedFlowModeIndex, value))
{
ushort reg = StationId switch
{
1 => _plcConfig.FlowModeRegister1,
2 => _plcConfig.FlowModeRegister2,
3 => _plcConfig.FlowModeRegister3,
_ => 0
};
Task.Run(async () => await _plcService.WriteSingleRegisterAsync(reg, (ushort)value));
}
}
}
private OxyPlot.PlotModel _plotModel;
public OxyPlot.PlotModel PlotModel
{
get => _plotModel;
set => SetProperty(ref _plotModel, value);
}
private void UpdatePlot()
{
var sorted = Record.DataPoints.OrderBy(p => p.Pressure).ToList();
if (sorted.Count == 0)
{
PlotModel = null;
return;
}
var model = new OxyPlot.PlotModel { Title = "流量-压力曲线" };
model.Axes.Add(new OxyPlot.Axes.LinearAxis { Position = OxyPlot.Axes.AxisPosition.Bottom, Title = "压力" });
model.Axes.Add(new OxyPlot.Axes.LinearAxis { Position = OxyPlot.Axes.AxisPosition.Left, Title = "流量 (L/min)" });
var wetSeries = new OxyPlot.Series.LineSeries { Title = "湿膜流量", Color = OxyPlot.OxyColors.Blue, MarkerType = OxyPlot.MarkerType.Circle };
var drySeries = new OxyPlot.Series.LineSeries { Title = "干膜流量", Color = OxyPlot.OxyColors.Red, MarkerType = OxyPlot.MarkerType.Circle };
foreach (var dp in sorted)
{
wetSeries.Points.Add(new OxyPlot.DataPoint(dp.Pressure, dp.WetFlow));
drySeries.Points.Add(new OxyPlot.DataPoint(dp.Pressure, dp.DryFlow));
}
model.Series.Add(wetSeries);
model.Series.Add(drySeries);
PlotModel = model;
}
}
}

View File

@@ -1,4 +1,5 @@
using System.ComponentModel;
using MembranePoreTester.Communication;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace MembranePoreTester.ViewModels
@@ -19,5 +20,15 @@ namespace MembranePoreTester.ViewModels
OnPropertyChanged(propertyName);
return true;
}
protected 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 App.PlcService.WriteSingleRegisterAsync(address, high);
await App.PlcService.WriteSingleRegisterAsync((ushort)(address + 1), low);
}
}
}

View File

@@ -78,6 +78,7 @@
<!-- 底部按钮 -->
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Content="压力校准" Command="{Binding OpenPressureCalibCommand}" Padding="15,8" Margin="5"/>
<Button Content="保存到历史" Command="{Binding SaveCommand}" Padding="15,8" Margin="5"/>
<Button Content="生成报告" Command="{Binding GenerateReportCommand}" Padding="15,8" Margin="5"/>
<Button Content="导出Excel" Command="{Binding ExportCommand}" Padding="15,8" Margin="5"/>

View File

@@ -5,7 +5,7 @@
xmlns:viewModels="clr-namespace:MembranePoreTester.ViewModels"
Title="膜孔径测试系统 (GB/T 32361-2015)"
Width="1024" Height="768"
WindowStartupLocation="CenterScreen">
WindowStartupLocation="CenterScreen" KeyDown="Window_KeyDown">
<Window.DataContext>
<viewModels:MainViewModel />
</Window.DataContext>

View File

@@ -1,5 +1,6 @@
using MembranePoreTester.ViewModels;
using System.Windows;
using System.Windows.Input;
namespace MembranePoreTester.Views
{
@@ -41,5 +42,16 @@ namespace MembranePoreTester.Views
var historyWin = new HistoryWindow { SelectedStation = currentStation };
historyWin.ShowDialog();
}
private void Window_KeyDown(object sender, KeyEventArgs e)
{
if (Keyboard.Modifiers == ModifierKeys.Control && e.Key == Key.P)
{
var win = new ParameterWindow();
win.Owner = this;
win.ShowDialog();
}
}
}
}

120
Views/ParameterWindow.xaml Normal file
View File

@@ -0,0 +1,120 @@
<Window x:Class="MembranePoreTester.Views.ParameterWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="运维参数设置" Height="600" Width="800"
WindowStartupLocation="CenterOwner">
<ScrollViewer>
<StackPanel Margin="10">
<GroupBox Header="加压控制">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0">加压上限(D300):</Label>
<TextBox x:Name="txtPressureUpper" Grid.Row="0" Grid.Column="1"/>
<Label Grid.Row="1" Grid.Column="0">加压速率(D280):</Label>
<TextBox x:Name="txtPressureRate" Grid.Row="1" Grid.Column="1"/>
<Label Grid.Row="2" Grid.Column="0">加压系数(D282):</Label>
<TextBox x:Name="txtPressureCoeff" Grid.Row="2" Grid.Column="1"/>
</Grid>
</GroupBox>
<GroupBox Header="高压/低压系数">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
<!-- 预留空白 -->
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" FontWeight="Bold">工位</Label>
<Label Grid.Row="0" Grid.Column="1" FontWeight="Bold">高压系数</Label>
<Label Grid.Row="0" Grid.Column="2" FontWeight="Bold">低压系数</Label>
<Label Grid.Row="1" Grid.Column="0">工位1</Label>
<TextBox x:Name="txtHPCoeff1" Grid.Row="1" Grid.Column="1" Width="80" HorizontalAlignment="Left"/>
<TextBox x:Name="txtLPCoeff1" Grid.Row="1" Grid.Column="2" Width="80" HorizontalAlignment="Left"/>
<Label Grid.Row="2" Grid.Column="0">工位2</Label>
<TextBox x:Name="txtHPCoeff2" Grid.Row="2" Grid.Column="1" Width="80" HorizontalAlignment="Left"/>
<TextBox x:Name="txtLPCoeff2" Grid.Row="2" Grid.Column="2" Width="80" HorizontalAlignment="Left"/>
<Label Grid.Row="3" Grid.Column="0">工位3</Label>
<TextBox x:Name="txtHPCoeff3" Grid.Row="3" Grid.Column="1" Width="80" HorizontalAlignment="Left"/>
<TextBox x:Name="txtLPCoeff3" Grid.Row="3" Grid.Column="2" Width="80" HorizontalAlignment="Left"/>
</Grid>
</GroupBox>
<GroupBox Header="流量系数">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" FontWeight="Bold">工位</Label>
<Label Grid.Row="0" Grid.Column="1" FontWeight="Bold">大流量系数</Label>
<Label Grid.Row="0" Grid.Column="2" FontWeight="Bold">小流量系数</Label>
<Label Grid.Row="1" Grid.Column="0">工位1</Label>
<TextBox x:Name="txtLargeFlow1" Grid.Row="1" Grid.Column="1"/>
<TextBox x:Name="txtSmallFlow1" Grid.Row="1" Grid.Column="2"/>
<Label Grid.Row="2" Grid.Column="0">工位2</Label>
<TextBox x:Name="txtLargeFlow2" Grid.Row="2" Grid.Column="1"/>
<TextBox x:Name="txtSmallFlow2" Grid.Row="2" Grid.Column="2"/>
<Label Grid.Row="3" Grid.Column="0">工位3</Label>
<TextBox x:Name="txtLargeFlow3" Grid.Row="3" Grid.Column="1"/>
<TextBox x:Name="txtSmallFlow3" Grid.Row="3" Grid.Column="2"/>
</Grid>
</GroupBox>
<GroupBox Header="校准参数">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0">压力零点校准:</Label>
<TextBox x:Name="txtPressureZero" Grid.Row="0" Grid.Column="1"/>
<Label Grid.Row="1" Grid.Column="0">压力量程校准:</Label>
<TextBox x:Name="txtPressureSpan" Grid.Row="1" Grid.Column="1"/>
<Label Grid.Row="2" Grid.Column="0">流量零点校准:</Label>
<TextBox x:Name="txtFlowZero" Grid.Row="2" Grid.Column="1"/>
<Label Grid.Row="3" Grid.Column="0">流量量程校准:</Label>
<TextBox x:Name="txtFlowSpan" Grid.Row="3" Grid.Column="1"/>
</Grid>
</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>
</ScrollViewer>
</Window>

View File

@@ -0,0 +1,186 @@
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;
}
}
}
}

View File

@@ -1,6 +1,7 @@
<UserControl x:Class="MembranePoreTester.Views.PoreDistributionView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:oxy="http://oxyplot.org/wpf"
xmlns:conv="clr-namespace:MembranePoreTester.Converters">
<UserControl.Resources>
<conv:InverseBooleanConverter x:Key="InverseBooleanConverter"/>
@@ -69,12 +70,17 @@
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="0,0,0,5">
<Button Content="添加行" Command="{Binding AddDataPointCommand}" Padding="10,5" Margin="5"/>
<Button Content="删除行" Command="{Binding RemoveDataPointCommand}" Padding="10,5" Margin="5"/>
<!-- 新增:模式选择 -->
<ComboBox SelectedItem="{Binding TestMode}" Width="80" Margin="5">
<ComboBoxItem Content="湿膜" IsSelected="True"/>
<ComboBoxItem Content="干膜"/>
</ComboBox>
<ComboBox SelectedIndex="{Binding SelectedFlowModeIndex}" Width="100" Margin="5">
<ComboBoxItem IsSelected="True" Content="大流量"/>
<ComboBoxItem Content="小流量"/>
</ComboBox>
<Button Content="读取PLC" Command="{Binding ReadPlcCommand}" Padding="10,5" Margin="5"/>
</StackPanel>
<DataGrid ItemsSource="{Binding Record.DataPoints}" AutoGenerateColumns="False" CanUserAddRows="False">
@@ -91,7 +97,7 @@
<!-- 右侧简易数据展示可用图表库此处仅用ListBox预览 -->
<GroupBox Grid.Column="2" Header="数据预览" Margin="5,0,0,0">
<ListBox ItemsSource="{Binding Record.DataPoints}">
<!--<ListBox ItemsSource="{Binding Record.DataPoints}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
@@ -103,7 +109,10 @@
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ListBox>-->
<GroupBox Grid.Column="2" Header="流量-压力曲线" Margin="5,0,0,0">
<oxy:PlotView Model="{Binding PlotModel}" Height="300" Margin="5"/>
</GroupBox>
</GroupBox>
</Grid>
@@ -122,6 +131,7 @@
<TextBlock Text="{Binding RangePercentage, StringFormat={}{0:F1}%}" FontSize="16" VerticalAlignment="Center" Margin="10,0"/>
</StackPanel>
</GroupBox>
<Button Content="流量校准" Command="{Binding OpenFlowCalibCommand}" Padding="15,8" Margin="5"/>
<Button Content="生成报告" Command="{Binding GenerateReportCommand}" Padding="15,8" Margin="5"/>
<Button Content="保存到历史" Command="{Binding SaveCommand}" Padding="15,8" Margin="5"/>
<Button Content="导出Excel" Command="{Binding ExportCommand}" Padding="15,8" Margin="5"/>

View File

@@ -15,6 +15,44 @@
"WetFlowRegister": 2,
"DryFlowRegister": 4,
"WetFlowFactor": 1.0,
"DryFlowFactor": 1.0
"DryFlowFactor": 1.0,
"PressureUpperLimit": 300,
"PressureRate": 280,
"PressureCoeff": 282,
"HPCoeff1": 3120,
"LPCoeff1": 3122,
"HPCoeff2": 3124,
"LPCoeff2": 3126,
"HPCoeff3": 3128,
"LPCoeff3": 3130,
"LargeFlowCoeff1": 3048,
"SmallFlowCoeff1": 380,
"LargeFlowCoeff2": 1218,
"SmallFlowCoeff2": 1318,
"LargeFlowCoeff3": 1418,
"SmallFlowCoeff3": 1468,
"FlowModeRegister1": 5,
"FlowModeRegister2": 6,
"FlowModeRegister3": 7,
"PressureCalibZero": 3200,
"PressureCalibSpan": 3202,
"FlowCalibZero": 3204,
"FlowCalibSpan": 3206
}
}

View File

@@ -15,7 +15,9 @@
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.3" />
<PackageReference Include="Microsoft.VisualBasic" Version="10.3.0" />
<PackageReference Include="NModbus4.NetCore" Version="4.0.0" />
<PackageReference Include="OxyPlot.Wpf" Version="2.2.0" />
<PackageReference Include="SunnyUI" Version="3.9.3" />
</ItemGroup>