This commit is contained in:
GukSang.Jin
2026-05-20 14:27:16 +08:00
parent 17d9904898
commit df5c7566fb
9 changed files with 97 additions and 27 deletions

View File

@@ -60,7 +60,7 @@ namespace TabletTester2025.Services
{ {
var data = batches.ToList(); var data = batches.ToList();
var sheet = package.Workbook.Worksheets.Add("硬度报表"); var sheet = package.Workbook.Worksheets.Add("硬度报表");
WriteHeader(sheet, "检测时间", "样品名称", "平均值(N)", "平均偏差(N)", "RSD(%)", "最大值(N)", "最小值(N)", "测试次数", "单次数据(N)", "判定"); WriteHeader(sheet, "检测时间", "样品名称", "平均值(N)", "平均偏差(N)", "RSD(%)", "最大值(N)", "测试次数", "单次数据(N)", "判定");
if (data.Count == 0) if (data.Count == 0)
{ {
@@ -78,10 +78,9 @@ namespace TabletTester2025.Services
sheet.Cells[row, 4].Value = b.HardnessAverageDeviation; sheet.Cells[row, 4].Value = b.HardnessAverageDeviation;
sheet.Cells[row, 5].Value = b.HardnessRSD; sheet.Cells[row, 5].Value = b.HardnessRSD;
sheet.Cells[row, 6].Value = b.HardnessMax; sheet.Cells[row, 6].Value = b.HardnessMax;
sheet.Cells[row, 7].Value = b.HardnessMin; sheet.Cells[row, 7].Value = b.HardnessTestCount;
sheet.Cells[row, 8].Value = b.HardnessTestCount; sheet.Cells[row, 8].Value = b.HardnessSampleSummary;
sheet.Cells[row, 9].Value = b.HardnessSampleSummary; sheet.Cells[row, 9].Value = b.HardnessPassText;
sheet.Cells[row, 10].Value = b.HardnessPassText;
row++; row++;
} }

View File

@@ -17,12 +17,13 @@ namespace TabletTester2025.Services
// 模拟不同地址的数据 // 模拟不同地址的数据
float value = startAddress switch float value = startAddress switch
{ {
72 => 40 + (float)_rand.NextDouble() * 20, // 硬度最大采集力
100 => 40 + (float)_rand.NextDouble() * 20, // 硬度 40~60N 100 => 40 + (float)_rand.NextDouble() * 20, // 硬度 40~60N
410 => 100f, // 脆碎圈数 410 => 100f, // 脆碎圈数
412 => 5.0f + (float)_rand.NextDouble() * 2, // 脆碎度前重 412 => 5.0f + (float)_rand.NextDouble() * 2, // 脆碎度前重
414 => 4.9f + (float)_rand.NextDouble() * 2, // 后重 414 => 4.9f + (float)_rand.NextDouble() * 2, // 后重
416 => 1.0f, // 失重率% 416 => 1.0f, // 失重率%
300 => 37.0f, // 温度 300 => 100.0f, // 硬度加压速度(mm/min)
340 => 50f, // 溶出速度1(r/min) 340 => 50f, // 溶出速度1(r/min)
350 => 50f, // 溶出速度2(r/min) 350 => 50f, // 溶出速度2(r/min)
400 => 50 + (float)_rand.NextDouble() * 30, // 转速 400 => 50 + (float)_rand.NextDouble() * 30, // 转速

View File

@@ -64,7 +64,7 @@ namespace TabletTester2025.ViewModels
Tester.ApplyPharmaDefaults(); Tester.ApplyPharmaDefaults();
return Task.CompletedTask; return Task.CompletedTask;
}); });
OpenHistoryCommand = new AsyncRelayCommand(() => { new HistoryWindow().ShowDialog(); return Task.CompletedTask; }); OpenHistoryCommand = new AsyncRelayCommand(() => { new HistoryWindow(_db).ShowDialog(); return Task.CompletedTask; });
} }
private async Task ConnectToPlc() private async Task ConnectToPlc()

View File

@@ -1603,8 +1603,13 @@ namespace TabletTester2025.ViewModels
throw new InvalidOperationException("硬度完成线圈未配置"); throw new InvalidOperationException("硬度完成线圈未配置");
if (ResolveHardnessLiveForceRegister() == 0) if (ResolveHardnessLiveForceRegister() == 0)
throw new InvalidOperationException("硬度实时力寄存器未配置"); throw new InvalidOperationException("硬度实时力寄存器未配置");
if (ResolveHardnessMaxRegister() == 0)
throw new InvalidOperationException("硬度最大采集力寄存器未配置");
if (ResolveHardnessSpeedRegister() == 0)
throw new InvalidOperationException("硬度加压速度寄存器未配置");
await _plc.WriteFloatAsync(_plcConfig.HardnessSudu, (float)HardnessSudu); await LoadHardnessSpeedSettingAsync();
await _plc.WriteFloatAsync(ResolveHardnessSpeedRegister(), (float)HardnessSudu);
await _plc.WriteFloatAsync(_plcConfig.HardnessWeiyi, (float)HardnessWeiyi); await _plc.WriteFloatAsync(_plcConfig.HardnessWeiyi, (float)HardnessWeiyi);
while (_isHardnessRunning && _hardnessResults.Count < count) while (_isHardnessRunning && _hardnessResults.Count < count)
@@ -1617,6 +1622,7 @@ namespace TabletTester2025.ViewModels
double value = await WaitForHardnessSamplePeakAsync(completeCoil); double value = await WaitForHardnessSamplePeakAsync(completeCoil);
AddHardnessSample(value); AddHardnessSample(value);
await ReadHardnessMaxCaptureAsync();
ApplyHardnessStatistics(count); ApplyHardnessStatistics(count);
await WaitForCoilStateAsync(completeCoil, false, TimeSpan.FromSeconds(10), "硬度完成信号未回落"); await WaitForCoilStateAsync(completeCoil, false, TimeSpan.FromSeconds(10), "硬度完成信号未回落");
} }
@@ -1624,6 +1630,7 @@ namespace TabletTester2025.ViewModels
if (_hardnessResults.Count < count) if (_hardnessResults.Count < count)
throw new InvalidOperationException("硬度测试已停止,未保存结果"); throw new InvalidOperationException("硬度测试已停止,未保存结果");
await ReadHardnessMaxCaptureAsync();
ApplyHardnessStatistics(count); ApplyHardnessStatistics(count);
AddHardnessGroupSummaryRow(); AddHardnessGroupSummaryRow();
resultReady = true; resultReady = true;
@@ -1663,6 +1670,20 @@ namespace TabletTester2025.ViewModels
: (ushort)1314; : (ushort)1314;
} }
private ushort ResolveHardnessMaxRegister()
{
return _plcConfig.HardnessMax != 0
? _plcConfig.HardnessMax
: (ushort)72;
}
private ushort ResolveHardnessSpeedRegister()
{
return _plcConfig.HardnessSudu != 0
? _plcConfig.HardnessSudu
: (ushort)300;
}
private async Task<double> ReadHardnessLiveForceAsync() private async Task<double> ReadHardnessLiveForceAsync()
{ {
double value = await _plc.ReadFloatAsync(ResolveHardnessLiveForceRegister()); double value = await _plc.ReadFloatAsync(ResolveHardnessLiveForceRegister());
@@ -1673,6 +1694,23 @@ namespace TabletTester2025.ViewModels
return value; return value;
} }
private async Task<double> ReadHardnessMaxCaptureAsync()
{
double value = await _plc.ReadFloatAsync(ResolveHardnessMaxRegister());
if (!double.IsFinite(value) || value < 0)
throw new InvalidOperationException("硬度最大采集力数据异常");
HardnessMax = value;
return value;
}
private async Task LoadHardnessSpeedSettingAsync()
{
double value = await _plc.ReadFloatAsync(ResolveHardnessSpeedRegister());
if (double.IsFinite(value) && value > 0)
HardnessSudu = value;
}
private async Task<double> WaitForHardnessSamplePeakAsync(ushort completeCoil) private async Task<double> WaitForHardnessSamplePeakAsync(ushort completeCoil)
{ {
double peak = 0; double peak = 0;
@@ -1759,8 +1797,6 @@ namespace TabletTester2025.ViewModels
HardnessAvg = stats.Average; HardnessAvg = stats.Average;
HardnessAverageDeviation = stats.AverageDeviation; HardnessAverageDeviation = stats.AverageDeviation;
HardnessRSD = stats.RsdPercent; HardnessRSD = stats.RsdPercent;
HardnessMax = stats.Maximum;
HardnessMin = stats.Minimum;
HardnessCurrentCount = stats.Count; HardnessCurrentCount = stats.Count;
HardnessPass = stats.IsPass; HardnessPass = stats.IsPass;
@@ -2357,7 +2393,7 @@ namespace TabletTester2025.ViewModels
TestType.Dissolution => string.IsNullOrWhiteSpace(effectiveDissolutionChannel) ? "溶出" : effectiveDissolutionChannel, TestType.Dissolution => string.IsNullOrWhiteSpace(effectiveDissolutionChannel) ? "溶出" : effectiveDissolutionChannel,
_ => "" _ => ""
}; };
LocalAlarm = $"{projectName}测试完成"; LocalAlarm = $"{projectName}测试完成,已保存";
}); });
return true; return true;
} }

View File

@@ -199,7 +199,6 @@
<DataGridTextColumn Header="平均偏差(N)" Binding="{Binding HardnessAverageDeviation, StringFormat=F2}" Width="110"/> <DataGridTextColumn Header="平均偏差(N)" Binding="{Binding HardnessAverageDeviation, StringFormat=F2}" Width="110"/>
<DataGridTextColumn Header="RSD(%)" Binding="{Binding HardnessRSD, StringFormat=F1}" Width="80"/> <DataGridTextColumn Header="RSD(%)" Binding="{Binding HardnessRSD, StringFormat=F1}" Width="80"/>
<DataGridTextColumn Header="最大值(N)" Binding="{Binding HardnessMax, StringFormat=F1}" Width="90"/> <DataGridTextColumn Header="最大值(N)" Binding="{Binding HardnessMax, StringFormat=F1}" Width="90"/>
<DataGridTextColumn Header="最小值(N)" Binding="{Binding HardnessMin, StringFormat=F1}" Width="90"/>
<DataGridTextColumn Header="测试次数" Binding="{Binding HardnessTestCount}" Width="70"/> <DataGridTextColumn Header="测试次数" Binding="{Binding HardnessTestCount}" Width="70"/>
<DataGridTextColumn Header="单次数据(N)" Binding="{Binding HardnessSampleSummary}" Width="220"/> <DataGridTextColumn Header="单次数据(N)" Binding="{Binding HardnessSampleSummary}" Width="220"/>
<DataGridTextColumn Header="判定" Binding="{Binding HardnessPassText}" Width="70"/> <DataGridTextColumn Header="判定" Binding="{Binding HardnessPassText}" Width="70"/>

View File

@@ -13,11 +13,10 @@ namespace TabletTester2025
private readonly DatabaseService _dbService; private readonly DatabaseService _dbService;
private List<TestBatch> _allData; private List<TestBatch> _allData;
public HistoryWindow() public HistoryWindow(DatabaseService dbService)
{ {
InitializeComponent(); InitializeComponent();
var connectionString = "Data Source=TabletTests.db"; _dbService = dbService ?? throw new ArgumentNullException(nameof(dbService));
_dbService = new DatabaseService(connectionString);
LoadData(); LoadData();
} }

View File

@@ -235,22 +235,22 @@
<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="#1565C0"/> <Setter Property="Foreground" Value="#1565C0"/>
</DataTrigger> </DataTrigger>
<DataTrigger Binding="{Binding LocalAlarm}" Value="脆碎度测试完成"> <DataTrigger Binding="{Binding LocalAlarm}" Value="脆碎度测试完成,已保存">
<Setter Property="Foreground" Value="#1565C0"/> <Setter Property="Foreground" Value="#1565C0"/>
</DataTrigger> </DataTrigger>
<DataTrigger Binding="{Binding LocalAlarm}" Value="崩解测试完成"> <DataTrigger Binding="{Binding LocalAlarm}" Value="崩解测试完成,已保存">
<Setter Property="Foreground" Value="#1565C0"/> <Setter Property="Foreground" Value="#1565C0"/>
</DataTrigger> </DataTrigger>
<DataTrigger Binding="{Binding LocalAlarm}" Value="溶出测试完成"> <DataTrigger Binding="{Binding LocalAlarm}" Value="溶出测试完成,已保存">
<Setter Property="Foreground" Value="#1565C0"/> <Setter Property="Foreground" Value="#1565C0"/>
</DataTrigger> </DataTrigger>
<DataTrigger Binding="{Binding LocalAlarm}" Value="溶出1测试完成"> <DataTrigger Binding="{Binding LocalAlarm}" Value="溶出1测试完成,已保存">
<Setter Property="Foreground" Value="#1565C0"/> <Setter Property="Foreground" Value="#1565C0"/>
</DataTrigger> </DataTrigger>
<DataTrigger Binding="{Binding LocalAlarm}" Value="溶出2测试完成"> <DataTrigger Binding="{Binding LocalAlarm}" Value="溶出2测试完成,已保存">
<Setter Property="Foreground" Value="#1565C0"/> <Setter Property="Foreground" Value="#1565C0"/>
</DataTrigger> </DataTrigger>
</Style.Triggers> </Style.Triggers>
@@ -260,6 +260,8 @@
<TabControl Grid.Row="1" FontSize="13" BorderThickness="0"> <TabControl Grid.Row="1" FontSize="13" BorderThickness="0">
<TabItem Header="硬度"> <TabItem Header="硬度">
<ScrollViewer VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled">
<Grid Margin="4,14,4,4"> <Grid Margin="4,14,4,4">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
@@ -304,12 +306,6 @@
<TextBlock Text="{Binding HardnessMax, StringFormat=F1}" Foreground="#1565C0" Style="{StaticResource MetricValue}"/> <TextBlock Text="{Binding HardnessMax, StringFormat=F1}" Foreground="#1565C0" Style="{StaticResource MetricValue}"/>
</StackPanel> </StackPanel>
</Border> </Border>
<Border Style="{StaticResource MetricCard}">
<StackPanel>
<TextBlock Text="最小力值(N)" Style="{StaticResource MetricLabel}"/>
<TextBlock Text="{Binding HardnessMin, StringFormat=F1}" Foreground="#2E7D32" Style="{StaticResource MetricValue}"/>
</StackPanel>
</Border>
<Border Style="{StaticResource MetricCard}"> <Border Style="{StaticResource MetricCard}">
<StackPanel> <StackPanel>
<TextBlock Text="平均值(N)" Style="{StaticResource MetricLabel}"/> <TextBlock Text="平均值(N)" Style="{StaticResource MetricLabel}"/>
@@ -465,6 +461,7 @@
<Button Command="{Binding ClearHardnessRecordsCommand}" Content="清空记录" Style="{StaticResource SecondaryButton}"/> <Button Command="{Binding ClearHardnessRecordsCommand}" Content="清空记录" Style="{StaticResource SecondaryButton}"/>
</WrapPanel> </WrapPanel>
</Grid> </Grid>
</ScrollViewer>
</TabItem> </TabItem>
<TabItem Header="脆碎度"> <TabItem Header="脆碎度">

View File

@@ -83,6 +83,10 @@
<TextBlock Text="加压压力:" Style="{StaticResource ParamLabel}"/> <TextBlock Text="加压压力:" Style="{StaticResource ParamLabel}"/>
<TextBox x:Name="HardnessPressureBox"/> <TextBox x:Name="HardnessPressureBox"/>
</StackPanel> </StackPanel>
<StackPanel Style="{StaticResource ParamRow}">
<TextBlock Text="加压速度(mm/min):" Style="{StaticResource ParamLabel}"/>
<TextBox x:Name="HardnessSpeedBox"/>
</StackPanel>
<StackPanel Style="{StaticResource ParamRow}"> <StackPanel Style="{StaticResource ParamRow}">
<TextBlock Text="硬度破损判定(N):" Style="{StaticResource ParamLabel}"/> <TextBlock Text="硬度破损判定(N):" Style="{StaticResource ParamLabel}"/>
<TextBox x:Name="HardnessDamageThresholdBox" helpers:NumericInput.AllowDecimal="True"/> <TextBox x:Name="HardnessDamageThresholdBox" helpers:NumericInput.AllowDecimal="True"/>

View File

@@ -19,6 +19,7 @@ namespace TabletTester2025
private async void SettingsWindow_Loaded(object sender, RoutedEventArgs e) private async void SettingsWindow_Loaded(object sender, RoutedEventArgs e)
{ {
await LoadHardnessPressureAsync(); await LoadHardnessPressureAsync();
await LoadHardnessSpeedAsync();
await LoadHardnessDamageThresholdAsync(); await LoadHardnessDamageThresholdAsync();
// 脆碎度 // 脆碎度
@@ -69,6 +70,7 @@ namespace TabletTester2025
}; };
p.HardnessTestCount = int.Parse(HardnessCountBox.Text); p.HardnessTestCount = int.Parse(HardnessCountBox.Text);
double hardnessPressure = ParseFiniteDouble(HardnessPressureBox.Text, "加压压力"); double hardnessPressure = ParseFiniteDouble(HardnessPressureBox.Text, "加压压力");
double hardnessSpeed = ParsePositiveDouble(HardnessSpeedBox.Text, "加压速度");
double hardnessDamageThreshold = ParsePositiveDouble(HardnessDamageThresholdBox.Text, "硬度破损判定"); double hardnessDamageThreshold = ParsePositiveDouble(HardnessDamageThresholdBox.Text, "硬度破损判定");
double friabilityRpm = ParseFiniteDouble(FriabilityRpmBox.Text, "脆碎度转速"); double friabilityRpm = ParseFiniteDouble(FriabilityRpmBox.Text, "脆碎度转速");
p.FriabilityTargetRpm = friabilityRpm; p.FriabilityTargetRpm = friabilityRpm;
@@ -86,6 +88,7 @@ namespace TabletTester2025
ValidateParameters(p); ValidateParameters(p);
await WriteHardnessPressureAsync(hardnessPressure); await WriteHardnessPressureAsync(hardnessPressure);
await WriteHardnessSpeedAsync(hardnessSpeed);
await WriteHardnessDamageThresholdAsync(hardnessDamageThreshold); await WriteHardnessDamageThresholdAsync(hardnessDamageThreshold);
await WriteFriabilityRpmAsync(friabilityRpm); await WriteFriabilityRpmAsync(friabilityRpm);
await WriteDisintegrationTimeAsync(disintegrationTimeMin); await WriteDisintegrationTimeAsync(disintegrationTimeMin);
@@ -202,6 +205,38 @@ namespace TabletTester2025
return App.PlcConfig.HardnessPressure != 0 ? App.PlcConfig.HardnessPressure : (ushort)1480; return App.PlcConfig.HardnessPressure != 0 ? App.PlcConfig.HardnessPressure : (ushort)1480;
} }
private async Task LoadHardnessSpeedAsync()
{
ushort registerAddress = ResolveHardnessSpeedRegister();
if (registerAddress == 0)
return;
try
{
float value = await App.PlcService.ReadFloatAsync(registerAddress);
if (float.IsFinite(value) && value > 0)
HardnessSpeedBox.Text = value.ToString("0.###");
}
catch
{
HardnessSpeedBox.Text = "";
}
}
private static async Task WriteHardnessSpeedAsync(double value)
{
ushort registerAddress = ResolveHardnessSpeedRegister();
if (registerAddress == 0)
throw new InvalidOperationException("加压速度PLC寄存器地址未配置。");
await App.PlcService.WriteFloatAsync(registerAddress, (float)value);
}
private static ushort ResolveHardnessSpeedRegister()
{
return App.PlcConfig.HardnessSudu != 0 ? App.PlcConfig.HardnessSudu : (ushort)300;
}
private async Task LoadHardnessDamageThresholdAsync() private async Task LoadHardnessDamageThresholdAsync()
{ {
ushort registerAddress = ResolveHardnessDamageThresholdRegister(); ushort registerAddress = ResolveHardnessDamageThresholdRegister();