更新最新2026

This commit is contained in:
GukSang.Jin
2026-05-18 14:06:04 +08:00
parent 41435205a4
commit 40bcd313a2
22 changed files with 1275 additions and 323 deletions

View File

@@ -5,12 +5,14 @@ using OxyPlot.Axes;
using OxyPlot.Series;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
using TabletTester2025.Helpers;
using TabletTester2025.Models;
using TabletTester2025.Services;
@@ -30,11 +32,13 @@ namespace TabletTester2025.ViewModels
private bool _isLoadingDissolution2SampleInterval;
private bool _isLoadingDisintegrationTime;
private bool _isLoadingDisintegrationSpeed;
private bool _isUpdatingFriabilityWeightFromPlc;
private readonly List<double> _dissolution1Times = new();
private readonly List<double> _dissolution1Values = new();
private readonly List<double> _dissolution2Times = new();
private readonly List<double> _dissolution2Values = new();
public ObservableCollection<DissolutionSamplePoint> DissolutionSamplePoints { get; } = new();
private DateTime _dissolution1StartTime = DateTime.MinValue;
private DateTime _dissolution2StartTime = DateTime.MinValue;
private bool _isDissolution1Running;
@@ -58,6 +62,8 @@ namespace TabletTester2025.ViewModels
[ObservableProperty] private bool _hardnessPass;
[ObservableProperty] private double _hardnessAvg;
[ObservableProperty] private double _hardnessRSD;
[ObservableProperty] private double _hardnessInternalMin = 40;
[ObservableProperty] private double _hardnessInternalMax = 60;
//硬度新增
@@ -113,7 +119,9 @@ namespace TabletTester2025.ViewModels
// 脆碎度新增
[ObservableProperty] private double _friabilityTargetRpm = 25;
[ObservableProperty] private int _friabilityTargetTimeSec = 240;
[ObservableProperty] private int _friabilityTargetTimeSec = 4;
[ObservableProperty] private int _friabilityTargetRounds = 100;
[ObservableProperty] private double _friabilityMaxLossPercent = 1.0;
[ObservableProperty] private bool _friabilityClockwise = true;
[ObservableProperty] private bool _friabilityCounterClockwise;
[ObservableProperty] private double _friabilityCurrentRpm;
@@ -153,6 +161,7 @@ namespace TabletTester2025.ViewModels
[ObservableProperty] private double _disintegrationTargetFreq = 31;
[ObservableProperty] private double _disintegrationSpeedRpm = 31;
[ObservableProperty] private double _disintegrationTimeMin = 15;
[ObservableProperty] private string _disintegrationDosageForm = "普通片";
public IAsyncRelayCommand StopDisintegrationCommand { get; }
public IAsyncRelayCommand ResetDisintegrationCommand { get; }
public IAsyncRelayCommand PrintDisintegrationCommand { get; }
@@ -178,6 +187,7 @@ namespace TabletTester2025.ViewModels
_db = db;
_excel = excel;
_alarm = alarm;
LoadPharmaDefaults();
StartHardnessCommand = new AsyncRelayCommand(RunHardnessAsync);
StartFriabilityCommand = new AsyncRelayCommand(RunFriabilityAsync);
@@ -255,7 +265,7 @@ namespace TabletTester2025.ViewModels
});
// 硬后退按钮命令
HardnessForward = new AsyncRelayCommand(async () =>
HardnessBack = new AsyncRelayCommand(async () =>
{
await _plc.WriteCoilAsync(_plcConfig.HardnessBack, true);
await Task.Delay(100); // 脉冲宽度,和复位按钮保持一致
@@ -272,18 +282,19 @@ namespace TabletTester2025.ViewModels
Phase = TestPhase.Idle;
});
// 脆碎度命令
StopFriabilityCommand = new AsyncRelayCommand(() => {
StopFriabilityCommand = new AsyncRelayCommand(async () => {
//测试停止
Phase = TestPhase.Idle; return Task.CompletedTask;
if (_plcConfig.FriabilityStartCoilStop != 0)
await PulseCoilAsync(_plcConfig.FriabilityStartCoilStop);
Phase = TestPhase.Idle;
});
ResetFriabilityCommand = new AsyncRelayCommand(() =>
{
FriabilityRemainingRounds = 100; // 剩余圈数重置为默认值通常是100圈
FriabilityRemainingRounds = FriabilityTargetRounds;
LossPercent = 0; // 失重率清零
WeightBefore = 0; // 脆碎前重量清零
WeightAfter = 0;// 脆碎后重量清零
SetFriabilityWeightFromPlc(0, 0); // 清空界面结果不向PLC写
return Task.CompletedTask;
});
PrintFriabilityCommand = new AsyncRelayCommand(async () => await PrintReport("脆碎度"));
@@ -308,6 +319,23 @@ namespace TabletTester2025.ViewModels
_ = LoadDisintegrationTimeAsync();
_ = LoadDisintegrationSpeedAsync();
_ = LoadDissolutionTimesAsync();
_ = LoadFriabilityWeightsAsync();
}
private void LoadPharmaDefaults()
{
var p = App.CurrentPharmaParams;
HardnessInternalMin = p.HardnessMin_N;
HardnessInternalMax = p.HardnessMax_N;
HardnessTestCount = Math.Max(1, p.HardnessTestCount);
FriabilityTargetRpm = p.FriabilityTargetRpm > 0 ? p.FriabilityTargetRpm : 25;
FriabilityTargetRounds = p.FriabilityTargetRounds > 0 ? p.FriabilityTargetRounds : 100;
FriabilityMaxLossPercent = p.FriabilityMaxLossPercent;
FriabilityRemainingRounds = FriabilityTargetRounds;
DisintegrationDosageForm = string.IsNullOrWhiteSpace(p.DisintegrationDosageForm) ? "普通片" : p.DisintegrationDosageForm;
int seconds = ResolveDisintegrationLimitSeconds();
if (seconds > 0)
DisintegrationTimeMin = seconds / 60.0;
}
private async Task PrintReport(string testName)
@@ -349,27 +377,17 @@ namespace TabletTester2025.ViewModels
private async Task UpdateDissolutionDataAsync()
{
bool updated = false;
DissolutionRpm = await _plc.ReadFloatAsync(_plcConfig.DissolutionRpm);
if (_plcConfig.DisintegrationTemp != 0)
DisintegrationTemp = await _plc.ReadFloatAsync(_plcConfig.DisintegrationTemp);
if (_isDissolution1Running)
{
await CheckDissolutionSampleAsync(1);
updated |= await ReadDissolutionChannelAsync(1);
}
if (_isDissolution2Running)
{
await CheckDissolutionSampleAsync(2);
updated |= await ReadDissolutionChannelAsync(2);
}
UpdateDissolutionClock();
if (updated)
DissolutionPlotModel.InvalidatePlot(true);
}
private async Task CheckDissolutionSampleAsync(int channel)
@@ -394,14 +412,22 @@ namespace TabletTester2025.ViewModels
if (IsDissolutionSampleRequestActive(channel) || IsDissolutionSamplePromptOpen(channel))
return;
if (GetNextPendingDissolutionSample(channel) == null)
{
await _plc.WriteCoilAsync(coilAddress, true);
DissolutionCurveStatus = $"溶出{channel}已完成全部取样点";
return;
}
SetDissolutionSampleRequestActive(channel, true);
SetDissolutionSamplePromptOpen(channel, true);
try
{
await ShowDissolutionSampleDialogAsync(channel);
double percent = await ShowDissolutionSampleDialogAsync(channel);
RecordDissolutionSample(channel, percent);
await _plc.WriteCoilAsync(coilAddress, true);
LocalAlarm = $"溶出{channel}已确认取样";
LocalAlarm = $"溶出{channel}已记录取样结果";
DissolutionCurveStatus = "";
}
catch (Exception ex)
@@ -442,14 +468,20 @@ namespace TabletTester2025.ViewModels
_isDissolution2SamplePromptOpen = value;
}
private async Task ShowDissolutionSampleDialogAsync(int channel)
private async Task<double> ShowDissolutionSampleDialogAsync(int channel)
{
bool confirmed = await App.Current.Dispatcher.InvokeAsync(() =>
double? result = await App.Current.Dispatcher.InvokeAsync<double?>(() =>
{
var nextSample = GetNextPendingDissolutionSample(channel);
double elapsed = GetDissolutionElapsedMinutes(channel);
string plannedText = nextSample == null
? "未找到待记录取样点"
: $"计划取样时间:{nextSample.ScheduledTimeMin:0.##} min";
var dialog = new Window
{
Title = $"溶出{channel}取样",
Width = 420,
Width = 460,
SizeToContent = SizeToContent.Height,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
ResizeMode = ResizeMode.NoResize,
@@ -460,19 +492,66 @@ namespace TabletTester2025.ViewModels
var panel = new StackPanel { Margin = new Thickness(24) };
panel.Children.Add(new TextBlock
{
Text = "请取样分析后,取样结束后点击“确定已取样”继续运行。",
Text = $"请人工采集样品并完成分析,录入该时间点的溶出度(%)。\n{plannedText}\n当前运行时间{elapsed:0.##} min",
FontSize = 16,
Foreground = new SolidColorBrush(Color.FromRgb(16, 42, 67)),
TextWrapping = TextWrapping.Wrap,
Margin = new Thickness(0, 0, 0, 18)
Margin = new Thickness(0, 0, 0, 14)
});
var button = new Button
panel.Children.Add(new TextBlock
{
Content = "确定已取样",
Width = 128,
Text = "溶出度(%)",
FontSize = 14,
FontWeight = System.Windows.FontWeights.SemiBold,
Foreground = new SolidColorBrush(Color.FromRgb(51, 65, 85)),
Margin = new Thickness(0, 0, 0, 6)
});
var input = new TextBox
{
Height = 42,
FontSize = 18,
Padding = new Thickness(10, 5, 10, 5),
Text = nextSample?.Percent?.ToString("0.###") ?? "",
Margin = new Thickness(0, 0, 0, 18)
};
NumericInput.SetIsEnabled(input, true);
NumericInput.SetAllowDecimal(input, true);
NumericInput.SetAllowNegative(input, false);
panel.Children.Add(input);
var buttons = new StackPanel
{
Orientation = Orientation.Horizontal,
HorizontalAlignment = System.Windows.HorizontalAlignment.Right
};
var cancelButton = new Button
{
Content = "取消",
Width = 96,
Height = 38,
Margin = new Thickness(0, 0, 10, 0),
Background = Brushes.White,
Foreground = new SolidColorBrush(Color.FromRgb(51, 65, 85)),
BorderBrush = new SolidColorBrush(Color.FromRgb(203, 213, 225)),
BorderThickness = new Thickness(1),
FontSize = 15,
FontWeight = System.Windows.FontWeights.SemiBold,
Cursor = System.Windows.Input.Cursors.Hand
};
cancelButton.Click += (_, _) =>
{
dialog.DialogResult = false;
dialog.Close();
};
var confirmButton = new Button
{
Content = "记录取样",
Width = 112,
Height = 38,
HorizontalAlignment = System.Windows.HorizontalAlignment.Right,
Background = new SolidColorBrush(Color.FromRgb(21, 101, 169)),
Foreground = Brushes.White,
BorderThickness = new Thickness(0),
@@ -480,19 +559,30 @@ namespace TabletTester2025.ViewModels
FontWeight = System.Windows.FontWeights.SemiBold,
Cursor = System.Windows.Input.Cursors.Hand
};
button.Click += (_, _) =>
confirmButton.Click += (_, _) =>
{
if (!double.TryParse(input.Text, out double value) || !IsValidDissolutionPercent(value))
{
MessageBox.Show("请输入0-150之间的溶出度百分比。", "取样结果无效", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
input.Tag = value;
dialog.DialogResult = true;
dialog.Close();
};
panel.Children.Add(button);
buttons.Children.Add(cancelButton);
buttons.Children.Add(confirmButton);
panel.Children.Add(buttons);
dialog.Content = panel;
return dialog.ShowDialog() == true;
return dialog.ShowDialog() == true && input.Tag is double value ? value : null;
});
if (!confirmed)
throw new InvalidOperationException("取样确认窗口已关闭,未写入确认线圈");
if (!result.HasValue)
throw new InvalidOperationException("取样结果未录入,未写入确认线圈");
return result.Value;
}
private async Task<bool> ReadDissolutionChannelAsync(int channel)
@@ -562,6 +652,112 @@ namespace TabletTester2025.ViewModels
series.Points.Add(new DataPoint(minutes, value));
}
private void CreateDissolutionSampleSchedule(int channel)
{
RemoveDissolutionSamples(channel);
var sampleTimes = App.CurrentPharmaParams.DissolutionSampleTimes?
.Where(t => t > 0)
.Distinct()
.OrderBy(t => t)
.ToArray();
if (sampleTimes == null || sampleTimes.Length == 0)
sampleTimes = new[] { 5, 10, 15, 30, 45, 60 };
foreach (int minute in sampleTimes)
{
DissolutionSamplePoints.Add(new DissolutionSamplePoint
{
Channel = channel,
ScheduledTimeMin = minute
});
}
}
private void RemoveDissolutionSamples(int channel)
{
for (int i = DissolutionSamplePoints.Count - 1; i >= 0; i--)
{
if (DissolutionSamplePoints[i].Channel == channel)
DissolutionSamplePoints.RemoveAt(i);
}
}
private DissolutionSamplePoint? GetNextPendingDissolutionSample(int channel)
{
return DissolutionSamplePoints
.Where(s => s.Channel == channel && !s.Percent.HasValue)
.OrderBy(s => s.ScheduledTimeMin)
.FirstOrDefault();
}
private double GetDissolutionElapsedMinutes(int channel)
{
DateTime startTime = channel == 1 ? _dissolution1StartTime : _dissolution2StartTime;
return startTime == DateTime.MinValue ? 0 : Math.Max(0, (DateTime.Now - startTime).TotalMinutes);
}
private void RecordDissolutionSample(int channel, double percent)
{
var sample = GetNextPendingDissolutionSample(channel)
?? new DissolutionSamplePoint
{
Channel = channel,
ScheduledTimeMin = GetDissolutionElapsedMinutes(channel)
};
if (!DissolutionSamplePoints.Contains(sample))
DissolutionSamplePoints.Add(sample);
sample.ActualTimeMin = GetDissolutionElapsedMinutes(channel);
sample.Percent = percent;
sample.RecordedAt = DateTime.Now;
RefreshDissolutionSeries(channel);
}
private void RefreshDissolutionSeries(int channel)
{
var times = channel == 1 ? _dissolution1Times : _dissolution2Times;
var values = channel == 1 ? _dissolution1Values : _dissolution2Values;
var series = channel == 1 ? _dissolution1Series : _dissolution2Series;
var recordedSamples = DissolutionSamplePoints
.Where(s => s.Channel == channel && s.Percent.HasValue)
.OrderBy(s => s.ScheduledTimeMin)
.ToList();
times.Clear();
values.Clear();
series.Points.Clear();
foreach (var sample in recordedSamples)
{
double time = sample.ScheduledTimeMin;
double value = sample.Percent!.Value;
times.Add(time);
values.Add(value);
series.Points.Add(new DataPoint(time, value));
}
double rsquared = CalculateRSquared(times, values);
double latestValue = recordedSamples.LastOrDefault()?.Percent ?? 0;
if (channel == 1)
{
Dissolution1Percent = latestValue;
Dissolution1RSquared = rsquared;
}
else
{
Dissolution2Percent = latestValue;
Dissolution2RSquared = rsquared;
}
DissolutionPercent = latestValue;
DissolutionRSquared = rsquared;
DissolutionPlotModel.InvalidatePlot(true);
}
private void UpdateDissolutionClock()
{
var now = DateTime.Now;
@@ -723,6 +919,75 @@ namespace TabletTester2025.ViewModels
return (int)Math.Min(int.MaxValue, Math.Round(value, MidpointRounding.AwayFromZero));
}
partial void OnWeightBeforeChanged(double value)
{
if (_isUpdatingFriabilityWeightFromPlc)
return;
_ = WriteFriabilityWeightAsync(_plcConfig.WeightBefore, value);
}
partial void OnWeightAfterChanged(double value)
{
if (_isUpdatingFriabilityWeightFromPlc)
return;
_ = WriteFriabilityWeightAsync(_plcConfig.WeightAfter, value);
}
private async Task LoadFriabilityWeightsAsync()
{
try
{
_isUpdatingFriabilityWeightFromPlc = true;
if (_plcConfig.WeightBefore != 0)
{
double before = await ReadFriabilityWeightAsync(_plcConfig.WeightBefore, "脆碎前重量");
WeightBefore = before;
}
if (_plcConfig.WeightAfter != 0)
{
double after = await ReadFriabilityWeightAsync(_plcConfig.WeightAfter, "脆碎后重量");
WeightAfter = after;
}
}
catch { }
finally
{
_isUpdatingFriabilityWeightFromPlc = false;
}
}
private async Task WriteFriabilityWeightAsync(ushort registerAddress, double value)
{
if (registerAddress == 0 || !double.IsFinite(value) || value < 0)
return;
try
{
await _plc.WriteFloatAsync(registerAddress, (float)value);
}
catch { }
}
private void SetFriabilityWeightFromPlc(double? weightBefore = null, double? weightAfter = null)
{
_isUpdatingFriabilityWeightFromPlc = true;
try
{
if (weightBefore.HasValue)
WeightBefore = weightBefore.Value;
if (weightAfter.HasValue)
WeightAfter = weightAfter.Value;
}
finally
{
_isUpdatingFriabilityWeightFromPlc = false;
}
}
partial void OnDisintegrationTimeMinChanged(double value)
{
if (_isLoadingDisintegrationTime || _plcConfig.DisintegrationTime == 0 || value <= 0)
@@ -739,6 +1004,28 @@ namespace TabletTester2025.ViewModels
_ = WriteDisintegrationSpeedAsync(value);
}
partial void OnDisintegrationDosageFormChanged(string value)
{
int seconds = ResolveDisintegrationLimitSeconds(value);
if (seconds > 0)
DisintegrationTimeMin = seconds / 60.0;
}
private int ResolveDisintegrationLimitSeconds(string? dosageForm = null)
{
string form = string.IsNullOrWhiteSpace(dosageForm) ? DisintegrationDosageForm : dosageForm;
return form switch
{
"薄膜衣片" => 30 * 60,
"糖衣片" => 60 * 60,
"胶囊" => 30 * 60,
"普通片" => 15 * 60,
_ => App.CurrentPharmaParams.DisintegrationMaxSeconds > 0
? App.CurrentPharmaParams.DisintegrationMaxSeconds
: 15 * 60
};
}
private async Task LoadDisintegrationSpeedAsync()
{
if (_plcConfig.DisintegrationSpeed == 0)
@@ -766,9 +1053,10 @@ namespace TabletTester2025.ViewModels
try
{
_isLoadingDisintegrationTime = true;
float value = await _plc.ReadFloatAsync(_plcConfig.DisintegrationTime);
if (value > 0)
DisintegrationTimeMin = value;
int seconds = ResolveDisintegrationLimitSeconds();
if (seconds > 0)
DisintegrationTimeMin = seconds / 60.0;
await WriteDisintegrationTimeAsync(DisintegrationTimeMin);
}
catch { }
finally
@@ -821,13 +1109,13 @@ namespace TabletTester2025.ViewModels
try
{
int count = App.CurrentPharmaParams.HardnessTestCount;
double min = App.CurrentPharmaParams.HardnessMin_N;
double max = App.CurrentPharmaParams.HardnessMax_N;
int count = Math.Max(1, HardnessTestCount);
double min = HardnessInternalMin;
double max = HardnessInternalMax;
double currentSpeed = _hardnessSudu;
double currentWeiyi = _hardnessWeiyi;
double currentSpeed = HardnessSudu;
double currentWeiyi = HardnessWeiyi;
// 如果你需要把这3个值发给PLC就在这里发一次测试开始用当前参数
@@ -898,13 +1186,17 @@ namespace TabletTester2025.ViewModels
{
throw new InvalidOperationException("未配置脆碎度启动线圈地址");
}
WeightBefore = await ReadFriabilityWeightAsync(_plcConfig.WeightBefore, "脆碎前重量");
double weightBefore = await ReadFriabilityWeightAsync(_plcConfig.WeightBefore, "脆碎前重量");
SetFriabilityWeightFromPlc(weightBefore: weightBefore);
if (WeightBefore <= 0)
throw new InvalidOperationException("脆碎前重量必须大于0");
int totalRounds = Math.Max(1, FriabilityTargetRounds);
double rpm = FriabilityTargetRpm > 0 ? FriabilityTargetRpm : 25;
FriabilityTargetTimeSec = (int)Math.Ceiling(totalRounds / rpm * 60);
FriabilityRemainingRounds = totalRounds;
FriabilityCurrentRpm = rpm;
await _plc.WriteCoilAsync(startCoil, true);
int totalRounds = 100; // 药典标准脆碎度总圈数100圈
double rpm = FriabilityTargetRpm; // 界面设置的目标转速r/min
int durationMs = (int)((totalRounds / rpm) * 60 * 1000); // 总运行时间(毫秒)
for (int i = 0; i < durationMs; i += 100)
@@ -927,11 +1219,14 @@ namespace TabletTester2025.ViewModels
if (Phase != TestPhase.Running)
throw new InvalidOperationException("脆碎度测试已停止,未保存结果");
WeightAfter = await ReadFriabilityWeightAsync(_plcConfig.WeightAfter, "脆碎后重量");
double weightAfter = await ReadFriabilityWeightAsync(_plcConfig.WeightAfter, "脆碎后重量");
SetFriabilityWeightFromPlc(weightAfter: weightAfter);
if (WeightAfter > WeightBefore)
throw new InvalidOperationException("脆碎后重量不能大于初始重量");
FriabilityCurrentRpm = FriabilityTargetRpm;
FriabilityCurrentRpm = rpm;
LossPercent = (WeightBefore - WeightAfter) / WeightBefore * 100;//失重率
FriabilityPass = LossPercent <= App.CurrentPharmaParams.FriabilityMaxLossPercent; //标准值
FriabilityPass = LossPercent <= FriabilityMaxLossPercent; //标准值
resultReady = true;
// 标记测试为已完成
Phase = TestPhase.Completed;
@@ -946,7 +1241,7 @@ namespace TabletTester2025.ViewModels
finally
{
Phase = TestPhase.Idle;
FriabilityRemainingRounds = 100;
FriabilityRemainingRounds = FriabilityTargetRounds;
if (resultReady)
await SaveBatchResult();
}
@@ -986,9 +1281,8 @@ namespace TabletTester2025.ViewModels
await WriteDisintegrationSpeedAsync(DisintegrationSpeedRpm);
await WriteDisintegrationTimeAsync(DisintegrationTimeMin);
await PulseCoilAsync(_plcConfig.DisintegrationStartCoil);
int maxSec = DisintegrationTimeMin > 0
? (int)Math.Ceiling(DisintegrationTimeMin * 60)
: App.CurrentPharmaParams.DisintegrationMaxSeconds;
int maxSec = ResolveDisintegrationLimitSeconds();
DisintegrationTimeMin = maxSec / 60.0;
while (RemainingTubes > 0 && DisintegrationSeconds < maxSec && Phase == TestPhase.Running)
{
await Task.Delay(500);
@@ -1005,9 +1299,7 @@ namespace TabletTester2025.ViewModels
finally
{
Phase = TestPhase.Idle;
int maxSec = DisintegrationTimeMin > 0
? (int)Math.Ceiling(DisintegrationTimeMin * 60)
: App.CurrentPharmaParams.DisintegrationMaxSeconds;
int maxSec = ResolveDisintegrationLimitSeconds();
DisintegrationPass = !_discardDisintegrationResult && RemainingTubes == 0 && DisintegrationSeconds <= maxSec;
if (!_discardDisintegrationResult)
@@ -1060,6 +1352,7 @@ namespace TabletTester2025.ViewModels
DissolutionPass = false;
ResetDissolutionChannel(1);
ResetDissolutionSampleState(1);
CreateDissolutionSampleSchedule(1);
_dissolution1StartTime = DateTime.Now;
_isDissolution1Running = true;
DissolutionPlotModel.Title = "溶出曲线";
@@ -1102,18 +1395,12 @@ namespace TabletTester2025.ViewModels
private async Task StartDissolution2Async()
{
if (_plcConfig.Dissolution2Percent == 0)
{
LocalAlarm = "溶出2溶出度寄存器未配置";
DissolutionCurveStatus = LocalAlarm;
return;
}
CurrentTest = TestType.Dissolution;
Phase = TestPhase.Running;
DissolutionPass = false;
ResetDissolutionChannel(2);
ResetDissolutionSampleState(2);
CreateDissolutionSampleSchedule(2);
_dissolution2StartTime = DateTime.Now;
_isDissolution2Running = true;
DissolutionPlotModel.Title = "溶出曲线";
@@ -1156,6 +1443,8 @@ namespace TabletTester2025.ViewModels
private void ResetDissolutionChannel(int channel)
{
RemoveDissolutionSamples(channel);
if (channel == 1)
{
_dissolution1Times.Clear();
@@ -1258,11 +1547,14 @@ namespace TabletTester2025.ViewModels
HardnessMax = HardnessMax,
HardnessMin = HardnessMin,
HardnessTestCount = HardnessTestCount,
HardnessInternalMin = HardnessInternalMin,
HardnessInternalMax = HardnessInternalMax,
// 脆碎度
FriabilityLoss = LossPercent,
FriabilityTargetRpm = FriabilityTargetRpm,
FriabilityTargetTimeSec = FriabilityTargetTimeSec,
FriabilityTargetRounds = FriabilityTargetRounds,
FriabilityClockwise = FriabilityClockwise,
FriabilityRemainingRounds = FriabilityRemainingRounds,
WeightBefore = WeightBefore,
@@ -1273,6 +1565,8 @@ namespace TabletTester2025.ViewModels
RemainingTubesAtEnd = RemainingTubes,
DisintegrationTargetFreq = 0,
DisintegrationTemp = DisintegrationTemp,
DisintegrationDosageForm = DisintegrationDosageForm,
DisintegrationLimitSeconds = ResolveDisintegrationLimitSeconds(),
// 溶出
DissolutionChannel = CurrentTest == TestType.Dissolution ? _dissolutionResultChannel : "",
@@ -1300,20 +1594,17 @@ namespace TabletTester2025.ViewModels
IsQualified = HardnessPass && FriabilityPass && DisintegrationPass && DissolutionPass
};
await Task.Run(() => _db.InsertBatch(batch));
var dissolutionSamples = CurrentTest == TestType.Dissolution
? DissolutionSamplePoints
.Where(s => s.ChannelName == _dissolutionResultChannel && s.Percent.HasValue)
.ToList()
: new List<DissolutionSamplePoint>();
await Task.Run(() => _db.InsertBatch(batch, dissolutionSamples));
await Application.Current.Dispatcher.InvokeAsync(() =>
{
// 获取当前测试项目是否合格
bool currentPass = CurrentTest switch
{
TestType.Hardness => HardnessPass,
TestType.Friability => FriabilityPass,
TestType.Disintegration => DisintegrationPass,
TestType.Dissolution => DissolutionPass,
_ => false
};
string projectName = CurrentTest switch
{
TestType.Hardness => "硬度",
@@ -1322,7 +1613,7 @@ namespace TabletTester2025.ViewModels
TestType.Dissolution => string.IsNullOrWhiteSpace(_dissolutionResultChannel) ? "溶出" : _dissolutionResultChannel,
_ => ""
};
LocalAlarm = currentPass ? $"{projectName}测试合格" : $"{projectName}测试不合格";
LocalAlarm = $"{projectName}测试完成";
});
}
catch (Exception ex)