更新2026
This commit is contained in:
17
App.xaml.cs
17
App.xaml.cs
@@ -17,7 +17,7 @@ namespace TabletTester2025
|
|||||||
public static PlcConfiguration PlcConfig { get; private set; }
|
public static PlcConfiguration PlcConfig { get; private set; }
|
||||||
public static PharmaParameters CurrentPharmaParams { get; set; } = new PharmaParameters();
|
public static PharmaParameters CurrentPharmaParams { get; set; } = new PharmaParameters();
|
||||||
|
|
||||||
protected override async void OnStartup(StartupEventArgs e)
|
protected override void OnStartup(StartupEventArgs e)
|
||||||
{
|
{
|
||||||
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
|
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
|
||||||
base.OnStartup(e);
|
base.OnStartup(e);
|
||||||
@@ -96,22 +96,7 @@ namespace TabletTester2025
|
|||||||
if (PlcConfig == null)
|
if (PlcConfig == null)
|
||||||
throw new InvalidOperationException("PLC配置缺失");
|
throw new InvalidOperationException("PLC配置缺失");
|
||||||
|
|
||||||
// 创建PLC服务(真实或模拟)
|
|
||||||
if (configuration["Plc:Type"] == "ModbusTcp")
|
|
||||||
PlcService = new ModbusTcpPlcService(PlcConfig);
|
PlcService = new ModbusTcpPlcService(PlcConfig);
|
||||||
else
|
|
||||||
PlcService = new PlcSimulator();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await PlcService.ConnectAsync();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
MessageBox.Show($"PLC连接失败,将使用模拟模式。\n{ex.Message}", "警告", MessageBoxButton.OK, MessageBoxImage.Warning);
|
|
||||||
PlcService = new PlcSimulator();
|
|
||||||
await PlcService.ConnectAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 业务服务
|
// 业务服务
|
||||||
var dbService = new DatabaseService(connectionString);
|
var dbService = new DatabaseService(connectionString);
|
||||||
|
|||||||
@@ -43,7 +43,12 @@ namespace TabletTester2025.Helpers
|
|||||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
string status = value as string;
|
string status = value as string;
|
||||||
return (status == "已连接") ? "Green" : "Red";
|
return status switch
|
||||||
|
{
|
||||||
|
"已连接" => "Green",
|
||||||
|
"连接中" => "Orange",
|
||||||
|
_ => "Red"
|
||||||
|
};
|
||||||
}
|
}
|
||||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
=> throw new NotImplementedException();
|
=> throw new NotImplementedException();
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace TabletTester2025.Services
|
namespace TabletTester2025.Services
|
||||||
{
|
{
|
||||||
public class BalanceService
|
public class BalanceService
|
||||||
{
|
{
|
||||||
// 实际项目中请使用串口通讯
|
|
||||||
public async Task<double> ReadWeightAsync()
|
public async Task<double> ReadWeightAsync()
|
||||||
{
|
{
|
||||||
await Task.Delay(100);
|
await Task.Delay(100);
|
||||||
return new Random().NextDouble() * 10; // 模拟重量 0-10g
|
throw new InvalidOperationException("天平真实通讯未接入,禁止返回模拟重量");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using Modbus.Device;
|
using Modbus.Device;
|
||||||
using System;
|
using System;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -7,102 +7,151 @@ using TabletTester2025.Models;
|
|||||||
|
|
||||||
namespace TabletTester2025.Services
|
namespace TabletTester2025.Services
|
||||||
{
|
{
|
||||||
public class ModbusTcpPlcService : IPlcService
|
public class ModbusTcpPlcService : IPlcService, IDisposable
|
||||||
{
|
{
|
||||||
|
private const int ConnectTimeoutMs = 3000;
|
||||||
|
private const int RetryDelayMs = 1000;
|
||||||
|
private const int DefaultRetryCount = 3;
|
||||||
|
|
||||||
private readonly PlcConfiguration _config;
|
private readonly PlcConfiguration _config;
|
||||||
private TcpClient _tcpClient;
|
private readonly SemaphoreSlim _connectLock = new(1, 1);
|
||||||
private IModbusMaster _master;
|
private TcpClient? _tcpClient;
|
||||||
|
private IModbusMaster? _master;
|
||||||
|
|
||||||
public ModbusTcpPlcService(PlcConfiguration config)
|
public ModbusTcpPlcService(PlcConfiguration config)
|
||||||
{
|
{
|
||||||
_config = config;
|
_config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ConnectAsync() => await EnsureConnectedAsync();
|
public Task ConnectAsync() => EnsureConnectedAsync();
|
||||||
|
|
||||||
public async Task EnsureConnectedAsync(int retryCount = 3)
|
public async Task EnsureConnectedAsync(int retryCount = DefaultRetryCount)
|
||||||
{
|
{
|
||||||
if (_tcpClient != null && _tcpClient.Connected)
|
if (HasOpenConnection)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (int i = 0; i < retryCount; i++)
|
await _connectLock.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (HasOpenConnection)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Exception? lastError = null;
|
||||||
|
for (int attempt = 1; attempt <= retryCount; attempt++)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_tcpClient?.Close();
|
CloseConnection();
|
||||||
_tcpClient = new TcpClient();
|
|
||||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
|
var client = new TcpClient();
|
||||||
await _tcpClient.ConnectAsync(_config.IpAddress, _config.Port).WithCancellation(cts.Token);
|
using var cts = new CancellationTokenSource(ConnectTimeoutMs);
|
||||||
_master = ModbusIpMaster.CreateIp(_tcpClient);
|
await client.ConnectAsync(_config.IpAddress, _config.Port).WithCancellation(cts.Token);
|
||||||
_master.Transport.ReadTimeout = 1000;
|
|
||||||
_master.Transport.WriteTimeout = 1000;
|
var master = ModbusIpMaster.CreateIp(client);
|
||||||
|
master.Transport.ReadTimeout = 1000;
|
||||||
|
master.Transport.WriteTimeout = 1000;
|
||||||
|
|
||||||
|
_tcpClient = client;
|
||||||
|
_master = master;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
catch (Exception ex) when (i < retryCount - 1)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine($"连接失败,500ms后重试... {ex.Message}");
|
lastError = ex;
|
||||||
await Task.Delay(500);
|
CloseConnection();
|
||||||
|
System.Diagnostics.Debug.WriteLine($"PLC连接失败,第{attempt}/{retryCount}次:{ex.Message}");
|
||||||
|
|
||||||
|
if (attempt < retryCount)
|
||||||
|
await Task.Delay(RetryDelayMs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new Exception($"无法连接到 PLC ({_config.IpAddress}:{_config.Port})");
|
|
||||||
|
throw new InvalidOperationException($"无法连接到 PLC ({_config.IpAddress}:{_config.Port})", lastError);
|
||||||
}
|
}
|
||||||
//读取寄存器返回浮点型
|
finally
|
||||||
|
{
|
||||||
|
_connectLock.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<float> ReadFloatAsync(ushort startAddress)
|
public async Task<float> ReadFloatAsync(ushort startAddress)
|
||||||
{
|
{
|
||||||
await EnsureConnectedAsync();
|
|
||||||
var registers = await ReadHoldingRegistersAsync(startAddress, 2);
|
var registers = await ReadHoldingRegistersAsync(startAddress, 2);
|
||||||
return UshortToFloat(registers[1], registers[0]);
|
return UshortToFloat(registers[1], registers[0]);
|
||||||
}
|
}
|
||||||
//读取返回整型
|
|
||||||
public async Task<int> ReadIntAsync(ushort startAddress)
|
public async Task<int> ReadIntAsync(ushort startAddress)
|
||||||
{
|
{
|
||||||
await EnsureConnectedAsync();
|
|
||||||
var registers = await ReadHoldingRegistersAsync(startAddress, 1);
|
var registers = await ReadHoldingRegistersAsync(startAddress, 1);
|
||||||
return registers[0];
|
return registers[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task WriteCoilAsync(ushort coilAddress, bool value)
|
public Task WriteCoilAsync(ushort coilAddress, bool value)
|
||||||
{
|
{
|
||||||
await EnsureConnectedAsync();
|
return ExecuteAsync(master => master.WriteSingleCoilAsync(_config.SlaveId, coilAddress, value));
|
||||||
await _master.WriteSingleCoilAsync(_config.SlaveId, coilAddress, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> ReadCoilAsync(ushort coilAddress)
|
public async Task<bool> ReadCoilAsync(ushort coilAddress)
|
||||||
{
|
{
|
||||||
await EnsureConnectedAsync();
|
bool[] result = await ExecuteAsync(master => master.ReadCoilsAsync(_config.SlaveId, coilAddress, 1));
|
||||||
bool[] result = await _master.ReadCoilsAsync(_config.SlaveId, coilAddress, 1);
|
|
||||||
return result[0];
|
return result[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task WriteRegisterAsync(ushort registerAddress, ushort value)
|
public Task WriteRegisterAsync(ushort registerAddress, ushort value)
|
||||||
{
|
{
|
||||||
await EnsureConnectedAsync();
|
return ExecuteAsync(master => master.WriteSingleRegisterAsync(_config.SlaveId, registerAddress, value));
|
||||||
await _master.WriteSingleRegisterAsync(_config.SlaveId, registerAddress, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task WriteFloatAsync(ushort startAddress, float value)
|
public Task WriteFloatAsync(ushort startAddress, float value)
|
||||||
{
|
{
|
||||||
await EnsureConnectedAsync();
|
|
||||||
byte[] bytes = BitConverter.GetBytes(value);
|
byte[] bytes = BitConverter.GetBytes(value);
|
||||||
ushort[] registers =
|
ushort[] registers =
|
||||||
{
|
{
|
||||||
(ushort)((bytes[2] << 8) | bytes[3]),
|
(ushort)((bytes[2] << 8) | bytes[3]),
|
||||||
(ushort)((bytes[0] << 8) | bytes[1])
|
(ushort)((bytes[0] << 8) | bytes[1])
|
||||||
};
|
};
|
||||||
await _master.WriteMultipleRegistersAsync(_config.SlaveId, startAddress, registers);
|
|
||||||
|
return ExecuteAsync(master => master.WriteMultipleRegistersAsync(_config.SlaveId, startAddress, registers));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ushort[]> ReadHoldingRegistersAsync(ushort startAddress, ushort count)
|
public Task<ushort[]> ReadHoldingRegistersAsync(ushort startAddress, ushort count)
|
||||||
|
{
|
||||||
|
return ExecuteAsync(master => master.ReadHoldingRegistersAsync(_config.SlaveId, startAddress, count));
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsConnected => HasOpenConnection;
|
||||||
|
|
||||||
|
private bool HasOpenConnection => _tcpClient?.Connected == true && _master != null;
|
||||||
|
|
||||||
|
private async Task ExecuteAsync(Func<IModbusMaster, Task> action)
|
||||||
|
{
|
||||||
|
await ExecuteAsync(async master =>
|
||||||
|
{
|
||||||
|
await action(master);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<T> ExecuteAsync<T>(Func<IModbusMaster, Task<T>> action)
|
||||||
{
|
{
|
||||||
await EnsureConnectedAsync();
|
await EnsureConnectedAsync();
|
||||||
return await _master.ReadHoldingRegistersAsync(_config.SlaveId, startAddress, count);
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_master == null)
|
||||||
|
throw new InvalidOperationException("PLC连接未初始化");
|
||||||
|
|
||||||
|
return await action(_master);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
CloseConnection();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsConnected => _tcpClient != null && _tcpClient.Connected;
|
private static float UshortToFloat(ushort high, ushort low)
|
||||||
|
|
||||||
private float UshortToFloat(ushort high, ushort low)
|
|
||||||
{
|
{
|
||||||
// Modbus 大端模式:高16位在前,低16位在后
|
|
||||||
byte[] bytes = new byte[4];
|
byte[] bytes = new byte[4];
|
||||||
bytes[0] = (byte)(high >> 8);
|
bytes[0] = (byte)(high >> 8);
|
||||||
bytes[1] = (byte)(high & 0xFF);
|
bytes[1] = (byte)(high & 0xFF);
|
||||||
@@ -111,11 +160,19 @@ namespace TabletTester2025.Services
|
|||||||
return BitConverter.ToSingle(bytes, 0);
|
return BitConverter.ToSingle(bytes, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CloseConnection()
|
||||||
|
{
|
||||||
|
try { _master?.Dispose(); } catch { }
|
||||||
|
try { _tcpClient?.Close(); } catch { }
|
||||||
|
try { _tcpClient?.Dispose(); } catch { }
|
||||||
|
_master = null;
|
||||||
|
_tcpClient = null;
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_master?.Dispose();
|
CloseConnection();
|
||||||
_tcpClient?.Close();
|
_connectLock.Dispose();
|
||||||
_tcpClient?.Dispose();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,11 +181,12 @@ namespace TabletTester2025.Services
|
|||||||
public static async Task WithCancellation(this Task task, CancellationToken cancellationToken)
|
public static async Task WithCancellation(this Task task, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var tcs = new TaskCompletionSource<bool>();
|
var tcs = new TaskCompletionSource<bool>();
|
||||||
using (cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs))
|
using (cancellationToken.Register(s => ((TaskCompletionSource<bool>)s!).TrySetResult(true), tcs))
|
||||||
{
|
{
|
||||||
if (task != await Task.WhenAny(task, tcs.Task))
|
if (task != await Task.WhenAny(task, tcs.Task))
|
||||||
throw new OperationCanceledException(cancellationToken);
|
throw new OperationCanceledException(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
await task;
|
await task;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ namespace TabletTester2025.ViewModels
|
|||||||
private readonly AlarmService _alarm;
|
private readonly AlarmService _alarm;
|
||||||
private readonly PlcConfiguration _plcConfig;
|
private readonly PlcConfiguration _plcConfig;
|
||||||
private DispatcherTimer _timer;
|
private DispatcherTimer _timer;
|
||||||
|
private bool _isConnecting;
|
||||||
|
private bool _isUpdatingRealtime;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -48,7 +50,7 @@ namespace TabletTester2025.ViewModels
|
|||||||
|
|
||||||
ExportAllCommand = new AsyncRelayCommand(ExportAllAsync);
|
ExportAllCommand = new AsyncRelayCommand(ExportAllAsync);
|
||||||
|
|
||||||
_timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(500) };
|
_timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
|
||||||
_timer.Tick += OnTimerTick;
|
_timer.Tick += OnTimerTick;
|
||||||
_ = ConnectToPlc();
|
_ = ConnectToPlc();
|
||||||
_timer.Start();
|
_timer.Start();
|
||||||
@@ -73,16 +75,54 @@ namespace TabletTester2025.ViewModels
|
|||||||
|
|
||||||
private async Task ConnectToPlc()
|
private async Task ConnectToPlc()
|
||||||
{
|
{
|
||||||
try { await _plc.ConnectAsync(); PlcStatus = "已连接"; }
|
if (_isConnecting)
|
||||||
catch { PlcStatus = "连接失败"; }
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_isConnecting = true;
|
||||||
|
PlcStatus = "连接中";
|
||||||
|
await _plc.ConnectAsync();
|
||||||
|
PlcStatus = _plc.IsConnected ? "已连接" : "连接失败";
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
PlcStatus = "连接失败";
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isConnecting = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void OnTimerTick(object sender, EventArgs e)
|
private async void OnTimerTick(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
CurrentTime = DateTime.Now.ToString("HH:mm:ss");
|
CurrentTime = DateTime.Now.ToString("HH:mm:ss");
|
||||||
if (PlcStatus != "已连接") return;
|
|
||||||
|
if (!_plc.IsConnected)
|
||||||
|
{
|
||||||
|
await ConnectToPlc();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlcStatus = "已连接";
|
||||||
|
if (_isUpdatingRealtime)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_isUpdatingRealtime = true;
|
||||||
await Tester.UpdateRealTimeData();
|
await Tester.UpdateRealTimeData();
|
||||||
}
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
PlcStatus = "连接失败";
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isUpdatingRealtime = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task ExportAllAsync()
|
private async Task ExportAllAsync()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ namespace TabletTester2025.ViewModels
|
|||||||
private readonly DatabaseService _db;
|
private readonly DatabaseService _db;
|
||||||
private readonly ExcelExportService _excel;
|
private readonly ExcelExportService _excel;
|
||||||
private readonly AlarmService _alarm;
|
private readonly AlarmService _alarm;
|
||||||
private readonly BalanceService _balance; // ✅ 新增天平服务
|
|
||||||
private DispatcherTimer _disintegrationTimer;
|
private DispatcherTimer _disintegrationTimer;
|
||||||
private bool _isLoadingDissolution1Time;
|
private bool _isLoadingDissolution1Time;
|
||||||
private bool _isLoadingDissolution2Time;
|
private bool _isLoadingDissolution2Time;
|
||||||
@@ -149,7 +148,6 @@ namespace TabletTester2025.ViewModels
|
|||||||
_db = db;
|
_db = db;
|
||||||
_excel = excel;
|
_excel = excel;
|
||||||
_alarm = alarm;
|
_alarm = alarm;
|
||||||
_balance = new BalanceService(); // 实例化天平服务(模拟)
|
|
||||||
|
|
||||||
StartHardnessCommand = new AsyncRelayCommand(RunHardnessAsync);
|
StartHardnessCommand = new AsyncRelayCommand(RunHardnessAsync);
|
||||||
StartFriabilityCommand = new AsyncRelayCommand(RunFriabilityAsync);
|
StartFriabilityCommand = new AsyncRelayCommand(RunFriabilityAsync);
|
||||||
@@ -632,6 +630,7 @@ namespace TabletTester2025.ViewModels
|
|||||||
CurrentTest = TestType.Friability;
|
CurrentTest = TestType.Friability;
|
||||||
Phase = TestPhase.Running;
|
Phase = TestPhase.Running;
|
||||||
FriabilityPass = false;
|
FriabilityPass = false;
|
||||||
|
bool resultReady = false;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -641,7 +640,10 @@ namespace TabletTester2025.ViewModels
|
|||||||
{
|
{
|
||||||
throw new InvalidOperationException("未配置脆碎度启动线圈地址");
|
throw new InvalidOperationException("未配置脆碎度启动线圈地址");
|
||||||
}
|
}
|
||||||
WeightBefore = await _balance.ReadWeightAsync();
|
WeightBefore = await ReadFriabilityWeightAsync(_plcConfig.WeightBefore, "脆碎前重量");
|
||||||
|
if (WeightBefore <= 0)
|
||||||
|
throw new InvalidOperationException("脆碎前重量必须大于0");
|
||||||
|
|
||||||
await _plc.WriteCoilAsync(startCoil, true);
|
await _plc.WriteCoilAsync(startCoil, true);
|
||||||
int totalRounds = 100; // 药典标准脆碎度总圈数:100圈
|
int totalRounds = 100; // 药典标准脆碎度总圈数:100圈
|
||||||
double rpm = FriabilityTargetRpm; // 界面设置的目标转速(r/min)
|
double rpm = FriabilityTargetRpm; // 界面设置的目标转速(r/min)
|
||||||
@@ -664,11 +666,15 @@ namespace TabletTester2025.ViewModels
|
|||||||
// 等待100ms,再更新下一次
|
// 等待100ms,再更新下一次
|
||||||
await Task.Delay(100);
|
await Task.Delay(100);
|
||||||
}
|
}
|
||||||
WeightAfter = await _balance.ReadWeightAsync();
|
if (Phase != TestPhase.Running)
|
||||||
|
throw new InvalidOperationException("脆碎度测试已停止,未保存结果");
|
||||||
|
|
||||||
|
WeightAfter = await ReadFriabilityWeightAsync(_plcConfig.WeightAfter, "脆碎后重量");
|
||||||
|
|
||||||
FriabilityCurrentRpm = FriabilityTargetRpm;
|
FriabilityCurrentRpm = FriabilityTargetRpm;
|
||||||
LossPercent = (WeightBefore - WeightAfter) / WeightBefore * 100;//失重率
|
LossPercent = (WeightBefore - WeightAfter) / WeightBefore * 100;//失重率
|
||||||
FriabilityPass = LossPercent <= App.CurrentPharmaParams.FriabilityMaxLossPercent; //标准值
|
FriabilityPass = LossPercent <= App.CurrentPharmaParams.FriabilityMaxLossPercent; //标准值
|
||||||
|
resultReady = true;
|
||||||
// 标记测试为已完成
|
// 标记测试为已完成
|
||||||
Phase = TestPhase.Completed;
|
Phase = TestPhase.Completed;
|
||||||
}
|
}
|
||||||
@@ -683,10 +689,23 @@ namespace TabletTester2025.ViewModels
|
|||||||
{
|
{
|
||||||
Phase = TestPhase.Idle;
|
Phase = TestPhase.Idle;
|
||||||
FriabilityRemainingRounds = 100;
|
FriabilityRemainingRounds = 100;
|
||||||
|
if (resultReady)
|
||||||
await SaveBatchResult();
|
await SaveBatchResult();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<double> ReadFriabilityWeightAsync(ushort registerAddress, string label)
|
||||||
|
{
|
||||||
|
if (registerAddress == 0)
|
||||||
|
throw new InvalidOperationException($"{label}寄存器未配置");
|
||||||
|
|
||||||
|
double value = await _plc.ReadFloatAsync(registerAddress);
|
||||||
|
if (!double.IsFinite(value) || value < 0)
|
||||||
|
throw new InvalidOperationException($"{label}数据异常");
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task RunDisintegrationAsync()
|
private async Task RunDisintegrationAsync()
|
||||||
{
|
{
|
||||||
if (Phase != TestPhase.Idle) return;
|
if (Phase != TestPhase.Idle) return;
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
"DefaultConnection": "Data Source=TabletTests.db"
|
"DefaultConnection": "Data Source=TabletTests.db"
|
||||||
},
|
},
|
||||||
"Plc": {
|
"Plc": {
|
||||||
"Type": "Simulator", // "Simulator" 或 "ModbusTcp"
|
"Type": "ModbusTcp",
|
||||||
"IpAddress": "127.0.0.1",
|
"IpAddress": "192.168.1.10",
|
||||||
"Port": 502,
|
"Port": 502,
|
||||||
"SlaveId": 1,
|
"SlaveId": 1,
|
||||||
"HardnessValue": 100,
|
"HardnessValue": 100,
|
||||||
|
|||||||
Reference in New Issue
Block a user