This commit is contained in:
GukSang.Jin
2026-05-18 09:47:22 +08:00
parent 3db2f360b1
commit 175d0983d4
8 changed files with 197 additions and 13 deletions

View File

@@ -69,6 +69,8 @@ namespace TabletTester2025
DissolutionTargetRpm REAL,
DissolutionRSquared REAL,
DissolutionSampleInterval INTEGER,
Dissolution1SampleInterval REAL,
Dissolution2SampleInterval REAL,
DissolutionUpDownFreq REAL,
-- 综合
@@ -129,6 +131,8 @@ namespace TabletTester2025
("TestBatches", "DissolutionTargetRpm", "REAL", "50"),
("TestBatches", "DissolutionRSquared", "REAL", "0"),
("TestBatches", "DissolutionSampleInterval", "INTEGER", "5"),
("TestBatches", "Dissolution1SampleInterval", "REAL", "5"),
("TestBatches", "Dissolution2SampleInterval", "REAL", "5"),
("TestBatches", "DissolutionUpDownFreq", "REAL", "32"),
("TestBatches", "HardnessRSD", "REAL", "0"), // 已存在但确保
("TestBatches", "RemainingTubesAtEnd", "INTEGER", "0"),

View File

@@ -31,6 +31,7 @@
public ushort DisintegrationMovingUpCoil { get; set; }
public ushort DisintegrationStartCoil { get; set; }
public ushort DisintegrationStopCoil { get; set; }
public ushort DisintegrationResetCoil { get; set; }
public ushort DisintegrationSpeed { get; set; }
public ushort DisintegrationTime { get; set; }
public ushort[] DisintegrationCompleteCoils { get; set; } = new ushort[6];
@@ -43,11 +44,15 @@
public ushort DissolutionStartCoil { get; set; }
public ushort Dissolution1StartCoil { get; set; }
public ushort Dissolution1StopCoil { get; set; }
public ushort Dissolution1ResetCoil { get; set; }
public ushort Dissolution1SampleAckCoil { get; set; }
public ushort Dissolution2StartCoil { get; set; }
public ushort Dissolution2StopCoil { get; set; }
public ushort Dissolution2ResetCoil { get; set; }
public ushort Dissolution2SampleAckCoil { get; set; }
public ushort Dissolution1Time { get; set; }
public ushort Dissolution2Time { get; set; }
public ushort Dissolution1SampleInterval { get; set; }
public ushort Dissolution2SampleInterval { get; set; }
}
}

View File

@@ -37,6 +37,8 @@ namespace TabletTester2025.Models
public double DissolutionTargetRpm { get; set; }
public double DissolutionRSquared { get; set; }
public int DissolutionSampleInterval { get; set; }
public double Dissolution1SampleInterval { get; set; }
public double Dissolution2SampleInterval { get; set; }
public double DissolutionUpDownFreq { get; set; }
// 综合

View File

@@ -26,6 +26,8 @@ namespace TabletTester2025.ViewModels
private DispatcherTimer _disintegrationTimer;
private bool _isLoadingDissolution1Time;
private bool _isLoadingDissolution2Time;
private bool _isLoadingDissolution1SampleInterval;
private bool _isLoadingDissolution2SampleInterval;
private bool _isLoadingDisintegrationTime;
private bool _isLoadingDisintegrationSpeed;
@@ -41,6 +43,7 @@ namespace TabletTester2025.ViewModels
private bool _dissolution2SampleRequestActive;
private bool _isDissolution1SamplePromptOpen;
private bool _isDissolution2SamplePromptOpen;
private bool _discardDisintegrationResult;
private string _dissolutionResultChannel = "";
private double _dissolutionResultRate30Min;
private double _dissolutionResultRSquared;
@@ -110,6 +113,8 @@ namespace TabletTester2025.ViewModels
[ObservableProperty] private double _dissolutionTargetRpm = 50;
[ObservableProperty] private int _dissolution1TimeMin = 30;
[ObservableProperty] private int _dissolution2TimeMin = 30;
[ObservableProperty] private double _dissolution1SampleIntervalMin = 5;
[ObservableProperty] private double _dissolution2SampleIntervalMin = 5;
[ObservableProperty] private double _dissolutionElapsedTime;
[ObservableProperty] private double _dissolutionCountdown;
[ObservableProperty] private double _dissolutionRSquared;
@@ -123,14 +128,17 @@ namespace TabletTester2025.ViewModels
public IAsyncRelayCommand PrintDissolutionCommand { get; }
public IAsyncRelayCommand StartDissolution1Command { get; }
public IAsyncRelayCommand StopDissolution1Command { get; }
public IAsyncRelayCommand ResetDissolution1Command { get; }
public IAsyncRelayCommand StartDissolution2Command { get; }
public IAsyncRelayCommand StopDissolution2Command { get; }
public IAsyncRelayCommand ResetDissolution2Command { get; }
// 崩解新增
[ObservableProperty] private double _disintegrationTargetFreq = 31;
[ObservableProperty] private double _disintegrationSpeedRpm = 31;
[ObservableProperty] private double _disintegrationTimeMin = 15;
public IAsyncRelayCommand StopDisintegrationCommand { get; }
public IAsyncRelayCommand ResetDisintegrationCommand { get; }
public IAsyncRelayCommand PrintDisintegrationCommand { get; }
public PlotModel DissolutionPlotModel { get; }
@@ -254,13 +262,16 @@ namespace TabletTester2025.ViewModels
DissolutionDownCommand = new AsyncRelayCommand(async () => await _plc.WriteCoilAsync(0x23, true));
StartDissolution1Command = new AsyncRelayCommand(StartDissolution1Async);
StopDissolution1Command = new AsyncRelayCommand(StopDissolution1Async);
ResetDissolution1Command = new AsyncRelayCommand(ResetDissolution1Async);
StartDissolution2Command = new AsyncRelayCommand(StartDissolution2Async);
StopDissolution2Command = new AsyncRelayCommand(StopDissolution2Async);
ResetDissolution2Command = new AsyncRelayCommand(ResetDissolution2Async);
StopDissolutionCommand = new AsyncRelayCommand(StopDissolution1Async);
PrintDissolutionCommand = new AsyncRelayCommand(async () => await PrintReport("溶出度"));
// 崩解命令
StopDisintegrationCommand = new AsyncRelayCommand(StopDisintegrationAsync);
ResetDisintegrationCommand = new AsyncRelayCommand(ResetDisintegrationAsync);
PrintDisintegrationCommand = new AsyncRelayCommand(async () => await PrintReport("崩解"));
_ = LoadDisintegrationTimeAsync();
@@ -559,6 +570,23 @@ namespace TabletTester2025.ViewModels
_ = WriteDissolutionTimeAsync(_plcConfig.Dissolution2Time, value);
}
partial void OnDissolution1SampleIntervalMinChanged(double value)
{
if (_isLoadingDissolution1SampleInterval || _plcConfig.Dissolution1SampleInterval == 0 || value <= 0)
return;
_ = WriteDissolutionFloatAsync(_plcConfig.Dissolution1SampleInterval, value);
DissolutionSampleInterval = ToCompatibleSampleInterval(value);
}
partial void OnDissolution2SampleIntervalMinChanged(double value)
{
if (_isLoadingDissolution2SampleInterval || _plcConfig.Dissolution2SampleInterval == 0 || value <= 0)
return;
_ = WriteDissolutionFloatAsync(_plcConfig.Dissolution2SampleInterval, value);
}
private async Task LoadDissolutionTimesAsync()
{
if (_plcConfig.Dissolution1Time != 0)
@@ -592,6 +620,8 @@ namespace TabletTester2025.ViewModels
_isLoadingDissolution2Time = false;
}
}
await LoadDissolutionSampleIntervalsAsync();
}
private async Task WriteDissolutionTimeAsync(ushort registerAddress, int value)
@@ -606,6 +636,64 @@ namespace TabletTester2025.ViewModels
catch { }
}
private async Task LoadDissolutionSampleIntervalsAsync()
{
if (_plcConfig.Dissolution1SampleInterval != 0)
{
try
{
_isLoadingDissolution1SampleInterval = true;
float value = await _plc.ReadFloatAsync(_plcConfig.Dissolution1SampleInterval);
if (float.IsFinite(value) && value > 0)
{
Dissolution1SampleIntervalMin = value;
DissolutionSampleInterval = ToCompatibleSampleInterval(value);
}
}
catch { }
finally
{
_isLoadingDissolution1SampleInterval = false;
}
}
if (_plcConfig.Dissolution2SampleInterval != 0)
{
try
{
_isLoadingDissolution2SampleInterval = true;
float value = await _plc.ReadFloatAsync(_plcConfig.Dissolution2SampleInterval);
if (float.IsFinite(value) && value > 0)
Dissolution2SampleIntervalMin = value;
}
catch { }
finally
{
_isLoadingDissolution2SampleInterval = false;
}
}
}
private async Task WriteDissolutionFloatAsync(ushort registerAddress, double value)
{
if (registerAddress == 0 || value <= 0 || !double.IsFinite(value))
return;
try
{
await _plc.WriteFloatAsync(registerAddress, (float)value);
}
catch { }
}
private static int ToCompatibleSampleInterval(double value)
{
if (!double.IsFinite(value) || value <= 0)
return 0;
return (int)Math.Min(int.MaxValue, Math.Round(value, MidpointRounding.AwayFromZero));
}
partial void OnDisintegrationTimeMinChanged(double value)
{
if (_isLoadingDisintegrationTime || _plcConfig.DisintegrationTime == 0 || value <= 0)
@@ -885,9 +973,12 @@ namespace TabletTester2025.ViewModels
int maxSec = DisintegrationTimeMin > 0
? (int)Math.Ceiling(DisintegrationTimeMin * 60)
: App.CurrentPharmaParams.DisintegrationMaxSeconds;
DisintegrationPass = (RemainingTubes == 0 && DisintegrationSeconds <= maxSec);
DisintegrationPass = !_discardDisintegrationResult && RemainingTubes == 0 && DisintegrationSeconds <= maxSec;
await SaveBatchResult();
if (!_discardDisintegrationResult)
await SaveBatchResult();
_discardDisintegrationResult = false;
}
}
@@ -904,6 +995,29 @@ namespace TabletTester2025.ViewModels
}
}
private async Task ResetDisintegrationAsync()
{
bool wasRunning = CurrentTest == TestType.Disintegration && Phase == TestPhase.Running;
_discardDisintegrationResult = wasRunning;
try
{
await PulseCoilAsync(_plcConfig.DisintegrationResetCoil);
}
finally
{
_disintegrationTimer?.Stop();
TubesCompleted = new bool[6];
RemainingTubes = 6;
DisintegrationSeconds = 0;
DisintegrationPass = false;
Phase = TestPhase.Idle;
if (!wasRunning)
_discardDisintegrationResult = false;
}
}
private async Task StartDissolution1Async()
{
CurrentTest = TestType.Dissolution;
@@ -915,6 +1029,7 @@ namespace TabletTester2025.ViewModels
_isDissolution1Running = true;
DissolutionPlotModel.Title = "溶出曲线";
await WriteDissolutionTimeAsync(_plcConfig.Dissolution1Time, Dissolution1TimeMin);
await WriteDissolutionFloatAsync(_plcConfig.Dissolution1SampleInterval, Dissolution1SampleIntervalMin);
await PulseCoilAsync(_plcConfig.Dissolution1StartCoil);
}
@@ -934,6 +1049,22 @@ namespace TabletTester2025.ViewModels
}
}
private async Task ResetDissolution1Async()
{
try
{
await PulseCoilAsync(_plcConfig.Dissolution1ResetCoil);
}
finally
{
_isDissolution1Running = false;
ResetDissolutionChannel(1);
ResetDissolutionSampleState(1);
Phase = _isDissolution2Running ? TestPhase.Running : TestPhase.Idle;
UpdateDissolutionClock();
}
}
private async Task StartDissolution2Async()
{
if (_plcConfig.Dissolution2Percent == 0)
@@ -952,6 +1083,7 @@ namespace TabletTester2025.ViewModels
_isDissolution2Running = true;
DissolutionPlotModel.Title = "溶出曲线";
await WriteDissolutionTimeAsync(_plcConfig.Dissolution2Time, Dissolution2TimeMin);
await WriteDissolutionFloatAsync(_plcConfig.Dissolution2SampleInterval, Dissolution2SampleIntervalMin);
await PulseCoilAsync(_plcConfig.Dissolution2StartCoil);
}
@@ -971,6 +1103,22 @@ namespace TabletTester2025.ViewModels
}
}
private async Task ResetDissolution2Async()
{
try
{
await PulseCoilAsync(_plcConfig.Dissolution2ResetCoil);
}
finally
{
_isDissolution2Running = false;
ResetDissolutionChannel(2);
ResetDissolutionSampleState(2);
Phase = _isDissolution1Running ? TestPhase.Running : TestPhase.Idle;
UpdateDissolutionClock();
}
}
private void ResetDissolutionChannel(int channel)
{
if (channel == 1)
@@ -1096,7 +1244,11 @@ namespace TabletTester2025.ViewModels
DissolutionRate30Min = dissolutionRate30Min,
DissolutionTargetRpm = DissolutionTargetRpm,
DissolutionRSquared = rsquared,
DissolutionSampleInterval = DissolutionSampleInterval,
DissolutionSampleInterval = CurrentTest == TestType.Dissolution && _dissolutionResultChannel == "溶出2"
? ToCompatibleSampleInterval(Dissolution2SampleIntervalMin)
: ToCompatibleSampleInterval(Dissolution1SampleIntervalMin),
Dissolution1SampleInterval = Dissolution1SampleIntervalMin,
Dissolution2SampleInterval = Dissolution2SampleIntervalMin,
DissolutionUpDownFreq = DissolutionUpDownFreq,
HardnessPass = HardnessPass,
FriabilityPass = FriabilityPass,

View File

@@ -384,8 +384,8 @@
</GroupBox>
<WrapPanel Grid.Row="2" Style="{StaticResource CommandBar}">
<Button Command="{Binding StartFriabilityCommand}" Content="测试启动" Style="{StaticResource StartButton}"/>
<Button Command="{Binding StopFriabilityCommand}" Content="测试停止" Style="{StaticResource StopButton}"/>
<Button Command="{Binding StartFriabilityCommand}" Content="开始" Style="{StaticResource StartButton}"/>
<Button Command="{Binding StopFriabilityCommand}" Content="停止" Style="{StaticResource StopButton}"/>
<Button Command="{Binding ResetFriabilityCommand}" Content="复位" Style="{StaticResource ResetButton}"/>
</WrapPanel>
</Grid>
@@ -416,6 +416,14 @@
<TextBlock Text="溶出2时间(min)" Style="{StaticResource ParamLabel}"/>
<TextBox Text="{Binding Dissolution2TimeMin, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
<StackPanel Style="{StaticResource ParamRow}">
<TextBlock Text="溶出1间隔取样时间(min)" Style="{StaticResource ParamLabel}"/>
<TextBox Text="{Binding Dissolution1SampleIntervalMin, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
<StackPanel Style="{StaticResource ParamRow}">
<TextBlock Text="溶出2间隔取样时间(min)" Style="{StaticResource ParamLabel}"/>
<TextBox Text="{Binding Dissolution2SampleIntervalMin, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</WrapPanel>
</GroupBox>
@@ -487,8 +495,10 @@
<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 ResetDissolution1Command}" Content="溶出1复位" Style="{StaticResource ResetButton}"/>
<Button Command="{Binding StartDissolution2Command}" Content="溶出2开始" Style="{StaticResource StartButton}"/>
<Button Command="{Binding StopDissolution2Command}" Content="溶出2停止" Style="{StaticResource StopButton}"/>
<Button Command="{Binding ResetDissolution2Command}" Content="溶出2复位" Style="{StaticResource ResetButton}"/>
</WrapPanel>
</Grid>
</TabItem>
@@ -538,8 +548,9 @@
</GroupBox>
<WrapPanel Grid.Row="2" Style="{StaticResource CommandBar}">
<Button Command="{Binding StartDisintegrationCommand}" Content="测试启动" Style="{StaticResource StartButton}"/>
<Button Command="{Binding StopDisintegrationCommand}" Content="测试停止" Style="{StaticResource StopButton}"/>
<Button Command="{Binding StartDisintegrationCommand}" Content="开始" Style="{StaticResource StartButton}"/>
<Button Command="{Binding StopDisintegrationCommand}" Content="停止" Style="{StaticResource StopButton}"/>
<Button Command="{Binding ResetDisintegrationCommand}" Content="复位" Style="{StaticResource ResetButton}"/>
</WrapPanel>
</Grid>
</TabItem>
@@ -558,7 +569,7 @@
<Border Background="#0F3D68" CornerRadius="6" Margin="0,0,0,8" Padding="12,10">
<Grid>
<TextBlock Text="CSI-Z420 片剂四用仪 硬度 · 脆碎度 · 溶出 · 崩解"
<TextBlock Text="片剂四用仪 硬度 · 脆碎度 · 溶出 · 崩解"
FontSize="22"
FontWeight="Bold"
Foreground="White"

View File

@@ -127,8 +127,12 @@
<TextBox Style="{StaticResource SettingTextBox}" Name="txt_DissolutionTime"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Style="{StaticResource SettingLabel}" Text="溶出间隔取样时间设置"/>
<TextBox Style="{StaticResource SettingTextBox}" Name="txt_DissolutionSamplingInterval"/>
<TextBlock Style="{StaticResource SettingLabel}" Text="溶出1间隔取样时间设置"/>
<TextBox Style="{StaticResource SettingTextBox}" Name="txt_Dissolution1SamplingInterval"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Style="{StaticResource SettingLabel}" Text="溶出2间隔取样时间设置"/>
<TextBox Style="{StaticResource SettingTextBox}" Name="txt_Dissolution2SamplingInterval"/>
</StackPanel>
</StackPanel>
</Border>
@@ -162,4 +166,4 @@
</WrapPanel>
</ScrollViewer>
</Window>
</Window>

View File

@@ -138,7 +138,8 @@ namespace 片剂四用仪.Views
new PlcParamMapping("txt_DisintegrationTime", 420, PlcParamType.Float),
new PlcParamMapping("txt_DissolutionTime", 430, PlcParamType.Int),
new PlcParamMapping("txt_DissolutionSamplingInterval", 432, PlcParamType.Int),
new PlcParamMapping("txt_Dissolution1SamplingInterval", 432, PlcParamType.Float),
new PlcParamMapping("txt_Dissolution2SamplingInterval", 442, PlcParamType.Float),
new PlcParamMapping("txt_ForceCoefficient", 1320, PlcParamType.Float),

View File

@@ -43,6 +43,7 @@
"DisintegrationMovingUpCoil": 30,
"DisintegrationStartCoil": 50,
"DisintegrationStopCoil": 53,
"DisintegrationResetCoil": 100,
"DisintegrationSpeed": 330,
"DisintegrationTime": 420,
"DisintegrationCompleteCoils": [ 200, 201, 202, 203, 204, 205 ],
@@ -53,12 +54,16 @@
"DissolutionStartCoil": 40,
"Dissolution1StartCoil": 40,
"Dissolution1StopCoil": 43,
"Dissolution1ResetCoil": 105,
"Dissolution1SampleAckCoil": 44,
"Dissolution2StartCoil": 30,
"Dissolution2StopCoil": 33,
"Dissolution2ResetCoil": 110,
"Dissolution2SampleAckCoil": 34,
"Dissolution1Time": 430,
"Dissolution2Time": 440
"Dissolution2Time": 440,
"Dissolution1SampleInterval": 432,
"Dissolution2SampleInterval": 442
},
"PharmaStandard": {
"HardnessMin_N": 40,