gx
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;
|
||||||
|
|||||||
@@ -1,35 +1,53 @@
|
|||||||
<Window x:Class="TabletTester2025.MainWindow"
|
<Window x:Class="TabletTester2025.MainWindow"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:oxy="http://oxyplot.org/wpf"
|
xmlns:oxy="http://oxyplot.org/wpf"
|
||||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
|
||||||
xmlns:helpers="clr-namespace:TabletTester2025.Helpers"
|
xmlns:helpers="clr-namespace:TabletTester2025.Helpers"
|
||||||
Title="片剂四用仪 (中国药典2025)" Width="1024" MinHeight="768" WindowState="Maximized"
|
Title="片剂四用仪 (中国药典2025)"
|
||||||
|
Width="1024"
|
||||||
|
MinHeight="768"
|
||||||
|
WindowState="Maximized"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
Background="#F0F2F5">
|
Background="#EDF1F5">
|
||||||
<Window.Resources>
|
<Window.Resources>
|
||||||
<helpers:BasketOffsetConverter x:Key="BasketOffsetConverter"/>
|
<helpers:BasketOffsetConverter x:Key="BasketOffsetConverter"/>
|
||||||
<helpers:PassToTextConverter x:Key="PassToTextConverter"/>
|
<helpers:PassToTextConverter x:Key="PassToTextConverter"/>
|
||||||
<helpers:BoolToColorConverter x:Key="BoolToColorConverter"/>
|
<helpers:BoolToColorConverter x:Key="BoolToColorConverter"/>
|
||||||
<helpers:StatusColorConverter x:Key="StatusColorConverter"/>
|
<helpers:StatusColorConverter x:Key="StatusColorConverter"/>
|
||||||
<DropShadowEffect x:Key="DropShadowLight" BlurRadius="5" ShadowDepth="2" Opacity="0.2"/>
|
|
||||||
<!-- 统一按钮样式 -->
|
<DropShadowEffect x:Key="DropShadowLight" BlurRadius="6" ShadowDepth="1" Opacity="0.14"/>
|
||||||
|
|
||||||
|
<SolidColorBrush x:Key="PrimaryBrush" Color="#1565A9"/>
|
||||||
|
<SolidColorBrush x:Key="PanelBorderBrush" Color="#D8E1EC"/>
|
||||||
|
<SolidColorBrush x:Key="PanelBackgroundBrush" Color="#FFFFFF"/>
|
||||||
|
<SolidColorBrush x:Key="SubtleBackgroundBrush" Color="#F7FAFC"/>
|
||||||
|
<SolidColorBrush x:Key="LabelBrush" Color="#526273"/>
|
||||||
|
<SolidColorBrush x:Key="ValueBrush" Color="#102A43"/>
|
||||||
|
|
||||||
<Style TargetType="Button" x:Key="ActionButton">
|
<Style TargetType="Button" x:Key="ActionButton">
|
||||||
<Setter Property="MinWidth" Value="92"/>
|
<Setter Property="MinWidth" Value="112"/>
|
||||||
<Setter Property="Height" Value="36"/>
|
<Setter Property="Height" Value="42"/>
|
||||||
<Setter Property="Margin" Value="6,0"/>
|
<Setter Property="Margin" Value="6"/>
|
||||||
|
<Setter Property="Padding" Value="14,0"/>
|
||||||
<Setter Property="Cursor" Value="Hand"/>
|
<Setter Property="Cursor" Value="Hand"/>
|
||||||
<Setter Property="Background" Value="#2196F3"/>
|
<Setter Property="Background" Value="{StaticResource PrimaryBrush}"/>
|
||||||
<Setter Property="Foreground" Value="White"/>
|
<Setter Property="Foreground" Value="White"/>
|
||||||
<Setter Property="BorderThickness" Value="0"/>
|
<Setter Property="BorderThickness" Value="0"/>
|
||||||
|
<Setter Property="FontSize" Value="15"/>
|
||||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
<ControlTemplate TargetType="Button">
|
<ControlTemplate TargetType="Button">
|
||||||
<Border Background="{TemplateBinding Background}" CornerRadius="4" Padding="8,0">
|
<Border Background="{TemplateBinding Background}" CornerRadius="4" Padding="{TemplateBinding Padding}">
|
||||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
</Border>
|
</Border>
|
||||||
<ControlTemplate.Triggers>
|
<ControlTemplate.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter Property="Opacity" Value="0.9"/>
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="IsPressed" Value="True">
|
||||||
|
<Setter Property="Opacity" Value="0.78"/>
|
||||||
|
</Trigger>
|
||||||
<Trigger Property="IsEnabled" Value="False">
|
<Trigger Property="IsEnabled" Value="False">
|
||||||
<Setter Property="Opacity" Value="0.45"/>
|
<Setter Property="Opacity" Value="0.45"/>
|
||||||
</Trigger>
|
</Trigger>
|
||||||
@@ -39,6 +57,22 @@
|
|||||||
</Setter>
|
</Setter>
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="Button" x:Key="StartButton" BasedOn="{StaticResource ActionButton}">
|
||||||
|
<Setter Property="Background" Value="#2E7D32"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="Button" x:Key="StopButton" BasedOn="{StaticResource ActionButton}">
|
||||||
|
<Setter Property="Background" Value="#C62828"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="Button" x:Key="ResetButton" BasedOn="{StaticResource ActionButton}">
|
||||||
|
<Setter Property="Background" Value="#687789"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="Button" x:Key="SecondaryButton" BasedOn="{StaticResource ActionButton}">
|
||||||
|
<Setter Property="Background" Value="#496579"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
<Style TargetType="TabControl">
|
<Style TargetType="TabControl">
|
||||||
<Setter Property="Background" Value="Transparent"/>
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
<Setter Property="BorderThickness" Value="0"/>
|
<Setter Property="BorderThickness" Value="0"/>
|
||||||
@@ -46,21 +80,26 @@
|
|||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style TargetType="TabItem">
|
<Style TargetType="TabItem">
|
||||||
<Setter Property="FontSize" Value="15"/>
|
<Setter Property="FontSize" Value="16"/>
|
||||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||||
<Setter Property="Padding" Value="22,10"/>
|
<Setter Property="Padding" Value="24,12"/>
|
||||||
<Setter Property="Foreground" Value="#475569"/>
|
<Setter Property="Foreground" Value="#465A6E"/>
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
<ControlTemplate TargetType="TabItem">
|
<ControlTemplate TargetType="TabItem">
|
||||||
<Border x:Name="TabBorder" Background="#F8FAFC" BorderBrush="#D6DEE8" BorderThickness="1,1,1,0"
|
<Border x:Name="TabBorder"
|
||||||
CornerRadius="6,6,0,0" Padding="{TemplateBinding Padding}" Margin="0,0,4,0">
|
Background="#F6F9FC"
|
||||||
|
BorderBrush="#D3DEEA"
|
||||||
|
BorderThickness="1,1,1,0"
|
||||||
|
CornerRadius="6,6,0,0"
|
||||||
|
Padding="{TemplateBinding Padding}"
|
||||||
|
Margin="0,0,6,0">
|
||||||
<ContentPresenter ContentSource="Header" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
<ContentPresenter ContentSource="Header" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
</Border>
|
</Border>
|
||||||
<ControlTemplate.Triggers>
|
<ControlTemplate.Triggers>
|
||||||
<Trigger Property="IsSelected" Value="True">
|
<Trigger Property="IsSelected" Value="True">
|
||||||
<Setter TargetName="TabBorder" Property="Background" Value="White"/>
|
<Setter TargetName="TabBorder" Property="Background" Value="White"/>
|
||||||
<Setter TargetName="TabBorder" Property="BorderBrush" Value="#1976D2"/>
|
<Setter TargetName="TabBorder" Property="BorderBrush" Value="{StaticResource PrimaryBrush}"/>
|
||||||
<Setter Property="Foreground" Value="#0F3D68"/>
|
<Setter Property="Foreground" Value="#0F3D68"/>
|
||||||
</Trigger>
|
</Trigger>
|
||||||
</ControlTemplate.Triggers>
|
</ControlTemplate.Triggers>
|
||||||
@@ -70,30 +109,123 @@
|
|||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style TargetType="GroupBox">
|
<Style TargetType="GroupBox">
|
||||||
<Setter Property="BorderBrush" Value="#D7DEE8"/>
|
<Setter Property="BorderBrush" Value="{StaticResource PanelBorderBrush}"/>
|
||||||
<Setter Property="BorderThickness" Value="1"/>
|
<Setter Property="BorderThickness" Value="1"/>
|
||||||
<Setter Property="Padding" Value="10"/>
|
<Setter Property="Padding" Value="14"/>
|
||||||
<Setter Property="Margin" Value="0,8"/>
|
<Setter Property="Margin" Value="0,0,0,12"/>
|
||||||
|
<Setter Property="FontSize" Value="15"/>
|
||||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||||
|
<Setter Property="Background" Value="{StaticResource PanelBackgroundBrush}"/>
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style TargetType="TextBox">
|
<Style TargetType="TextBox">
|
||||||
<Setter Property="Height" Value="30"/>
|
<Setter Property="Width" Value="110"/>
|
||||||
<Setter Property="Padding" Value="6,2"/>
|
<Setter Property="Height" Value="36"/>
|
||||||
|
<Setter Property="Padding" Value="8,2"/>
|
||||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
||||||
<Setter Property="BorderBrush" Value="#B8C4D4"/>
|
<Setter Property="BorderBrush" Value="#B7C4D2"/>
|
||||||
<Setter Property="BorderThickness" Value="1"/>
|
<Setter Property="BorderThickness" Value="1"/>
|
||||||
|
<Setter Property="FontSize" Value="15"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="RadioButton">
|
||||||
|
<Setter Property="FontSize" Value="15"/>
|
||||||
|
<Setter Property="Margin" Value="0,0,22,0"/>
|
||||||
|
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="TextBlock" x:Key="ParamLabel">
|
||||||
|
<Setter Property="Width" Value="150"/>
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource LabelBrush}"/>
|
||||||
|
<Setter Property="FontSize" Value="15"/>
|
||||||
|
<Setter Property="FontWeight" Value="Normal"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="StackPanel" x:Key="ParamRow">
|
||||||
|
<Setter Property="Orientation" Value="Horizontal"/>
|
||||||
|
<Setter Property="Margin" Value="0,6,26,6"/>
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="Border" x:Key="MetricCard">
|
||||||
|
<Setter Property="Background" Value="{StaticResource SubtleBackgroundBrush}"/>
|
||||||
|
<Setter Property="BorderBrush" Value="#E0E7EF"/>
|
||||||
|
<Setter Property="BorderThickness" Value="1"/>
|
||||||
|
<Setter Property="CornerRadius" Value="6"/>
|
||||||
|
<Setter Property="Padding" Value="12,10"/>
|
||||||
|
<Setter Property="Margin" Value="6"/>
|
||||||
|
<Setter Property="MinHeight" Value="86"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="TextBlock" x:Key="MetricLabel">
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource LabelBrush}"/>
|
||||||
|
<Setter Property="FontSize" Value="14"/>
|
||||||
|
<Setter Property="FontWeight" Value="Normal"/>
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Center"/>
|
||||||
|
<Setter Property="TextAlignment" Value="Center"/>
|
||||||
|
<Setter Property="TextWrapping" Value="Wrap"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="TextBlock" x:Key="MetricValue">
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource ValueBrush}"/>
|
||||||
|
<Setter Property="FontSize" Value="26"/>
|
||||||
|
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Center"/>
|
||||||
|
<Setter Property="TextAlignment" Value="Center"/>
|
||||||
|
<Setter Property="Margin" Value="0,8,0,0"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="WrapPanel" x:Key="CommandBar">
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Center"/>
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||||
|
<Setter Property="Margin" Value="0,8,0,0"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="Border" x:Key="FooterItem">
|
||||||
|
<Setter Property="Background" Value="#F8FBFD"/>
|
||||||
|
<Setter Property="BorderBrush" Value="#DFE7EF"/>
|
||||||
|
<Setter Property="BorderThickness" Value="1"/>
|
||||||
|
<Setter Property="CornerRadius" Value="5"/>
|
||||||
|
<Setter Property="Padding" Value="12,7"/>
|
||||||
|
<Setter Property="MinHeight" Value="36"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="TextBlock" x:Key="FooterLabel">
|
||||||
|
<Setter Property="Foreground" Value="#647487"/>
|
||||||
|
<Setter Property="FontSize" Value="13"/>
|
||||||
|
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="TextBlock" x:Key="FooterValue">
|
||||||
|
<Setter Property="Foreground" Value="#102A43"/>
|
||||||
|
<Setter Property="FontSize" Value="14"/>
|
||||||
|
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<!-- 四功能测试工作区模板 -->
|
|
||||||
<DataTemplate x:Key="StationCardTemplate">
|
<DataTemplate x:Key="StationCardTemplate">
|
||||||
<Border BorderBrush="#D7DEE8" BorderThickness="1" CornerRadius="8" Margin="0" Padding="8,6,8,8"
|
<Border BorderBrush="{StaticResource PanelBorderBrush}"
|
||||||
Background="White" Effect="{StaticResource DropShadowLight}">
|
BorderThickness="1"
|
||||||
<StackPanel>
|
CornerRadius="8"
|
||||||
<TextBlock Text="{Binding LocalAlarm}" FontWeight="Bold" Margin="0,0,0,6" HorizontalAlignment="Center">
|
Padding="12"
|
||||||
|
Background="White"
|
||||||
|
Effect="{StaticResource DropShadowLight}">
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Text="{Binding LocalAlarm}"
|
||||||
|
FontSize="15"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Margin="0,0,0,8"
|
||||||
|
HorizontalAlignment="Center">
|
||||||
<TextBlock.Style>
|
<TextBlock.Style>
|
||||||
<Style TargetType="TextBlock">
|
<Style TargetType="TextBlock">
|
||||||
<Setter Property="Foreground" Value="Red"/>
|
<Setter Property="Foreground" Value="#C62828"/>
|
||||||
<Setter Property="Visibility" Value="Visible"/>
|
<Setter Property="Visibility" Value="Visible"/>
|
||||||
<Style.Triggers>
|
<Style.Triggers>
|
||||||
<DataTrigger Binding="{Binding LocalAlarm}" Value="{x:Null}">
|
<DataTrigger Binding="{Binding LocalAlarm}" Value="{x:Null}">
|
||||||
@@ -102,281 +234,316 @@
|
|||||||
<DataTrigger Binding="{Binding LocalAlarm}" Value="">
|
<DataTrigger Binding="{Binding LocalAlarm}" Value="">
|
||||||
<Setter Property="Visibility" Value="Collapsed"/>
|
<Setter Property="Visibility" Value="Collapsed"/>
|
||||||
</DataTrigger>
|
</DataTrigger>
|
||||||
<!-- 当文本包含“合格”时变为绿色 -->
|
|
||||||
<DataTrigger Binding="{Binding LocalAlarm}" Value="硬度测试合格">
|
<DataTrigger Binding="{Binding LocalAlarm}" Value="硬度测试合格">
|
||||||
<Setter Property="Foreground" Value="Green"/>
|
<Setter Property="Foreground" Value="#2E7D32"/>
|
||||||
</DataTrigger>
|
</DataTrigger>
|
||||||
<DataTrigger Binding="{Binding LocalAlarm}" Value="脆碎度测试合格">
|
<DataTrigger Binding="{Binding LocalAlarm}" Value="脆碎度测试合格">
|
||||||
<Setter Property="Foreground" Value="Green"/>
|
<Setter Property="Foreground" Value="#2E7D32"/>
|
||||||
</DataTrigger>
|
</DataTrigger>
|
||||||
<DataTrigger Binding="{Binding LocalAlarm}" Value="崩解测试合格">
|
<DataTrigger Binding="{Binding LocalAlarm}" Value="崩解测试合格">
|
||||||
<Setter Property="Foreground" Value="Green"/>
|
<Setter Property="Foreground" Value="#2E7D32"/>
|
||||||
</DataTrigger>
|
</DataTrigger>
|
||||||
<DataTrigger Binding="{Binding LocalAlarm}" Value="溶出测试合格">
|
<DataTrigger Binding="{Binding LocalAlarm}" Value="溶出测试合格">
|
||||||
<Setter Property="Foreground" Value="Green"/>
|
<Setter Property="Foreground" Value="#2E7D32"/>
|
||||||
</DataTrigger>
|
</DataTrigger>
|
||||||
<DataTrigger Binding="{Binding LocalAlarm}" Value="溶出1测试合格">
|
<DataTrigger Binding="{Binding LocalAlarm}" Value="溶出1测试合格">
|
||||||
<Setter Property="Foreground" Value="Green"/>
|
<Setter Property="Foreground" Value="#2E7D32"/>
|
||||||
</DataTrigger>
|
</DataTrigger>
|
||||||
<DataTrigger Binding="{Binding LocalAlarm}" Value="溶出2测试合格">
|
<DataTrigger Binding="{Binding LocalAlarm}" Value="溶出2测试合格">
|
||||||
<Setter Property="Foreground" Value="Green"/>
|
<Setter Property="Foreground" Value="#2E7D32"/>
|
||||||
</DataTrigger>
|
</DataTrigger>
|
||||||
</Style.Triggers>
|
</Style.Triggers>
|
||||||
</Style>
|
</Style>
|
||||||
</TextBlock.Style>
|
</TextBlock.Style>
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
|
|
||||||
<TabControl FontSize="13" BorderThickness="0">
|
<TabControl Grid.Row="1" FontSize="13" BorderThickness="0">
|
||||||
<!-- ========== 硬度测试 ========== -->
|
|
||||||
<TabItem Header="硬度测试">
|
<TabItem Header="硬度测试">
|
||||||
<Grid Margin="4,6,4,4">
|
<Grid Margin="4,14,4,4">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<GroupBox Header="测试参数设置" Grid.Row="0">
|
||||||
|
<WrapPanel>
|
||||||
|
<StackPanel Style="{StaticResource ParamRow}">
|
||||||
|
<TextBlock Text="试验次数设定:" Style="{StaticResource ParamLabel}"/>
|
||||||
|
<TextBox Text="{Binding HardnessTestCount, UpdateSourceTrigger=PropertyChanged}"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Style="{StaticResource ParamRow}">
|
||||||
|
<TextBlock Text="试验次数间隔(秒):" Style="{StaticResource ParamLabel}"/>
|
||||||
|
<TextBox Text="{Binding HardnessIntervalSec, UpdateSourceTrigger=PropertyChanged}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</WrapPanel>
|
||||||
|
</GroupBox>
|
||||||
|
|
||||||
|
<Grid Grid.Row="1">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<!-- 参数设置区 -->
|
<GroupBox Header="测试结果" Grid.Row="0">
|
||||||
<GroupBox Header="测试参数设置" Grid.Row="0" Margin="0,5">
|
<UniformGrid Columns="3">
|
||||||
<UniformGrid Columns="1" Rows="2" Margin="10">
|
<Border Style="{StaticResource MetricCard}">
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,10,0,0" >
|
|
||||||
<TextBlock Text="试验次数设定:" Width="100" VerticalAlignment="Center"/>
|
|
||||||
<TextBox Text="{Binding HardnessTestCount, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,10,0,0">
|
|
||||||
<TextBlock Text="试验次数间隔(秒):" Width="100" VerticalAlignment="Center"/>
|
|
||||||
<TextBox Text="{Binding HardnessIntervalSec, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
|
||||||
</StackPanel>
|
|
||||||
</UniformGrid>
|
|
||||||
</GroupBox>
|
|
||||||
|
|
||||||
<!-- 测试结果区 -->
|
|
||||||
<GroupBox Header="测试结果" Grid.Row="1" Margin="0,5">
|
|
||||||
<UniformGrid Columns="3" Rows="1" Margin="10">
|
|
||||||
<StackPanel HorizontalAlignment="Center">
|
|
||||||
<TextBlock Text="最大值(N)" FontWeight="SemiBold" HorizontalAlignment="Center"/>
|
|
||||||
<TextBlock Text="{Binding HardnessMax, StringFormat=F1}" FontSize="22" Foreground="Blue" HorizontalAlignment="Center"/>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel HorizontalAlignment="Center">
|
|
||||||
<TextBlock Text="最小值(N)" FontWeight="SemiBold" HorizontalAlignment="Center"/>
|
|
||||||
<TextBlock Text="{Binding HardnessMin, StringFormat=F1}" FontSize="22" Foreground="Blue" HorizontalAlignment="Center"/>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel HorizontalAlignment="Center">
|
|
||||||
<TextBlock Text="平均值(N)" FontWeight="SemiBold" HorizontalAlignment="Center"/>
|
|
||||||
<TextBlock Text="{Binding HardnessAvg, StringFormat=F1}" FontSize="22" Foreground="Green" HorizontalAlignment="Center"/>
|
|
||||||
</StackPanel>
|
|
||||||
</UniformGrid>
|
|
||||||
</GroupBox>
|
|
||||||
|
|
||||||
<!-- 参数状态显示区 -->
|
|
||||||
<GroupBox Header="参数状态显示" Grid.Row="2" Margin="0,5">
|
|
||||||
<UniformGrid Columns="2" Rows="1" Margin="10">
|
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Text="试验次数" HorizontalAlignment="Center"/>
|
<TextBlock Text="最大值(N)" Style="{StaticResource MetricLabel}"/>
|
||||||
<TextBlock Text="{Binding HardnessCurrentCount, StringFormat=F0}" FontSize="18" HorizontalAlignment="Center"/>
|
<TextBlock Text="{Binding HardnessMax, StringFormat=F1}" Foreground="#1565C0" Style="{StaticResource MetricValue}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
<Border Style="{StaticResource MetricCard}">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Text="测试力值(N)" HorizontalAlignment="Center"/>
|
<TextBlock Text="最小值(N)" Style="{StaticResource MetricLabel}"/>
|
||||||
<TextBlock Text="{Binding HardnessValue, StringFormat=F1}" FontSize="18" Foreground="#FF9800" HorizontalAlignment="Center"/>
|
<TextBlock Text="{Binding HardnessMin, StringFormat=F1}" Foreground="#1565C0" Style="{StaticResource MetricValue}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
<Border Style="{StaticResource MetricCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="平均值(N)" Style="{StaticResource MetricLabel}"/>
|
||||||
|
<TextBlock Text="{Binding HardnessAvg, StringFormat=F1}" Foreground="#2E7D32" Style="{StaticResource MetricValue}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
</UniformGrid>
|
</UniformGrid>
|
||||||
</GroupBox>
|
</GroupBox>
|
||||||
|
|
||||||
<!-- 按钮区 -->
|
<GroupBox Header="参数状态显示" Grid.Row="1">
|
||||||
<WrapPanel Grid.Row="3" HorizontalAlignment="Center" Margin="0,10">
|
<UniformGrid Columns="2">
|
||||||
<!--<Button Command="{Binding HardnessUpCommand}" Content="梁杆上升" Style="{StaticResource ActionButton}" Background="#FF9800" Margin="5 10 5 10"/>
|
<Border Style="{StaticResource MetricCard}">
|
||||||
<Button Command="{Binding HardnessDownCommand}" Content="梁杆下降" Style="{StaticResource ActionButton}" Background="#FF9800"/>-->
|
<StackPanel>
|
||||||
<Button Command="{Binding HardnessResetCommand}" Content="复位" Style="{StaticResource ActionButton}" Background="#9E9E9E"/>
|
<TextBlock Text="试验次数" Style="{StaticResource MetricLabel}"/>
|
||||||
<Button Command="{Binding PrintHardnessCommand}" Content="打印" Style="{StaticResource ActionButton}" Background="#607D8B"/>
|
<TextBlock Text="{Binding HardnessCurrentCount, StringFormat=F0}" Style="{StaticResource MetricValue}"/>
|
||||||
<Button Command="{Binding StartHardnessCommand}" Content="启动测试" Style="{StaticResource ActionButton}" Background="#4CAF50" Margin="5 0 0 0"/>
|
</StackPanel>
|
||||||
<Button Command="{Binding StopHardnessCommand}" Content="测试停止" Style="{StaticResource ActionButton}" Background="#F44336"/>
|
</Border>
|
||||||
|
<Border Style="{StaticResource MetricCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="测试力值(N)" Style="{StaticResource MetricLabel}"/>
|
||||||
|
<TextBlock Text="{Binding HardnessValue, StringFormat=F1}" Foreground="#D98200" Style="{StaticResource MetricValue}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</UniformGrid>
|
||||||
|
</GroupBox>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<WrapPanel Grid.Row="2" Style="{StaticResource CommandBar}">
|
||||||
|
<Button Command="{Binding HardnessResetCommand}" Content="复位" Style="{StaticResource ResetButton}"/>
|
||||||
|
<Button Command="{Binding PrintHardnessCommand}" Content="打印" Style="{StaticResource SecondaryButton}"/>
|
||||||
|
<Button Command="{Binding StartHardnessCommand}" Content="启动测试" Style="{StaticResource StartButton}"/>
|
||||||
|
<Button Command="{Binding StopHardnessCommand}" Content="测试停止" Style="{StaticResource StopButton}"/>
|
||||||
</WrapPanel>
|
</WrapPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
||||||
<!-- ========== 脆碎度测试 ========== -->
|
|
||||||
<TabItem Header="脆碎度测试">
|
<TabItem Header="脆碎度测试">
|
||||||
<Grid Margin="4,6,4,4">
|
<Grid Margin="4,14,4,4">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
<RowDefinition Height="Auto"/>
|
|
||||||
|
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<GroupBox Header="测试参数设置" Grid.Row="0" Margin="0,5">
|
<GroupBox Header="测试参数设置" Grid.Row="0">
|
||||||
<UniformGrid Columns="1" Rows="3" Margin="10" >
|
<WrapPanel>
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,15,0,0" >
|
<StackPanel Style="{StaticResource ParamRow}">
|
||||||
<TextBlock Text="转速设置(r/min):" Width="110" VerticalAlignment="Center"/>
|
<TextBlock Text="转速设置(r/min):" Style="{StaticResource ParamLabel}"/>
|
||||||
<TextBox Text="{Binding FriabilityTargetRpm, UpdateSourceTrigger=PropertyChanged}" Width="100"/>
|
<TextBox Text="{Binding FriabilityTargetRpm, UpdateSourceTrigger=PropertyChanged}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,15,0,0" >
|
<StackPanel Style="{StaticResource ParamRow}">
|
||||||
<TextBlock Text="转数设置(秒):" Width="110" VerticalAlignment="Center"/>
|
<TextBlock Text="转数设置(秒):" Style="{StaticResource ParamLabel}"/>
|
||||||
<TextBox Text="{Binding FriabilityTargetTimeSec, UpdateSourceTrigger=PropertyChanged}" Width="100"/>
|
<TextBox Text="{Binding FriabilityTargetTimeSec, UpdateSourceTrigger=PropertyChanged}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,10,0,0" Width="280" >
|
<StackPanel Style="{StaticResource ParamRow}">
|
||||||
<TextBlock Text="方向:" Width="80" VerticalAlignment="Center" Margin="0 10 0 0"/>
|
<TextBlock Text="方向:" Style="{StaticResource ParamLabel}"/>
|
||||||
<RadioButton Content="顺时针" IsChecked="{Binding FriabilityClockwise}" Margin="0,10,10,0"/>
|
<RadioButton Content="顺时针" IsChecked="{Binding FriabilityClockwise}"/>
|
||||||
<RadioButton Content="逆时针" IsChecked="{Binding FriabilityCounterClockwise}" Margin="0 10 0 0"/>
|
<RadioButton Content="逆时针" IsChecked="{Binding FriabilityCounterClockwise}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
</WrapPanel>
|
||||||
|
</GroupBox>
|
||||||
|
|
||||||
|
<GroupBox Header="参数状态显示" Grid.Row="1">
|
||||||
|
<UniformGrid Columns="2">
|
||||||
|
<Border Style="{StaticResource MetricCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="转速显示(r/min)" Style="{StaticResource MetricLabel}"/>
|
||||||
|
<TextBlock Text="{Binding FriabilityCurrentRpm, StringFormat=F1}" Style="{StaticResource MetricValue}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
<Border Style="{StaticResource MetricCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="剩余圈数" Style="{StaticResource MetricLabel}"/>
|
||||||
|
<TextBlock Text="{Binding FriabilityRemainingRounds}" Foreground="#1565C0" Style="{StaticResource MetricValue}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
</UniformGrid>
|
</UniformGrid>
|
||||||
</GroupBox>
|
</GroupBox>
|
||||||
|
|
||||||
<GroupBox Header="参数状态显示" Grid.Row="1" Margin="0,45,0,15">
|
<WrapPanel Grid.Row="2" Style="{StaticResource CommandBar}">
|
||||||
<UniformGrid Columns="2" Rows="1" Margin="10">
|
<Button Command="{Binding StartFriabilityCommand}" Content="测试启动" Style="{StaticResource StartButton}"/>
|
||||||
<StackPanel>
|
<Button Command="{Binding StopFriabilityCommand}" Content="测试停止" Style="{StaticResource StopButton}"/>
|
||||||
<TextBlock Text="转速显示(r/min)" HorizontalAlignment="Center"/>
|
<Button Command="{Binding ResetFriabilityCommand}" Content="复位" Style="{StaticResource ResetButton}"/>
|
||||||
<TextBlock Text="{Binding FriabilityCurrentRpm, StringFormat=F1}" FontSize="18" HorizontalAlignment="Center"/>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock Text="剩余圈数" HorizontalAlignment="Center"/>
|
|
||||||
<TextBlock Text="{Binding FriabilityRemainingRounds}" FontSize="18" HorizontalAlignment="Center"/>
|
|
||||||
</StackPanel>
|
|
||||||
</UniformGrid>
|
|
||||||
</GroupBox>
|
|
||||||
|
|
||||||
<WrapPanel Grid.Row="2" HorizontalAlignment="Center" Margin="0,10">
|
|
||||||
<Button Command="{Binding StartFriabilityCommand}" Content="测试启动" Style="{StaticResource ActionButton}" Background="#4CAF50" Margin="5 10 5 10"/>
|
|
||||||
<Button Command="{Binding StopFriabilityCommand}" Content="测试停止" Style="{StaticResource ActionButton}" Background="#F44336"/>
|
|
||||||
<Button Command="{Binding ResetFriabilityCommand}" Content="复位" Style="{StaticResource ActionButton}" Background="#9E9E9E"/>
|
|
||||||
<!--<Button Command="{Binding PrintFriabilityCommand}" Content="脆碎度测试记录" Style="{StaticResource ActionButton}" Background="#607D8B"/>-->
|
|
||||||
</WrapPanel>
|
</WrapPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
||||||
<!-- ========== 溶出度 ========== -->
|
|
||||||
<TabItem Header="溶出度">
|
<TabItem Header="溶出度">
|
||||||
<ScrollViewer VerticalScrollBarVisibility="Auto"
|
<Grid Margin="4,14,4,4">
|
||||||
Height="470">
|
|
||||||
<Grid Margin="4,6,4,4">
|
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="*"/>
|
||||||
<RowDefinition Height="Auto"/>
|
|
||||||
<RowDefinition Height="Auto"/>
|
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<GroupBox Header="测试参数设置" Grid.Row="0" Margin="0,5">
|
<ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Auto">
|
||||||
<UniformGrid Columns="1" Rows="2" Margin="10">
|
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,6,10,0">
|
|
||||||
<TextBlock Text="溶出1时间(min):" Width="130" VerticalAlignment="Center"/>
|
|
||||||
<TextBox Text="{Binding Dissolution1TimeMin, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,6,10,0">
|
|
||||||
<TextBlock Text="溶出2时间(min):" Width="130" VerticalAlignment="Center"/>
|
|
||||||
<TextBox Text="{Binding Dissolution2TimeMin, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
|
||||||
</StackPanel>
|
|
||||||
</UniformGrid>
|
|
||||||
</GroupBox>
|
|
||||||
|
|
||||||
<GroupBox Header="测试状态显示" Grid.Row="1" Margin="0,5">
|
|
||||||
<UniformGrid Columns="2" Rows="3" Margin="10">
|
|
||||||
<StackPanel Margin="0,5">
|
|
||||||
<TextBlock Text="水浴温度显示(℃)" HorizontalAlignment="Center"/>
|
|
||||||
<TextBlock Text="{Binding DisintegrationTemp, StringFormat=F1}" FontSize="18" Foreground="#E91E63" HorizontalAlignment="Center"/>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Margin="0,5">
|
|
||||||
<TextBlock Text="试验运行时间(min)" HorizontalAlignment="Center"/>
|
|
||||||
<TextBlock Text="{Binding DissolutionElapsedTime, StringFormat=F1}" FontSize="18" HorizontalAlignment="Center"/>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Margin="0,5">
|
|
||||||
<TextBlock Text="取样倒计时(min)" HorizontalAlignment="Center"/>
|
|
||||||
<TextBlock Text="{Binding DissolutionCountdown, StringFormat=F1}" FontSize="18" HorizontalAlignment="Center"/>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Margin="0,5">
|
|
||||||
<TextBlock Text="溶出1溶出度(%)" HorizontalAlignment="Center"/>
|
|
||||||
<TextBlock Text="{Binding Dissolution1Percent, StringFormat=F1}" FontSize="18" Foreground="#2E7D32" HorizontalAlignment="Center"/>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Margin="0,5">
|
|
||||||
<TextBlock Text="溶出2溶出度(%)" HorizontalAlignment="Center"/>
|
|
||||||
<TextBlock Text="{Binding Dissolution2Percent, StringFormat=F1}" FontSize="18" Foreground="#1565C0" HorizontalAlignment="Center"/>
|
|
||||||
</StackPanel>
|
|
||||||
</UniformGrid>
|
|
||||||
</GroupBox>
|
|
||||||
|
|
||||||
<GroupBox Header="溶出双曲线 & R²值" Grid.Row="2" Margin="0,5">
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="200"/>
|
<RowDefinition Height="Auto"/>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<oxy:PlotView Model="{Binding DissolutionPlotModel}" Height="180" Margin="5"/>
|
|
||||||
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="5,0,0,5">
|
<GroupBox Header="测试参数设置" Grid.Row="0">
|
||||||
<TextBlock Text="{Binding Dissolution1RSquared, StringFormat='溶出1 R² = {0:F4}'}"
|
<WrapPanel>
|
||||||
FontWeight="Bold" Margin="0,0,20,0"/>
|
<StackPanel Style="{StaticResource ParamRow}">
|
||||||
<TextBlock Text="{Binding Dissolution2RSquared, StringFormat='溶出2 R² = {0:F4}'}"
|
<TextBlock Text="溶出1时间(min):" Style="{StaticResource ParamLabel}"/>
|
||||||
FontWeight="Bold"/>
|
<TextBox Text="{Binding Dissolution1TimeMin, UpdateSourceTrigger=PropertyChanged}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<TextBlock Grid.Row="2" Text="{Binding DissolutionCurveStatus}" Foreground="#D32F2F" FontWeight="SemiBold" Margin="5,0,0,5"/>
|
<StackPanel Style="{StaticResource ParamRow}">
|
||||||
</Grid>
|
<TextBlock Text="溶出2时间(min):" Style="{StaticResource ParamLabel}"/>
|
||||||
|
<TextBox Text="{Binding Dissolution2TimeMin, UpdateSourceTrigger=PropertyChanged}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</WrapPanel>
|
||||||
</GroupBox>
|
</GroupBox>
|
||||||
|
|
||||||
<WrapPanel Grid.Row="3" HorizontalAlignment="Center" Margin="0,10">
|
<GroupBox Header="测试状态显示" Grid.Row="1">
|
||||||
<Button Command="{Binding StartDissolution1Command}" Content="溶出1开始" Style="{StaticResource ActionButton}" Background="#4CAF50" Margin="5 10 5 10"/>
|
<UniformGrid Columns="5">
|
||||||
<Button Command="{Binding StopDissolution1Command}" Content="溶出1停止" Style="{StaticResource ActionButton}" Background="#F44336"/>
|
<Border Style="{StaticResource MetricCard}">
|
||||||
<Button Command="{Binding StartDissolution2Command}" Content="溶出2开始" Style="{StaticResource ActionButton}" Background="#4CAF50"/>
|
<StackPanel>
|
||||||
<Button Command="{Binding StopDissolution2Command}" Content="溶出2停止" Style="{StaticResource ActionButton}" Background="#F44336"/>
|
<TextBlock Text="水浴温度显示(℃)" Style="{StaticResource MetricLabel}"/>
|
||||||
<!--<Button Command="{Binding PrintDissolutionCommand}" Content="溶出度测试记录" Style="{StaticResource ActionButton}" Background="#607D8B" Margin="5 10 0 0"/>-->
|
<TextBlock Text="{Binding DisintegrationTemp, StringFormat=F1}" Foreground="#C2185B" Style="{StaticResource MetricValue}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
<Border Style="{StaticResource MetricCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="试验运行时间(min)" Style="{StaticResource MetricLabel}"/>
|
||||||
|
<TextBlock Text="{Binding DissolutionElapsedTime, StringFormat=F1}" Style="{StaticResource MetricValue}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
<Border Style="{StaticResource MetricCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="取样倒计时(min)" Style="{StaticResource MetricLabel}"/>
|
||||||
|
<TextBlock Text="{Binding DissolutionCountdown, StringFormat=F1}" Foreground="#D98200" Style="{StaticResource MetricValue}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
<Border Style="{StaticResource MetricCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="溶出1溶出度(%)" Style="{StaticResource MetricLabel}"/>
|
||||||
|
<TextBlock Text="{Binding Dissolution1Percent, StringFormat=F1}" Foreground="#2E7D32" Style="{StaticResource MetricValue}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
<Border Style="{StaticResource MetricCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="溶出2溶出度(%)" Style="{StaticResource MetricLabel}"/>
|
||||||
|
<TextBlock Text="{Binding Dissolution2Percent, StringFormat=F1}" Foreground="#1565C0" Style="{StaticResource MetricValue}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</UniformGrid>
|
||||||
|
</GroupBox>
|
||||||
|
|
||||||
|
<GroupBox Header="溶出双曲线和R²值" Grid.Row="2">
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="240"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<oxy:PlotView Grid.Row="0" Model="{Binding DissolutionPlotModel}" Margin="4"/>
|
||||||
|
<WrapPanel Grid.Row="1" Margin="4,8,4,2">
|
||||||
|
<TextBlock Text="{Binding Dissolution1RSquared, StringFormat='溶出1 R² = {0:F4}'}"
|
||||||
|
FontSize="15"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Foreground="#2E7D32"
|
||||||
|
Margin="0,0,28,0"/>
|
||||||
|
<TextBlock Text="{Binding Dissolution2RSquared, StringFormat='溶出2 R² = {0:F4}'}"
|
||||||
|
FontSize="15"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Foreground="#1565C0"/>
|
||||||
</WrapPanel>
|
</WrapPanel>
|
||||||
|
<TextBlock Grid.Row="2"
|
||||||
|
Text="{Binding DissolutionCurveStatus}"
|
||||||
|
Foreground="#C62828"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Margin="4,4,4,0"
|
||||||
|
TextWrapping="Wrap"/>
|
||||||
|
</Grid>
|
||||||
|
</GroupBox>
|
||||||
</Grid>
|
</Grid>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
|
|
||||||
|
<WrapPanel Grid.Row="1" Style="{StaticResource CommandBar}">
|
||||||
|
<Button Command="{Binding StartDissolution1Command}" Content="溶出1开始" Style="{StaticResource StartButton}"/>
|
||||||
|
<Button Command="{Binding StopDissolution1Command}" Content="溶出1停止" Style="{StaticResource StopButton}"/>
|
||||||
|
<Button Command="{Binding StartDissolution2Command}" Content="溶出2开始" Style="{StaticResource StartButton}"/>
|
||||||
|
<Button Command="{Binding StopDissolution2Command}" Content="溶出2停止" Style="{StaticResource StopButton}"/>
|
||||||
|
</WrapPanel>
|
||||||
|
</Grid>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
||||||
<!-- ========== 崩解时限 ========== -->
|
|
||||||
<TabItem Header="崩解时限">
|
<TabItem Header="崩解时限">
|
||||||
<Grid Margin="4,6,4,4">
|
<Grid Margin="4,14,4,4">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="*"/>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<GroupBox Header="测试参数设置" Grid.Row="0" Margin="0,5">
|
<GroupBox Header="测试参数设置" Grid.Row="0">
|
||||||
<UniformGrid Columns="1" Rows="2" Margin="10">
|
<WrapPanel>
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
<StackPanel Style="{StaticResource ParamRow}">
|
||||||
<TextBlock Text="崩速度(r/min):" Width="130" VerticalAlignment="Center"/>
|
<TextBlock Text="崩速度(r/min):" Style="{StaticResource ParamLabel}"/>
|
||||||
<TextBox Text="{Binding DisintegrationSpeedRpm, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
<TextBox Text="{Binding DisintegrationSpeedRpm, UpdateSourceTrigger=PropertyChanged}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
<StackPanel Style="{StaticResource ParamRow}">
|
||||||
<TextBlock Text="崩解时间(min):" Width="130" VerticalAlignment="Center"/>
|
<TextBlock Text="崩解时间(min):" Style="{StaticResource ParamLabel}"/>
|
||||||
<TextBox Text="{Binding DisintegrationTimeMin, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
<TextBox Text="{Binding DisintegrationTimeMin, UpdateSourceTrigger=PropertyChanged}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
</WrapPanel>
|
||||||
|
</GroupBox>
|
||||||
|
|
||||||
|
<GroupBox Header="测试状态显示" Grid.Row="1">
|
||||||
|
<UniformGrid Columns="3">
|
||||||
|
<Border Style="{StaticResource MetricCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="水浴温度显示(℃)" Style="{StaticResource MetricLabel}"/>
|
||||||
|
<TextBlock Text="{Binding DisintegrationTemp, StringFormat=F1}" Foreground="#C2185B" Style="{StaticResource MetricValue}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
<Border Style="{StaticResource MetricCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="试验运行时间(秒)" Style="{StaticResource MetricLabel}"/>
|
||||||
|
<TextBlock Text="{Binding DisintegrationSeconds}" Style="{StaticResource MetricValue}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
<Border Style="{StaticResource MetricCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="剩余未崩解管数" Style="{StaticResource MetricLabel}"/>
|
||||||
|
<TextBlock Text="{Binding RemainingTubes}" Foreground="#C62828" Style="{StaticResource MetricValue}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
</UniformGrid>
|
</UniformGrid>
|
||||||
</GroupBox>
|
</GroupBox>
|
||||||
|
|
||||||
<GroupBox Header="测试状态显示" Grid.Row="1" Margin="0,5">
|
<WrapPanel Grid.Row="2" Style="{StaticResource CommandBar}">
|
||||||
<UniformGrid Columns="3" Rows="1" Margin="10">
|
<Button Command="{Binding StartDisintegrationCommand}" Content="测试启动" Style="{StaticResource StartButton}"/>
|
||||||
<StackPanel>
|
<Button Command="{Binding StopDisintegrationCommand}" Content="测试停止" Style="{StaticResource StopButton}"/>
|
||||||
<TextBlock Text="水浴温度显示(℃)" HorizontalAlignment="Center"/>
|
|
||||||
<TextBlock Text="{Binding DisintegrationTemp, StringFormat=F1}" FontSize="18" Foreground="#E91E63" HorizontalAlignment="Center"/>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock Text="试验运行时间(秒)" HorizontalAlignment="Center"/>
|
|
||||||
<TextBlock Text="{Binding DisintegrationSeconds}" FontSize="18" HorizontalAlignment="Center"/>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock Text="剩余未崩解管数" HorizontalAlignment="Center"/>
|
|
||||||
<TextBlock Text="{Binding RemainingTubes}" FontSize="18" Foreground="Red" HorizontalAlignment="Center"/>
|
|
||||||
</StackPanel>
|
|
||||||
</UniformGrid>
|
|
||||||
</GroupBox>
|
|
||||||
|
|
||||||
<WrapPanel Grid.Row="2" HorizontalAlignment="Center" Margin="0,10">
|
|
||||||
<Button Command="{Binding StartDisintegrationCommand}" Content="测试启动" Style="{StaticResource ActionButton}" Background="#4CAF50"/>
|
|
||||||
<Button Command="{Binding StopDisintegrationCommand}" Content="测试停止" Style="{StaticResource ActionButton}" Background="#F44336"/>
|
|
||||||
<!--<Button Command="{Binding PrintDisintegrationCommand}" Content="崩解时限记录" Style="{StaticResource ActionButton}" Background="#607D8B"/>-->
|
|
||||||
</WrapPanel>
|
</WrapPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
</TabControl>
|
</TabControl>
|
||||||
</StackPanel>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</Window.Resources>
|
</Window.Resources>
|
||||||
|
|
||||||
<!-- 主布局 -->
|
|
||||||
<Grid Margin="10">
|
<Grid Margin="10">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
@@ -384,13 +551,14 @@
|
|||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<!-- 标题栏 -->
|
|
||||||
<Border Background="#0F3D68" CornerRadius="6" Margin="0,0,0,8" Padding="12,10">
|
<Border Background="#0F3D68" CornerRadius="6" Margin="0,0,0,8" Padding="12,10">
|
||||||
<Grid>
|
<Grid>
|
||||||
<TextBlock Text="CSI-Z420 片剂四用仪 硬度 · 脆碎度 · 溶出 · 崩解"
|
<TextBlock Text="CSI-Z420 片剂四用仪 硬度 · 脆碎度 · 溶出 · 崩解"
|
||||||
FontSize="22" FontWeight="Bold" Foreground="White" VerticalAlignment="Center"/>
|
FontSize="22"
|
||||||
|
FontWeight="Bold"
|
||||||
|
Foreground="White"
|
||||||
|
VerticalAlignment="Center"/>
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||||
<!--<Button Command="{Binding OpenSettingsCommand}" Content="⚙ 参数设置" Style="{StaticResource ActionButton}"/>-->
|
|
||||||
<Button Command="{Binding OpenHistoryCommand}" Content="历史记录" Style="{StaticResource ActionButton}"/>
|
<Button Command="{Binding OpenHistoryCommand}" Content="历史记录" Style="{StaticResource ActionButton}"/>
|
||||||
<Button Command="{Binding OpenCalibrationCommand}" Content="校准" Style="{StaticResource ActionButton}"/>
|
<Button Command="{Binding OpenCalibrationCommand}" Content="校准" Style="{StaticResource ActionButton}"/>
|
||||||
<Button Command="{Binding ExportAllCommand}" Content="导出报告" Style="{StaticResource ActionButton}"/>
|
<Button Command="{Binding ExportAllCommand}" Content="导出报告" Style="{StaticResource ActionButton}"/>
|
||||||
@@ -398,42 +566,74 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<!-- 四功能测试工作区 -->
|
<ContentControl Grid.Row="1"
|
||||||
<ContentControl Grid.Row="1" Margin="0,0,0,16"
|
Margin="0,0,0,16"
|
||||||
|
HorizontalContentAlignment="Stretch"
|
||||||
|
VerticalContentAlignment="Stretch"
|
||||||
Content="{Binding Tester}"
|
Content="{Binding Tester}"
|
||||||
ContentTemplate="{StaticResource StationCardTemplate}"/>
|
ContentTemplate="{StaticResource StationCardTemplate}"/>
|
||||||
|
|
||||||
<!-- 状态栏 -->
|
<Border Grid.Row="2"
|
||||||
<StatusBar Grid.Row="2" Background="#FFF" BorderBrush="#DDD" BorderThickness="0,1,0,0" Padding="8">
|
Background="#FFFFFF"
|
||||||
<StatusBarItem>
|
BorderBrush="#DCE5EE"
|
||||||
<StackPanel Orientation="Horizontal">
|
BorderThickness="1"
|
||||||
<Ellipse Width="10" Height="10" Fill="{Binding PlcStatus, Converter={StaticResource StatusColorConverter}}" Margin="0,0,5,0"/>
|
CornerRadius="6"
|
||||||
<TextBlock Text="PLC: "/>
|
Padding="8">
|
||||||
<TextBlock Text="{Binding PlcStatus}"/>
|
<Grid>
|
||||||
</StackPanel>
|
<Grid.ColumnDefinitions>
|
||||||
</StatusBarItem>
|
<ColumnDefinition Width="Auto"/>
|
||||||
<Separator/>
|
<ColumnDefinition Width="Auto"/>
|
||||||
<StatusBarItem>
|
<ColumnDefinition Width="*"/>
|
||||||
<StackPanel Orientation="Horizontal">
|
<ColumnDefinition Width="Auto"/>
|
||||||
<TextBlock Text="当前时间: "/>
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock Text="{Binding CurrentTime}"/>
|
|
||||||
</StackPanel>
|
|
||||||
</StatusBarItem>
|
|
||||||
<Separator/>
|
|
||||||
<StatusBarItem>
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<TextBlock Text="⚠ " Foreground="#FF5722"/>
|
|
||||||
<TextBlock Text="{Binding GlobalAlarm}" Foreground="Red"/>
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
</StatusBarItem>
|
<Border Grid.Column="0" Style="{StaticResource FooterItem}" Margin="0,0,8,0">
|
||||||
<!-- 透明的点击区域:完全看不到,但是能响应点击 -->
|
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||||
<Button Background="Transparent" Margin="10" Width="80" Height="30"
|
<Ellipse Width="10"
|
||||||
Command="{Binding ShowDataCommand}">
|
Height="10"
|
||||||
|
Fill="{Binding PlcStatus, Converter={StaticResource StatusColorConverter}}"
|
||||||
|
Margin="0,0,8,0"/>
|
||||||
|
<TextBlock Text="PLC" Style="{StaticResource FooterLabel}" Margin="0,0,8,0"/>
|
||||||
|
<TextBlock Text="{Binding PlcStatus}" Style="{StaticResource FooterValue}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
</Button>
|
<Border Grid.Column="1" Style="{StaticResource FooterItem}" Margin="0,0,8,0">
|
||||||
</StatusBar>
|
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||||
|
<TextBlock Text="当前时间" Style="{StaticResource FooterLabel}" Margin="0,0,8,0"/>
|
||||||
|
<TextBlock Text="{Binding CurrentTime}" Style="{StaticResource FooterValue}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Border Grid.Column="2" Style="{StaticResource FooterItem}">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Text="报警"
|
||||||
|
Style="{StaticResource FooterLabel}"
|
||||||
|
Foreground="#B33A3A"
|
||||||
|
Margin="0,0,8,0"/>
|
||||||
|
<TextBlock Grid.Column="1"
|
||||||
|
Text="{Binding GlobalAlarm}"
|
||||||
|
Foreground="#C62828"
|
||||||
|
FontSize="14"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
TextTrimming="CharacterEllipsis"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Button Grid.Column="3"
|
||||||
|
Background="Transparent"
|
||||||
|
BorderThickness="0"
|
||||||
|
Opacity="0"
|
||||||
|
Width="18"
|
||||||
|
Height="18"
|
||||||
|
Margin="8,0,0,0"
|
||||||
|
Command="{Binding ShowDataCommand}"/>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
</Window>
|
</Window>
|
||||||
|
|||||||
@@ -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