This commit is contained in:
xyy
2026-02-27 16:58:02 +08:00
parent 82abc41dbc
commit 63c81b48a1
9 changed files with 237 additions and 3 deletions

View File

@@ -1,8 +1,37 @@
using System.Windows;
using System;
using System.IO;
using System.Windows;
using MembranePoreTester.Communication;
using Microsoft.Extensions.Configuration;
namespace MembranePoreTester
{
public partial class App : Application
{
public static IPlcService PlcService { get; private set; }
public static PlcConfiguration PlcConfig { get; private set; }
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
var configuration = builder.Build();
PlcConfig = configuration.GetSection("PlcSettings").Get<PlcConfiguration>();
if (PlcConfig == null)
throw new InvalidOperationException("PLC settings missing in appsettings.json");
PlcService = new ModbusTcpPlcService(PlcConfig);
}
protected override void OnExit(ExitEventArgs e)
{
(PlcService as IDisposable)?.Dispose();
base.OnExit(e);
}
}
}

View File

@@ -0,0 +1,27 @@
namespace MembranePoreTester.Communication
{
public class PlcConfiguration
{
public string IpAddress { get; set; }
public int Port { get; set; }
public byte SlaveId { get; set; } = 1; // 从站地址默认1
public ushort PressureRegister { get; set; }
public ushort WetFlowRegister { get; set; }
public ushort DryFlowRegister { get; set; }
public double PressureFactor { get; set; } = 1.0;
public double WetFlowFactor { get; set; } = 1.0;
public double DryFlowFactor { get; set; } = 1.0;
}
public interface IPlcService
{
Task<float> ReadPressureAsync();
Task<float> ReadWetFlowAsync();
Task<float> ReadDryFlowAsync();
}
}

View File

@@ -0,0 +1,65 @@
using System;
using System.Net.Sockets;
using System.Threading.Tasks;
using Modbus.Device;
namespace MembranePoreTester.Communication
{
public class ModbusTcpPlcService : IPlcService, IDisposable
{
private readonly PlcConfiguration _config;
private TcpClient _tcpClient;
private IModbusMaster _master;
public ModbusTcpPlcService(PlcConfiguration config)
{
_config = config;
}
private async Task EnsureConnectedAsync()
{
if (_tcpClient == null || !_tcpClient.Connected)
{
_tcpClient = new TcpClient();
await _tcpClient.ConnectAsync(_config.IpAddress, _config.Port);
_master = ModbusIpMaster.CreateIp(_tcpClient);
}
}
// 读取两个连续的保持寄存器转换为32位浮点数假设大端模式
private async Task<float> ReadFloatAsync(ushort startAddress)
{
await EnsureConnectedAsync();
// 读取两个寄存器(从站地址由配置指定)
ushort[] registers = await _master.ReadHoldingRegistersAsync(_config.SlaveId, startAddress, 2);
// 将两个16位寄存器合并为32位浮点数大端
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);
}
public async Task<float> ReadPressureAsync() =>
await ReadFloatAsync(_config.PressureRegister) * (float)_config.PressureFactor;
public async Task<float> ReadWetFlowAsync() =>
await ReadFloatAsync(_config.WetFlowRegister) * (float)_config.WetFlowFactor;
public async Task<float> ReadDryFlowAsync() =>
await ReadFloatAsync(_config.DryFlowRegister) * (float)_config.DryFlowFactor;
public void Dispose()
{
_master?.Dispose();
_tcpClient?.Close();
}
}
}

View File

@@ -1,6 +1,8 @@
using MembranePoreTester.Helpers;
using MembranePoreTester.Communication;
using MembranePoreTester.Helpers;
using MembranePoreTester.Models;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;
namespace MembranePoreTester.ViewModels
@@ -12,6 +14,13 @@ namespace MembranePoreTester.ViewModels
private bool _isCustomLiquid;
private double _customSurfaceTension = 30.0;
// 添加字段
private readonly IPlcService _plcService;
private readonly PlcConfiguration _plcConfig;
public ICommand ReadPlcCommand { get; }
public BubblePointRecord Record
{
get => _record;
@@ -77,9 +86,28 @@ namespace MembranePoreTester.ViewModels
public BubblePointViewModel()
{
_plcService = App.PlcService;
_plcConfig = App.PlcConfig;
CalculateCommand = new RelayCommand(Calculate);
GenerateReportCommand = new RelayCommand(GenerateReport);
SelectedLiquid = Liquids[0];
ReadPlcCommand = new RelayCommand(async () => await ReadPlcAsync());
}
private async Task ReadPlcAsync()
{
try
{
float rawPressure = await _plcService.ReadPressureAsync();
Record.BubblePointPressure = rawPressure * _plcConfig.PressureFactor;
OnPropertyChanged(nameof(Record.BubblePointPressure));
}
catch (Exception ex)
{
MessageBox.Show($"读取PLC失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}

View File

@@ -1,8 +1,10 @@
using MembranePoreTester.Helpers;
using MembranePoreTester.Communication;
using MembranePoreTester.Helpers;
using MembranePoreTester.Models;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Input;
namespace MembranePoreTester.ViewModels
@@ -17,6 +19,23 @@ namespace MembranePoreTester.ViewModels
private double _upperPore = 0.8;
private double _rangePercentage;
// 添加字段
private readonly IPlcService _plcService;
private readonly PlcConfiguration _plcConfig;
private DataPoint _selectedDataPoint;
// 添加属性
public DataPoint SelectedDataPoint
{
get => _selectedDataPoint;
set => SetProperty(ref _selectedDataPoint, value);
}
public ICommand ReadPlcCommand { get; }
public PoreDistributionRecord Record
{
get => _record;
@@ -88,12 +107,52 @@ namespace MembranePoreTester.ViewModels
public PoreDistributionViewModel()
{
_plcService = App.PlcService;
_plcConfig = App.PlcConfig;
AddDataPointCommand = new RelayCommand(AddDataPoint);
RemoveDataPointCommand = new RelayCommand(RemoveDataPoint, () => Record.DataPoints.Any());
CalculateCommand = new RelayCommand(Calculate);
GenerateReportCommand = new RelayCommand(GenerateReport);
SelectedLiquid = Liquids[0];
Record.PressureUnit = PressureUnits[0]; // 默认 "Pa"
ReadPlcCommand = new RelayCommand(async () => await ReadPlcAsync());
}
private async Task ReadPlcAsync()
{
try
{
float rawPressure = await _plcService.ReadPressureAsync();
float rawWetFlow = await _plcService.ReadWetFlowAsync();
float rawDryFlow = await _plcService.ReadDryFlowAsync();
double pressure = rawPressure * _plcConfig.PressureFactor;
double wetFlow = rawWetFlow * _plcConfig.WetFlowFactor;
double dryFlow = rawDryFlow * _plcConfig.DryFlowFactor;
if (SelectedDataPoint != null)
{
SelectedDataPoint.Pressure = pressure;
SelectedDataPoint.WetFlow = wetFlow;
SelectedDataPoint.DryFlow = dryFlow;
}
else
{
Record.DataPoints.Add(new DataPoint
{
Pressure = pressure,
WetFlow = wetFlow,
DryFlow = dryFlow
});
}
}
catch (Exception ex)
{
MessageBox.Show($"读取PLC失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void AddDataPoint()

View File

@@ -62,6 +62,8 @@
<GroupBox Header="泡点压力" Margin="0,0,0,10">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Record.BubblePointPressure}" Width="150" Margin="5"/>
<Button Content="读取PLC" Command="{Binding ReadPlcCommand}" Padding="10,5" Margin="5"/>
<ComboBox ItemsSource="{Binding PressureUnits}" SelectedItem="{Binding Record.PressureUnit}" Width="80" Margin="5"/>
<Button Content="计算最大孔径" Command="{Binding CalculateCommand}" Padding="10,5" Margin="5"/>
</StackPanel>

View File

@@ -69,6 +69,7 @@
<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"/>
<Button Content="读取PLC" Command="{Binding ReadPlcCommand}" Padding="10,5" Margin="5"/>
</StackPanel>
<DataGrid ItemsSource="{Binding Record.DataPoints}" AutoGenerateColumns="False" CanUserAddRows="False">
<DataGrid.Columns>

13
appsettings.json Normal file
View File

@@ -0,0 +1,13 @@
{
"PlcSettings": {
"IpAddress": "192.168.1.100",
"Port": 502,
"SlaveId": 1,
"PressureRegister": 0,
"WetFlowRegister": 2,
"DryFlowRegister": 4,
"PressureFactor": 1.0,
"WetFlowFactor": 1.0,
"DryFlowFactor": 1.0
}
}

View File

@@ -10,6 +10,16 @@
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<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="NModbus4.NetCore" Version="4.0.0" />
</ItemGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>