This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<TargetFramework>net10.0-windows7.0</TargetFramework>
|
||||
<RootNamespace>ASTM_D7896_19瞬态热线法</RootNamespace>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
@@ -11,6 +11,12 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" />
|
||||
<PackageReference Include="EPPlus" Version="7.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.8" />
|
||||
<PackageReference Include="NModbus4.NetCore" Version="4.0.0" />
|
||||
<PackageReference Include="OxyPlot.Wpf" Version="2.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -20,4 +26,8 @@
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Models\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
4
App.xaml
4
App.xaml
@@ -1,8 +1,8 @@
|
||||
<Application x:Class="ASTM_D7896_Tester.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="clr-namespace:ASTM_D7896_Tester.Converters"
|
||||
StartupUri="Views/MainWindow.xaml">
|
||||
xmlns:converters="clr-namespace:ASTM_D7896_Tester.Converters"
|
||||
>
|
||||
<Application.Resources>
|
||||
<converters:InverseBooleanConverter x:Key="InverseBooleanConverter"/>
|
||||
|
||||
|
||||
61
App.xaml.cs
61
App.xaml.cs
@@ -1,14 +1,71 @@
|
||||
using System.Configuration;
|
||||
using ASTM_D7896_Tester.Models;
|
||||
using ASTM_D7896_Tester.Services;
|
||||
using ASTM_D7896_Tester.Views;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using OfficeOpenXml;
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
|
||||
namespace ASTM_D7896_Tester.Views
|
||||
namespace ASTM_D7896_Tester
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
public static IPlcService PlcService { get; private set; }
|
||||
public static AppConfig PlcConfig { get; private set; }
|
||||
protected override async void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
|
||||
|
||||
base.OnStartup(e);
|
||||
|
||||
// 防止在登录窗口关闭时应用程序因没有窗口而自动退出
|
||||
ShutdownMode = ShutdownMode.OnExplicitShutdown;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
|
||||
|
||||
var configuration = builder.Build();
|
||||
|
||||
// appsettings.json in this project keeps PLC settings at the root of the file
|
||||
// (not under a "PlcSettings" section). Bind the entire configuration to AppConfig.
|
||||
PlcConfig = configuration.Get<AppConfig>() ?? new AppConfig();
|
||||
|
||||
PlcService = new PlcService(PlcConfig);
|
||||
|
||||
var plcService = App.PlcService as PlcService;
|
||||
try
|
||||
{
|
||||
await plcService.EnsureConnectedAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"PLC 连接失败:{ex.Message}");
|
||||
}
|
||||
|
||||
// 启动主窗口,设置为应用程序的主窗口并恢复默认的退出模式
|
||||
var mainWindow = new MainWindow();
|
||||
MainWindow = mainWindow;
|
||||
ShutdownMode = ShutdownMode.OnMainWindowClose;
|
||||
mainWindow.Show();
|
||||
}
|
||||
|
||||
protected override void OnExit(ExitEventArgs e)
|
||||
{
|
||||
(PlcService as IDisposable)?.Dispose();
|
||||
base.OnExit(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ public class AppConfig
|
||||
public PlcRegisterAddresses PlcRegisterAddresses { get; set; } = new();
|
||||
public TestParameters TestParameters { get; set; } = new();
|
||||
public AppSettings AppSettings { get; set; } = new();
|
||||
|
||||
}
|
||||
|
||||
public class PlcConnectionConfig
|
||||
@@ -15,15 +16,18 @@ public class PlcConnectionConfig
|
||||
public string IpAddress { get; set; } = "127.0.0.1";
|
||||
public int Port { get; set; } = 502;
|
||||
public int TimeoutMs { get; set; } = 5000;
|
||||
|
||||
public byte SlaveId { get; set; } = 1; // 从站地址(默认1)
|
||||
}
|
||||
|
||||
public class PlcRegisterAddresses
|
||||
{
|
||||
public int ThermalConductivity { get; set; } = 40001;
|
||||
public int ThermalDiffusivity { get; set; } = 40003;
|
||||
public int TestTemperature { get; set; } = 40005;
|
||||
public int StartCommand { get; set; } = 40010;
|
||||
public int ResetCommand { get; set; } = 40011;
|
||||
public ushort ThermalConductivity { get; set; } = 40001;
|
||||
public ushort ThermalDiffusivity { get; set; } = 40003;
|
||||
public ushort TestTemperature { get; set; } = 40005;
|
||||
public ushort StartCommand { get; set; } = 40010;
|
||||
public ushort ResetCommand { get; set; } = 40011;
|
||||
|
||||
}
|
||||
|
||||
public class TestParameters
|
||||
@@ -43,6 +47,8 @@ public class TestParameters
|
||||
public bool UsePressure { get; set; } = false;
|
||||
public string ReferenceLiquid { get; set; } = "蒸馏水";
|
||||
public double ReferenceConductivity { get; set; } = 0.606;
|
||||
|
||||
public CalibrationCoefficients CalibrationCoefficients { get; set; } = new();
|
||||
}
|
||||
|
||||
public class AppSettings
|
||||
@@ -50,4 +56,12 @@ public class AppSettings
|
||||
public int WindowWidth { get; set; } = 1024;
|
||||
public int WindowHeight { get; set; } = 768;
|
||||
public string ThemeColor { get; set; } = "Blue";
|
||||
}
|
||||
|
||||
public class CalibrationCoefficients
|
||||
{
|
||||
public ushort PressureCoefficient { get; set; }
|
||||
public ushort PressureProtection { get; set; }
|
||||
public ushort TemperatureCoefficient { get; set; }
|
||||
public ushort ResistanceCoefficient { get; set; }
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
namespace ASTM_D7896_Tester.Services;
|
||||
|
||||
public interface IPlcCommunicationService
|
||||
{
|
||||
Task<bool> ConnectAsync();
|
||||
Task DisconnectAsync();
|
||||
Task<bool> IsConnectedAsync();
|
||||
Task<float> ReadFloatAsync(int address);
|
||||
Task WriteSingleCoilAsync(int address, bool value);
|
||||
Task WriteSingleRegisterAsync(int address, short value);
|
||||
}
|
||||
39
Services/IPlcService.cs
Normal file
39
Services/IPlcService.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
namespace ASTM_D7896_Tester.Services;
|
||||
|
||||
public interface IPlcService
|
||||
{
|
||||
Task<bool> ConnectAsync();
|
||||
Task DisconnectAsync();
|
||||
Task<bool> IsConnectedAsync();
|
||||
/// <summary> 读取指定工位的压力(浮点数) </summary>
|
||||
/// <param name="stationId">工位号 1~3</param>
|
||||
//Task<float> ReadPressureAsync(int stationId);
|
||||
|
||||
/// <summary> 读取湿膜流量(浮点数) </summary>
|
||||
Task<float> ReadWetFlowAsync(int stationId);
|
||||
|
||||
/// <summary> 读取干膜流量(浮点数) </summary>
|
||||
Task<float> ReadDryFlowAsync(int stationId);
|
||||
|
||||
/// <summary> 写入线圈(如 M 元件) </summary>
|
||||
Task WriteCoilAsync(ushort coilAddress, bool value);
|
||||
|
||||
/// <summary> 写入单个寄存器(16位) </summary>
|
||||
Task WriteRegisterAsync(ushort registerAddress, ushort value);
|
||||
|
||||
/// <summary> 读取线圈状态(如 M 元件的 ON/OFF) </summary>
|
||||
Task<bool> ReadCoilAsync(ushort coilAddress);
|
||||
|
||||
/// <summary> 读取连续多个保持寄存器(16位) </summary>
|
||||
Task<ushort[]> ReadHoldingRegistersAsync(ushort startAddress, ushort count);
|
||||
|
||||
/// <summary> 写入单个保持寄存器(16位) </summary>
|
||||
Task WriteSingleRegisterAsync(ushort registerAddress, ushort value);
|
||||
|
||||
|
||||
Task WriteMultipleRegistersAsync(ushort registerAddress, float value);
|
||||
|
||||
float UshortToFloat(ushort P1, ushort P2);
|
||||
|
||||
Task<float> ReadFloatAsync(ushort startAddress);
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ASTM_D7896_Tester.Services;
|
||||
|
||||
public class PlcCommunicationService : IPlcCommunicationService
|
||||
{
|
||||
private bool _isConnected = false;
|
||||
private readonly Random _random = new();
|
||||
|
||||
public Task<bool> ConnectAsync()
|
||||
{
|
||||
// 模拟连接
|
||||
_isConnected = true;
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
public Task DisconnectAsync()
|
||||
{
|
||||
_isConnected = false;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<bool> IsConnectedAsync() => Task.FromResult(_isConnected);
|
||||
|
||||
public Task<float> ReadFloatAsync(int address)
|
||||
{
|
||||
// 模拟读取PLC寄存器返回随机值(实际应通过协议读取)
|
||||
// 热导率范围 0.1~1.0 W/m·K,热扩散率范围 0.05~1.5 ×10⁻⁶ m²/s
|
||||
if (address == 40001)
|
||||
return Task.FromResult((float)(0.2 + _random.NextDouble() * 0.8));
|
||||
if (address == 40003)
|
||||
return Task.FromResult((float)(0.1 + _random.NextDouble() * 1.0));
|
||||
if (address == 40005)
|
||||
return Task.FromResult(25.0f); // 测试温度
|
||||
return Task.FromResult(0.0f);
|
||||
}
|
||||
|
||||
public Task WriteSingleCoilAsync(int address, bool value)
|
||||
{
|
||||
// 模拟写入线圈
|
||||
System.Diagnostics.Debug.WriteLine($"Write coil {address} = {value}");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task WriteSingleRegisterAsync(int address, short value)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Write register {address} = {value}");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
212
Services/PlcService.cs
Normal file
212
Services/PlcService.cs
Normal file
@@ -0,0 +1,212 @@
|
||||
using ASTM_D7896_Tester.Models;
|
||||
using Modbus.Device;
|
||||
using System;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ASTM_D7896_Tester.Services;
|
||||
|
||||
public class PlcService : IPlcService
|
||||
{
|
||||
private bool _isConnected = false;
|
||||
private readonly AppConfig _config;
|
||||
private TcpClient _tcpClient;
|
||||
private IModbusMaster _master;
|
||||
|
||||
public PlcService(AppConfig config)
|
||||
{
|
||||
_config = config;
|
||||
}
|
||||
|
||||
public Task<bool> ConnectAsync()
|
||||
{
|
||||
// 模拟连接
|
||||
_isConnected = true;
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
public Task DisconnectAsync()
|
||||
{
|
||||
_isConnected = false;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<bool> IsConnectedAsync() => Task.FromResult(_isConnected);
|
||||
|
||||
public async Task EnsureConnectedAsync(int retryCount = 3)
|
||||
{
|
||||
if (_tcpClient != null && _tcpClient.Connected)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < retryCount; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
_tcpClient?.Close();
|
||||
_tcpClient = new TcpClient();
|
||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
|
||||
// 现在可以直接使用扩展方法
|
||||
await _tcpClient.ConnectAsync(_config.PlcConnection.IpAddress, _config.PlcConnection.Port).WithCancellation(cts.Token);
|
||||
_master = ModbusIpMaster.CreateIp(_tcpClient);
|
||||
return;
|
||||
}
|
||||
catch (Exception ex) when (i < retryCount - 1)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"连接失败,{500}ms 后重试... {ex.Message}");
|
||||
await Task.Delay(500);
|
||||
}
|
||||
}
|
||||
throw new Exception($"无法连接到 PLC ({_config.PlcConnection.IpAddress}:{_config.PlcConnection.Port}),请检查网络和 PLC 状态。");
|
||||
}
|
||||
|
||||
// 读取两个连续的保持寄存器,转换为32位浮点数(假设大端模式)
|
||||
public async Task<float> ReadFloatAsync(ushort startAddress)
|
||||
{
|
||||
await EnsureConnectedAsync();
|
||||
var registers = await ReadHoldingRegistersAsync(startAddress, 2);
|
||||
return UshortToFloat(registers[1], registers[0]);
|
||||
}
|
||||
|
||||
|
||||
//public async Task<float> ReadPressureAsync() =>
|
||||
// await ReadFloatAsync(_config.PressureRegister);
|
||||
|
||||
public async Task<float> ReadWetFlowAsync(int stationId)
|
||||
{
|
||||
ushort startAddress = stationId switch
|
||||
{
|
||||
//1 => _config.WetFlowRegister,
|
||||
//2 => _config.WetFlowRegister2,
|
||||
//3 => _config.WetFlowRegister3,
|
||||
};
|
||||
return await ReadFloatAsync(startAddress);
|
||||
}
|
||||
|
||||
public async Task<float> ReadDryFlowAsync(int stationId)
|
||||
{
|
||||
ushort startAddress = stationId switch
|
||||
{
|
||||
_ => throw new ArgumentException("Invalid station")
|
||||
};
|
||||
return await ReadFloatAsync(startAddress);
|
||||
}
|
||||
|
||||
|
||||
public async Task WriteCoilAsync(ushort coilAddress, bool value)
|
||||
{
|
||||
await EnsureConnectedAsync();
|
||||
await _master.WriteSingleCoilAsync(_config.PlcConnection.SlaveId, coilAddress, value);
|
||||
}
|
||||
|
||||
public async Task WriteRegisterAsync(ushort registerAddress, ushort value)
|
||||
{
|
||||
await EnsureConnectedAsync();
|
||||
await Task.Delay(100);
|
||||
await _master.WriteSingleRegisterAsync(_config.PlcConnection.SlaveId, registerAddress, value);
|
||||
}
|
||||
|
||||
|
||||
public async Task<bool> ReadCoilAsync(ushort coilAddress)
|
||||
{
|
||||
await EnsureConnectedAsync();
|
||||
await Task.Delay(100);
|
||||
bool[] result = await _master?.ReadCoilsAsync(_config.PlcConnection.SlaveId, coilAddress, 1);
|
||||
return result[0];
|
||||
}
|
||||
|
||||
public bool IsConnected => _tcpClient != null && _tcpClient.Connected;
|
||||
|
||||
public async Task<ushort[]> ReadHoldingRegistersAsync(ushort startAddress, ushort count)
|
||||
{
|
||||
await EnsureConnectedAsync();
|
||||
// await Task.Delay(100);
|
||||
return await _master.ReadHoldingRegistersAsync(_config.PlcConnection.SlaveId, startAddress, count);
|
||||
}
|
||||
|
||||
public async Task WriteSingleRegisterAsync(ushort registerAddress, ushort value)
|
||||
{
|
||||
await EnsureConnectedAsync();
|
||||
int val = (int)value;
|
||||
await Task.Delay(100);
|
||||
await _master.WriteMultipleRegistersAsync(1, registerAddress, intToushorts(val));
|
||||
}
|
||||
|
||||
public async Task WriteMultipleRegistersAsync(ushort registerAddress, float value)
|
||||
{
|
||||
await EnsureConnectedAsync();
|
||||
await Task.Delay(100);
|
||||
await _master.WriteMultipleRegistersAsync(_config.PlcConnection.SlaveId, registerAddress, SplitFloatToUShortArray((float)value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Int转为ushort数组发送
|
||||
/// </summary>
|
||||
/// <param name="res"></param>
|
||||
/// <returns>返回ushort数组</returns>
|
||||
private ushort[] intToushorts(int res)
|
||||
{
|
||||
ushort ust1 = (ushort)(res >> 16);
|
||||
ushort ust2 = (ushort)res;
|
||||
return new ushort[] { ust2, ust1 };
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Float转为Ushort数组发送
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns>返回ushort数组</returns>
|
||||
public ushort[] SplitFloatToUShortArray(float value)
|
||||
{
|
||||
byte[] floatBytes = BitConverter.GetBytes(value);
|
||||
ushort[] ushortArray = new ushort[floatBytes.Length / 2];
|
||||
|
||||
for (int i = 0, j = 0; i < floatBytes.Length; i += 2, j++)
|
||||
{
|
||||
ushortArray[j] = BitConverter.ToUInt16(floatBytes, i);
|
||||
}
|
||||
|
||||
return ushortArray;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ushort转为float类型
|
||||
/// </summary>
|
||||
/// <param name="P1"></param>
|
||||
/// <param name="P2"></param>
|
||||
/// <returns>float型数据</returns>
|
||||
public float UshortToFloat(ushort P1, ushort P2)
|
||||
{
|
||||
int intSign, intSignRest, intExponent, intExponentRest;
|
||||
float faResult, faDigit;
|
||||
intSign = P1 / 32768;
|
||||
intSignRest = P1 % 32768;
|
||||
intExponent = intSignRest / 128;
|
||||
intExponentRest = intSignRest % 128;
|
||||
faDigit = (float)(intExponentRest * 65536 + P2) / 8388608;
|
||||
faResult = (float)Math.Pow(-1, intSign) * (float)Math.Pow(2, intExponent - 127) * (faDigit + 1);
|
||||
return faResult;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_master?.Dispose();
|
||||
_tcpClient?.Close();
|
||||
_tcpClient?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class TaskExtensions
|
||||
{
|
||||
public static async Task WithCancellation(this Task task, CancellationToken cancellationToken)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<bool>();
|
||||
using (cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs))
|
||||
{
|
||||
if (task != await Task.WhenAny(task, tcs.Task))
|
||||
throw new OperationCanceledException(cancellationToken);
|
||||
}
|
||||
await task;
|
||||
}
|
||||
}
|
||||
85
ViewModels/ConfigViewModel.cs
Normal file
85
ViewModels/ConfigViewModel.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
using ASTM_D7896_Tester.Models;
|
||||
using ASTM_D7896_Tester.Services;
|
||||
using ASTM_D7896_Tester.Views;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using static ASTM_D7896_Tester.Models.TestParameters;
|
||||
|
||||
namespace ASTM_D7896_Tester.ViewModels;
|
||||
|
||||
public partial class ConfigViewModel : ObservableObject
|
||||
{
|
||||
[ObservableProperty]
|
||||
private float _pressureCoefficient;
|
||||
|
||||
[ObservableProperty]
|
||||
private float _pressureProtection;
|
||||
|
||||
[ObservableProperty]
|
||||
private float _temperatureCoefficient;
|
||||
|
||||
[ObservableProperty]
|
||||
private float _resistanceCoefficient;
|
||||
|
||||
private readonly IPlcService _plcService;
|
||||
private readonly AppConfig _coefficientAddresses;
|
||||
|
||||
public ConfigViewModel()
|
||||
{
|
||||
// 从 App 静态属性获取 PLC 服务和系数寄存器地址
|
||||
_plcService = ASTM_D7896_Tester.App.PlcService;
|
||||
_coefficientAddresses = ASTM_D7896_Tester.App.PlcConfig;
|
||||
|
||||
// 窗口打开时加载一次
|
||||
LoadFromPlc();
|
||||
}
|
||||
|
||||
private async void LoadFromPlc()
|
||||
{
|
||||
try
|
||||
{
|
||||
PressureCoefficient = await _plcService.ReadFloatAsync(_coefficientAddresses.TestParameters.CalibrationCoefficients.PressureCoefficient);
|
||||
PressureProtection = await _plcService.ReadFloatAsync(_coefficientAddresses.TestParameters.CalibrationCoefficients.PressureProtection);
|
||||
TemperatureCoefficient = await _plcService.ReadFloatAsync(_coefficientAddresses.TestParameters.CalibrationCoefficients.TemperatureCoefficient);
|
||||
ResistanceCoefficient = await _plcService.ReadFloatAsync(_coefficientAddresses.TestParameters.CalibrationCoefficients.ResistanceCoefficient);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"从 PLC 读取系数失败:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task Save()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 写入 PLC(使用 WriteMultipleRegistersAsync,该方法支持 float)
|
||||
await _plcService.WriteMultipleRegistersAsync(_coefficientAddresses.TestParameters.CalibrationCoefficients.PressureCoefficient, PressureCoefficient);
|
||||
await _plcService.WriteMultipleRegistersAsync(_coefficientAddresses.TestParameters.CalibrationCoefficients.PressureProtection, PressureProtection);
|
||||
await _plcService.WriteMultipleRegistersAsync(_coefficientAddresses.TestParameters.CalibrationCoefficients.TemperatureCoefficient, TemperatureCoefficient);
|
||||
await _plcService.WriteMultipleRegistersAsync(_coefficientAddresses.TestParameters.CalibrationCoefficients.ResistanceCoefficient, ResistanceCoefficient);
|
||||
|
||||
MessageBox.Show("系数已保存到 PLC。", "成功", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
CloseWindow();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"保存失败:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void Cancel()
|
||||
{
|
||||
CloseWindow();
|
||||
}
|
||||
|
||||
private void CloseWindow()
|
||||
{
|
||||
Application.Current.Windows.OfType<ConfigWindow>().FirstOrDefault()?.Close();
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,10 @@ namespace ASTM_D7896_Tester.ViewModels;
|
||||
|
||||
public partial class D7896ViewModel : ObservableObject
|
||||
{
|
||||
private readonly IPlcCommunicationService _plcService;
|
||||
private readonly ReportService _reportService;
|
||||
private readonly IPlcService _plcService;
|
||||
private AppConfig _config;
|
||||
private readonly ReportService _reportService;
|
||||
|
||||
|
||||
public ObservableCollection<string> ReferenceLiquids { get; } = new ObservableCollection<string> { "蒸馏水", "甲苯", "乙二醇" };
|
||||
|
||||
@@ -119,8 +120,10 @@ public partial class D7896ViewModel : ObservableObject
|
||||
|
||||
public D7896ViewModel()
|
||||
{
|
||||
_config = JsonConfigHelper.LoadConfig();
|
||||
_plcService = new PlcCommunicationService();
|
||||
// 获取应用全局配置并确保不为 null
|
||||
_config = ASTM_D7896_Tester.App.PlcConfig ?? new Models.AppConfig();
|
||||
|
||||
_plcService = ASTM_D7896_Tester.App.PlcService;
|
||||
_reportService = new ReportService(_config.TestParameters.ReportOutputPath);
|
||||
|
||||
// 加载配置中的默认值
|
||||
@@ -198,9 +201,9 @@ public partial class D7896ViewModel : ObservableObject
|
||||
CurrentMeasurementIndex = i;
|
||||
StatusMessage = $"正在执行第 {i} 次测量...";
|
||||
|
||||
await _plcService.WriteSingleCoilAsync(_config.PlcRegisterAddresses.StartCommand, true);
|
||||
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, true);
|
||||
await Task.Delay(500);
|
||||
await _plcService.WriteSingleCoilAsync(_config.PlcRegisterAddresses.StartCommand, false);
|
||||
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, false);
|
||||
|
||||
await Task.Delay(2000);
|
||||
|
||||
@@ -461,9 +464,9 @@ public partial class D7896ViewModel : ObservableObject
|
||||
if (!await _plcService.IsConnectedAsync())
|
||||
await _plcService.ConnectAsync();
|
||||
|
||||
await _plcService.WriteSingleCoilAsync(_config.PlcRegisterAddresses.StartCommand, true);
|
||||
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, true);
|
||||
await Task.Delay(500);
|
||||
await _plcService.WriteSingleCoilAsync(_config.PlcRegisterAddresses.StartCommand, false);
|
||||
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, false);
|
||||
await Task.Delay(2000);
|
||||
|
||||
float lambda = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.ThermalConductivity);
|
||||
|
||||
44
Views/ConfigWindow.xaml
Normal file
44
Views/ConfigWindow.xaml
Normal file
@@ -0,0 +1,44 @@
|
||||
<Window x:Class="ASTM_D7896_Tester.Views.ConfigWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="高级参数配置" Height="300" Width="400"
|
||||
WindowStartupLocation="CenterOwner">
|
||||
<Grid Margin="20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Text="校准系数配置" FontWeight="Bold" FontSize="16" Grid.Row="0" Margin="0,0,0,15"/>
|
||||
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,5">
|
||||
<TextBlock Text="压力系数 (D1328):" Width="150" VerticalAlignment="Center"/>
|
||||
<TextBox Text="{Binding PressureCoefficient ,UpdateSourceTrigger=LostFocus}" Width="100"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal" Margin="0,5">
|
||||
<TextBlock Text="压力保护 (kPa) (D1332):" Width="150" VerticalAlignment="Center"/>
|
||||
<TextBox Text="{Binding PressureProtection,UpdateSourceTrigger=LostFocus}" Width="100"/>
|
||||
<TextBlock Text="(超此值停止测试)" Margin="10,0,0,0" Foreground="Gray" FontSize="11"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="3" Orientation="Horizontal" Margin="0,5">
|
||||
<TextBlock Text="温度系数 (D1378):" Width="150" VerticalAlignment="Center"/>
|
||||
<TextBox Text="{Binding TemperatureCoefficient ,UpdateSourceTrigger=LostFocus}" Width="100"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="4" Orientation="Horizontal" Margin="0,5">
|
||||
<TextBlock Text="电阻系数 (D1428):" Width="150" VerticalAlignment="Center"/>
|
||||
<TextBox Text="{Binding ResistanceCoefficient,UpdateSourceTrigger=LostFocus}" Width="100"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="5" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,20,0,0">
|
||||
<Button Content="保存并关闭" Command="{Binding SaveAndCloseCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}" Width="100" Margin="5"/>
|
||||
<Button Content="取消" Click="CancelButton_Click" Width="100" Margin="5"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
18
Views/ConfigWindow.xaml.cs
Normal file
18
Views/ConfigWindow.xaml.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System.Windows;
|
||||
using ASTM_D7896_Tester.ViewModels;
|
||||
|
||||
namespace ASTM_D7896_Tester.Views;
|
||||
|
||||
public partial class ConfigWindow : Window
|
||||
{
|
||||
public ConfigWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = new ConfigViewModel();
|
||||
}
|
||||
|
||||
private void CancelButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using ASTM_D7896_Tester.ViewModels;
|
||||
using ASTM_D7896_Tester.Models;
|
||||
using ASTM_D7896_Tester.Services;
|
||||
using ASTM_D7896_Tester.ViewModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -21,10 +23,11 @@ namespace ASTM_D7896_Tester.Views
|
||||
/// </summary>
|
||||
public partial class D7896View : UserControl
|
||||
{
|
||||
|
||||
public D7896View()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = new D7896ViewModel(); // 或者从外部注入
|
||||
DataContext = new D7896ViewModel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,28 @@
|
||||
using System.Text;
|
||||
using ASTM_D7896_Tester.Views;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace ASTM_D7896_Tester.Views
|
||||
namespace ASTM_D7896_Tester.Views;
|
||||
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
public MainWindow()
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
InitializeComponent();
|
||||
// 注册快捷键
|
||||
KeyGesture keyGesture = new KeyGesture(Key.P, ModifierKeys.Control);
|
||||
var inputBinding = new InputBinding(OpenConfigCommand, keyGesture);
|
||||
this.InputBindings.Add(inputBinding);
|
||||
}
|
||||
|
||||
private ICommand _openConfigCommand;
|
||||
public ICommand OpenConfigCommand => _openConfigCommand ??= new RelayCommand(OpenConfig);
|
||||
|
||||
private void OpenConfig()
|
||||
{
|
||||
var configWindow = new ConfigWindow();
|
||||
configWindow.Owner = this;
|
||||
configWindow.ShowDialog();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"PlcConnection": {
|
||||
"IpAddress": "192.168.1.100",
|
||||
"IpAddress": "127.0.0.1",
|
||||
"Port": 502,
|
||||
"TimeoutMs": 5000
|
||||
},
|
||||
@@ -21,7 +21,13 @@
|
||||
"DefaultPressure": 0.0,
|
||||
"UsePressure": false,
|
||||
"ReferenceLiquid": "蒸馏水",
|
||||
"ReferenceConductivity": 0.606
|
||||
"ReferenceConductivity": 0.606,
|
||||
"CalibrationCoefficients": {
|
||||
"PressureCoefficient": 1328,
|
||||
"PressureProtection": 1332,
|
||||
"TemperatureCoefficient": 1378,
|
||||
"ResistanceCoefficient": 1428
|
||||
}
|
||||
},
|
||||
"AppSettings": {
|
||||
"WindowWidth": 1024,
|
||||
|
||||
Reference in New Issue
Block a user