更新
This commit is contained in:
@@ -46,6 +46,10 @@ namespace TabletTester2025.ViewModels
|
|||||||
public ObservableCollection<DissolutionSamplePoint> DissolutionSamplePoints { get; } = new();
|
public ObservableCollection<DissolutionSamplePoint> DissolutionSamplePoints { get; } = new();
|
||||||
private DateTime _dissolution1StartTime = DateTime.MinValue;
|
private DateTime _dissolution1StartTime = DateTime.MinValue;
|
||||||
private DateTime _dissolution2StartTime = DateTime.MinValue;
|
private DateTime _dissolution2StartTime = DateTime.MinValue;
|
||||||
|
private DateTime _dissolution1LastRunUpdate = DateTime.MinValue;
|
||||||
|
private DateTime _dissolution2LastRunUpdate = DateTime.MinValue;
|
||||||
|
private double _dissolution1ElapsedRunMinutes;
|
||||||
|
private double _dissolution2ElapsedRunMinutes;
|
||||||
private bool _isDissolution1Running;
|
private bool _isDissolution1Running;
|
||||||
private bool _isDissolution2Running;
|
private bool _isDissolution2Running;
|
||||||
private bool _dissolution1SampleRequestActive;
|
private bool _dissolution1SampleRequestActive;
|
||||||
@@ -155,6 +159,10 @@ namespace TabletTester2025.ViewModels
|
|||||||
[ObservableProperty] private double _dissolutionMinPercentAt30Min = 80;
|
[ObservableProperty] private double _dissolutionMinPercentAt30Min = 80;
|
||||||
[ObservableProperty] private double _dissolutionElapsedTime;
|
[ObservableProperty] private double _dissolutionElapsedTime;
|
||||||
[ObservableProperty] private double _dissolutionCountdown;
|
[ObservableProperty] private double _dissolutionCountdown;
|
||||||
|
[ObservableProperty] private double _dissolution1ElapsedTime;
|
||||||
|
[ObservableProperty] private double _dissolution2ElapsedTime;
|
||||||
|
[ObservableProperty] private double _dissolution1Countdown;
|
||||||
|
[ObservableProperty] private double _dissolution2Countdown;
|
||||||
[ObservableProperty] private double _dissolutionRSquared;
|
[ObservableProperty] private double _dissolutionRSquared;
|
||||||
[ObservableProperty] private double _dissolution1RSquared;
|
[ObservableProperty] private double _dissolution1RSquared;
|
||||||
[ObservableProperty] private double _dissolution2RSquared;
|
[ObservableProperty] private double _dissolution2RSquared;
|
||||||
@@ -441,41 +449,47 @@ namespace TabletTester2025.ViewModels
|
|||||||
|
|
||||||
private async Task CheckDissolutionSampleAsync(int channel)
|
private async Task CheckDissolutionSampleAsync(int channel)
|
||||||
{
|
{
|
||||||
ushort coilAddress = channel == 1
|
|
||||||
? _plcConfig.Dissolution1SampleAckCoil
|
|
||||||
: _plcConfig.Dissolution2SampleAckCoil;
|
|
||||||
|
|
||||||
if (coilAddress == 0)
|
|
||||||
{
|
|
||||||
DissolutionCurveStatus = $"溶出{channel}取样确认线圈未配置";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool sampleRequested = await _plc.ReadCoilAsync(coilAddress);
|
|
||||||
if (!sampleRequested)
|
|
||||||
{
|
|
||||||
SetDissolutionSampleRequestActive(channel, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsDissolutionSampleRequestActive(channel) || IsDissolutionSamplePromptOpen(channel))
|
if (IsDissolutionSampleRequestActive(channel) || IsDissolutionSamplePromptOpen(channel))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
AccumulateDissolutionRunTime(channel, DateTime.Now);
|
||||||
|
var nextSample = GetNextPendingDissolutionSample(channel);
|
||||||
|
if (nextSample == null || GetDissolutionElapsedMinutes(channel) + 0.0001 < nextSample.ScheduledTimeMin)
|
||||||
|
return;
|
||||||
|
|
||||||
SetDissolutionSampleRequestActive(channel, true);
|
SetDissolutionSampleRequestActive(channel, true);
|
||||||
SetDissolutionSamplePromptOpen(channel, true);
|
SetDissolutionSamplePromptOpen(channel, true);
|
||||||
|
PauseDissolutionRunClock(channel);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
await PulseCoilAsync(ResolveDissolutionStopCoil(channel));
|
||||||
double percent = await ShowDissolutionSampleDialogAsync(channel);
|
double percent = await ShowDissolutionSampleDialogAsync(channel);
|
||||||
RecordDissolutionSample(channel, percent);
|
RecordDissolutionSample(channel, percent);
|
||||||
await _plc.WriteCoilAsync(coilAddress, false);
|
|
||||||
SetDissolutionSampleRequestActive(channel, false);
|
SetDissolutionSampleRequestActive(channel, false);
|
||||||
LocalAlarm = $"溶出{channel}已记录取样结果";
|
LocalAlarm = $"溶出{channel}已记录取样结果";
|
||||||
DissolutionCurveStatus = "";
|
DissolutionCurveStatus = "";
|
||||||
|
|
||||||
|
if (IsDissolutionChannelComplete(channel))
|
||||||
|
{
|
||||||
|
await FinalizeDissolutionChannelAsync(channel);
|
||||||
|
SetDissolutionRunning(channel, false);
|
||||||
|
ResetDissolutionSampleState(channel);
|
||||||
|
Phase = IsAnyDissolutionRunning() ? TestPhase.Running : TestPhase.Idle;
|
||||||
|
UpdateDissolutionClock();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await PulseCoilAsync(ResolveDissolutionStartCoil(channel));
|
||||||
|
ResumeDissolutionRunClock(channel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
SetDissolutionSampleRequestActive(channel, false);
|
SetDissolutionSampleRequestActive(channel, false);
|
||||||
|
SetDissolutionRunning(channel, false);
|
||||||
|
Phase = IsAnyDissolutionRunning() ? TestPhase.Running : TestPhase.Idle;
|
||||||
|
UpdateDissolutionClock();
|
||||||
await App.Current.Dispatcher.InvokeAsync(() =>
|
await App.Current.Dispatcher.InvokeAsync(() =>
|
||||||
MessageBox.Show($"溶出{channel}取样确认失败:{ex.Message}", "取样确认失败", MessageBoxButton.OK, MessageBoxImage.Error));
|
MessageBox.Show($"溶出{channel}取样确认失败:{ex.Message}", "取样确认失败", MessageBoxButton.OK, MessageBoxImage.Error));
|
||||||
}
|
}
|
||||||
@@ -511,6 +525,96 @@ namespace TabletTester2025.ViewModels
|
|||||||
_isDissolution2SamplePromptOpen = value;
|
_isDissolution2SamplePromptOpen = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ushort ResolveDissolutionStartCoil(int channel)
|
||||||
|
{
|
||||||
|
return channel == 1 ? _plcConfig.Dissolution1StartCoil : _plcConfig.Dissolution2StartCoil;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ushort ResolveDissolutionStopCoil(int channel)
|
||||||
|
{
|
||||||
|
return channel == 1 ? _plcConfig.Dissolution1StopCoil : _plcConfig.Dissolution2StopCoil;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsAnyDissolutionRunning()
|
||||||
|
{
|
||||||
|
return _isDissolution1Running || _isDissolution2Running;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetDissolutionRunning(int channel, bool value)
|
||||||
|
{
|
||||||
|
if (channel == 1)
|
||||||
|
_isDissolution1Running = value;
|
||||||
|
else
|
||||||
|
_isDissolution2Running = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetDissolutionRunClock(int channel)
|
||||||
|
{
|
||||||
|
if (channel == 1)
|
||||||
|
{
|
||||||
|
_dissolution1ElapsedRunMinutes = 0;
|
||||||
|
_dissolution1LastRunUpdate = DateTime.MinValue;
|
||||||
|
Dissolution1ElapsedTime = 0;
|
||||||
|
Dissolution1Countdown = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_dissolution2ElapsedRunMinutes = 0;
|
||||||
|
_dissolution2LastRunUpdate = DateTime.MinValue;
|
||||||
|
Dissolution2ElapsedTime = 0;
|
||||||
|
Dissolution2Countdown = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateDissolutionClock();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResumeDissolutionRunClock(int channel)
|
||||||
|
{
|
||||||
|
if (channel == 1)
|
||||||
|
_dissolution1LastRunUpdate = DateTime.Now;
|
||||||
|
else
|
||||||
|
_dissolution2LastRunUpdate = DateTime.Now;
|
||||||
|
|
||||||
|
UpdateDissolutionClock();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PauseDissolutionRunClock(int channel)
|
||||||
|
{
|
||||||
|
AccumulateDissolutionRunTime(channel, DateTime.Now);
|
||||||
|
if (channel == 1)
|
||||||
|
_dissolution1LastRunUpdate = DateTime.MinValue;
|
||||||
|
else
|
||||||
|
_dissolution2LastRunUpdate = DateTime.MinValue;
|
||||||
|
|
||||||
|
UpdateDissolutionClock();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AccumulateDissolutionRunTime(int channel, DateTime now)
|
||||||
|
{
|
||||||
|
if (channel == 1)
|
||||||
|
{
|
||||||
|
if (_isDissolution1Running && _dissolution1LastRunUpdate != DateTime.MinValue)
|
||||||
|
{
|
||||||
|
_dissolution1ElapsedRunMinutes = Math.Max(0, _dissolution1ElapsedRunMinutes + (now - _dissolution1LastRunUpdate).TotalMinutes);
|
||||||
|
_dissolution1LastRunUpdate = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_isDissolution2Running && _dissolution2LastRunUpdate != DateTime.MinValue)
|
||||||
|
{
|
||||||
|
_dissolution2ElapsedRunMinutes = Math.Max(0, _dissolution2ElapsedRunMinutes + (now - _dissolution2LastRunUpdate).TotalMinutes);
|
||||||
|
_dissolution2LastRunUpdate = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsDissolutionChannelComplete(int channel)
|
||||||
|
{
|
||||||
|
return GetNextPendingDissolutionSample(channel) == null
|
||||||
|
|| GetDissolutionElapsedMinutes(channel) + 0.0001 >= GetDissolutionDurationMinutes(channel);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<double> ShowDissolutionSampleDialogAsync(int channel)
|
private async Task<double> ShowDissolutionSampleDialogAsync(int channel)
|
||||||
{
|
{
|
||||||
double? result = await App.Current.Dispatcher.InvokeAsync<double?>(() =>
|
double? result = await App.Current.Dispatcher.InvokeAsync<double?>(() =>
|
||||||
@@ -623,7 +727,7 @@ namespace TabletTester2025.ViewModels
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!result.HasValue)
|
if (!result.HasValue)
|
||||||
throw new InvalidOperationException("取样结果未录入,未写入确认线圈");
|
throw new InvalidOperationException("取样结果未录入");
|
||||||
|
|
||||||
return result.Value;
|
return result.Value;
|
||||||
}
|
}
|
||||||
@@ -694,35 +798,28 @@ namespace TabletTester2025.ViewModels
|
|||||||
|
|
||||||
private List<double> ResolveDissolutionSampleTimes(int channel)
|
private List<double> ResolveDissolutionSampleTimes(int channel)
|
||||||
{
|
{
|
||||||
int durationMin = channel == 1 ? Dissolution1TimeMin : Dissolution2TimeMin;
|
return GenerateIntervalSampleTimes(
|
||||||
double intervalMin = channel == 1 ? Dissolution1SampleIntervalMin : Dissolution2SampleIntervalMin;
|
GetDissolutionDurationMinutes(channel),
|
||||||
durationMin = Math.Max(1, durationMin);
|
GetDissolutionIntervalMinutes(channel));
|
||||||
|
|
||||||
var configuredTimes = App.CurrentPharmaParams.DissolutionSampleTimes?
|
|
||||||
.Where(t => t > 0)
|
|
||||||
.Select(t => (double)t)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
var times = configuredTimes != null && configuredTimes.Count > 0
|
|
||||||
? configuredTimes
|
|
||||||
: GenerateIntervalSampleTimes(durationMin, intervalMin);
|
|
||||||
|
|
||||||
times.Add(Math.Min(30, durationMin));
|
|
||||||
if (durationMin >= 30)
|
|
||||||
times.Add(30);
|
|
||||||
times.Add(durationMin);
|
|
||||||
|
|
||||||
return times
|
|
||||||
.Where(t => t > 0 && t <= durationMin)
|
|
||||||
.Distinct()
|
|
||||||
.OrderBy(t => t)
|
|
||||||
.ToList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<double> GenerateIntervalSampleTimes(int durationMin, double intervalMin)
|
private double GetDissolutionDurationMinutes(int channel)
|
||||||
|
{
|
||||||
|
return Math.Max(1, channel == 1 ? Dissolution1TimeMin : Dissolution2TimeMin);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double GetDissolutionIntervalMinutes(int channel)
|
||||||
|
{
|
||||||
|
double intervalMin = channel == 1 ? Dissolution1SampleIntervalMin : Dissolution2SampleIntervalMin;
|
||||||
|
return double.IsFinite(intervalMin) && intervalMin > 0 ? intervalMin : 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<double> GenerateIntervalSampleTimes(double durationMin, double intervalMin)
|
||||||
{
|
{
|
||||||
if (!double.IsFinite(intervalMin) || intervalMin <= 0)
|
if (!double.IsFinite(intervalMin) || intervalMin <= 0)
|
||||||
intervalMin = 5;
|
intervalMin = 5;
|
||||||
|
if (!double.IsFinite(durationMin) || durationMin <= 0)
|
||||||
|
durationMin = 1;
|
||||||
|
|
||||||
var times = new List<double>();
|
var times = new List<double>();
|
||||||
for (double time = intervalMin; time <= durationMin + 0.0001; time += intervalMin)
|
for (double time = intervalMin; time <= durationMin + 0.0001; time += intervalMin)
|
||||||
@@ -753,8 +850,7 @@ namespace TabletTester2025.ViewModels
|
|||||||
|
|
||||||
private double GetDissolutionElapsedMinutes(int channel)
|
private double GetDissolutionElapsedMinutes(int channel)
|
||||||
{
|
{
|
||||||
DateTime startTime = channel == 1 ? _dissolution1StartTime : _dissolution2StartTime;
|
return channel == 1 ? _dissolution1ElapsedRunMinutes : _dissolution2ElapsedRunMinutes;
|
||||||
return startTime == DateTime.MinValue ? 0 : Math.Max(0, (DateTime.Now - startTime).TotalMinutes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RecordDissolutionSample(int channel, double percent)
|
private void RecordDissolutionSample(int channel, double percent)
|
||||||
@@ -820,24 +916,44 @@ namespace TabletTester2025.ViewModels
|
|||||||
private void UpdateDissolutionClock()
|
private void UpdateDissolutionClock()
|
||||||
{
|
{
|
||||||
var now = DateTime.Now;
|
var now = DateTime.Now;
|
||||||
double elapsed1 = _isDissolution1Running && _dissolution1StartTime != DateTime.MinValue
|
AccumulateDissolutionRunTime(1, now);
|
||||||
? (now - _dissolution1StartTime).TotalMinutes
|
AccumulateDissolutionRunTime(2, now);
|
||||||
: 0;
|
|
||||||
double elapsed2 = _isDissolution2Running && _dissolution2StartTime != DateTime.MinValue
|
|
||||||
? (now - _dissolution2StartTime).TotalMinutes
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
|
double elapsed1 = _dissolution1ElapsedRunMinutes;
|
||||||
|
double elapsed2 = _dissolution2ElapsedRunMinutes;
|
||||||
|
|
||||||
|
Dissolution1ElapsedTime = elapsed1;
|
||||||
|
Dissolution2ElapsedTime = elapsed2;
|
||||||
DissolutionElapsedTime = Math.Max(elapsed1, elapsed2);
|
DissolutionElapsedTime = Math.Max(elapsed1, elapsed2);
|
||||||
|
|
||||||
|
Dissolution1Countdown = ResolveDissolutionCountdown(1);
|
||||||
|
Dissolution2Countdown = ResolveDissolutionCountdown(2);
|
||||||
|
|
||||||
var remaining = new List<double>();
|
var remaining = new List<double>();
|
||||||
if (_isDissolution1Running)
|
if (_isDissolution1Running)
|
||||||
remaining.Add(Math.Max(0, Dissolution1TimeMin - elapsed1));
|
remaining.Add(Dissolution1Countdown);
|
||||||
if (_isDissolution2Running)
|
if (_isDissolution2Running)
|
||||||
remaining.Add(Math.Max(0, Dissolution2TimeMin - elapsed2));
|
remaining.Add(Dissolution2Countdown);
|
||||||
|
|
||||||
DissolutionCountdown = remaining.Count == 0 ? 0 : remaining.Min();
|
DissolutionCountdown = remaining.Count == 0 ? 0 : remaining.Min();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private double ResolveDissolutionCountdown(int channel)
|
||||||
|
{
|
||||||
|
if (channel == 1 && !_isDissolution1Running)
|
||||||
|
return 0;
|
||||||
|
if (channel == 2 && !_isDissolution2Running)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
double elapsed = GetDissolutionElapsedMinutes(channel);
|
||||||
|
double totalRemaining = Math.Max(0, GetDissolutionDurationMinutes(channel) - elapsed);
|
||||||
|
var nextSample = GetNextPendingDissolutionSample(channel);
|
||||||
|
if (nextSample == null)
|
||||||
|
return totalRemaining;
|
||||||
|
|
||||||
|
return Math.Max(0, Math.Min(nextSample.ScheduledTimeMin - elapsed, totalRemaining));
|
||||||
|
}
|
||||||
|
|
||||||
partial void OnDissolution1TimeMinChanged(int value)
|
partial void OnDissolution1TimeMinChanged(int value)
|
||||||
{
|
{
|
||||||
if (_isLoadingDissolution1Time || _plcConfig.Dissolution1Time == 0 || value <= 0)
|
if (_isLoadingDissolution1Time || _plcConfig.Dissolution1Time == 0 || value <= 0)
|
||||||
@@ -927,7 +1043,7 @@ namespace TabletTester2025.ViewModels
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_isLoadingDissolution1SampleInterval = true;
|
_isLoadingDissolution1SampleInterval = true;
|
||||||
int value = await _plc.ReadIntAsync(_plcConfig.Dissolution1SampleInterval);
|
double value = await _plc.ReadFloatAsync(_plcConfig.Dissolution1SampleInterval);
|
||||||
if (value > 0)
|
if (value > 0)
|
||||||
{
|
{
|
||||||
Dissolution1SampleIntervalMin = value;
|
Dissolution1SampleIntervalMin = value;
|
||||||
@@ -946,7 +1062,7 @@ namespace TabletTester2025.ViewModels
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_isLoadingDissolution2SampleInterval = true;
|
_isLoadingDissolution2SampleInterval = true;
|
||||||
int value = await _plc.ReadIntAsync(_plcConfig.Dissolution2SampleInterval);
|
double value = await _plc.ReadFloatAsync(_plcConfig.Dissolution2SampleInterval);
|
||||||
if (value > 0)
|
if (value > 0)
|
||||||
Dissolution2SampleIntervalMin = value;
|
Dissolution2SampleIntervalMin = value;
|
||||||
}
|
}
|
||||||
@@ -965,7 +1081,7 @@ namespace TabletTester2025.ViewModels
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _plc.WriteRegisterAsync(registerAddress, (ushort)Math.Clamp(ToCompatibleSampleInterval(value), 1, ushort.MaxValue));
|
await _plc.WriteFloatAsync(registerAddress, (float)value);
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
}
|
}
|
||||||
@@ -1735,9 +1851,11 @@ namespace TabletTester2025.ViewModels
|
|||||||
DissolutionPass = false;
|
DissolutionPass = false;
|
||||||
ResetDissolutionChannel(1);
|
ResetDissolutionChannel(1);
|
||||||
ResetDissolutionSampleState(1);
|
ResetDissolutionSampleState(1);
|
||||||
|
ResetDissolutionRunClock(1);
|
||||||
CreateDissolutionSampleSchedule(1);
|
CreateDissolutionSampleSchedule(1);
|
||||||
_dissolution1StartTime = DateTime.Now;
|
_dissolution1StartTime = DateTime.Now;
|
||||||
_isDissolution1Running = true;
|
_isDissolution1Running = true;
|
||||||
|
ResumeDissolutionRunClock(1);
|
||||||
DissolutionPlotModel.Title = "溶出曲线";
|
DissolutionPlotModel.Title = "溶出曲线";
|
||||||
await WriteDissolutionTimeAsync(_plcConfig.Dissolution1Time, Dissolution1TimeMin);
|
await WriteDissolutionTimeAsync(_plcConfig.Dissolution1Time, Dissolution1TimeMin);
|
||||||
await WriteDissolutionSampleIntervalAsync(_plcConfig.Dissolution1SampleInterval, Dissolution1SampleIntervalMin);
|
await WriteDissolutionSampleIntervalAsync(_plcConfig.Dissolution1SampleInterval, Dissolution1SampleIntervalMin);
|
||||||
@@ -1748,6 +1866,7 @@ namespace TabletTester2025.ViewModels
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
PauseDissolutionRunClock(1);
|
||||||
await PulseCoilAsync(_plcConfig.Dissolution1StopCoil);
|
await PulseCoilAsync(_plcConfig.Dissolution1StopCoil);
|
||||||
await FinalizeDissolutionChannelAsync(1);
|
await FinalizeDissolutionChannelAsync(1);
|
||||||
}
|
}
|
||||||
@@ -1755,6 +1874,7 @@ namespace TabletTester2025.ViewModels
|
|||||||
{
|
{
|
||||||
_isDissolution1Running = false;
|
_isDissolution1Running = false;
|
||||||
ResetDissolutionSampleState(1);
|
ResetDissolutionSampleState(1);
|
||||||
|
_dissolution1LastRunUpdate = DateTime.MinValue;
|
||||||
Phase = _isDissolution2Running ? TestPhase.Running : TestPhase.Idle;
|
Phase = _isDissolution2Running ? TestPhase.Running : TestPhase.Idle;
|
||||||
UpdateDissolutionClock();
|
UpdateDissolutionClock();
|
||||||
}
|
}
|
||||||
@@ -1771,6 +1891,7 @@ namespace TabletTester2025.ViewModels
|
|||||||
_isDissolution1Running = false;
|
_isDissolution1Running = false;
|
||||||
ResetDissolutionChannel(1);
|
ResetDissolutionChannel(1);
|
||||||
ResetDissolutionSampleState(1);
|
ResetDissolutionSampleState(1);
|
||||||
|
ResetDissolutionRunClock(1);
|
||||||
Phase = _isDissolution2Running ? TestPhase.Running : TestPhase.Idle;
|
Phase = _isDissolution2Running ? TestPhase.Running : TestPhase.Idle;
|
||||||
UpdateDissolutionClock();
|
UpdateDissolutionClock();
|
||||||
}
|
}
|
||||||
@@ -1786,9 +1907,11 @@ namespace TabletTester2025.ViewModels
|
|||||||
DissolutionPass = false;
|
DissolutionPass = false;
|
||||||
ResetDissolutionChannel(2);
|
ResetDissolutionChannel(2);
|
||||||
ResetDissolutionSampleState(2);
|
ResetDissolutionSampleState(2);
|
||||||
|
ResetDissolutionRunClock(2);
|
||||||
CreateDissolutionSampleSchedule(2);
|
CreateDissolutionSampleSchedule(2);
|
||||||
_dissolution2StartTime = DateTime.Now;
|
_dissolution2StartTime = DateTime.Now;
|
||||||
_isDissolution2Running = true;
|
_isDissolution2Running = true;
|
||||||
|
ResumeDissolutionRunClock(2);
|
||||||
DissolutionPlotModel.Title = "溶出曲线";
|
DissolutionPlotModel.Title = "溶出曲线";
|
||||||
await WriteDissolutionTimeAsync(_plcConfig.Dissolution2Time, Dissolution2TimeMin);
|
await WriteDissolutionTimeAsync(_plcConfig.Dissolution2Time, Dissolution2TimeMin);
|
||||||
await WriteDissolutionSampleIntervalAsync(_plcConfig.Dissolution2SampleInterval, Dissolution2SampleIntervalMin);
|
await WriteDissolutionSampleIntervalAsync(_plcConfig.Dissolution2SampleInterval, Dissolution2SampleIntervalMin);
|
||||||
@@ -1799,6 +1922,7 @@ namespace TabletTester2025.ViewModels
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
PauseDissolutionRunClock(2);
|
||||||
await PulseCoilAsync(_plcConfig.Dissolution2StopCoil);
|
await PulseCoilAsync(_plcConfig.Dissolution2StopCoil);
|
||||||
await FinalizeDissolutionChannelAsync(2);
|
await FinalizeDissolutionChannelAsync(2);
|
||||||
}
|
}
|
||||||
@@ -1806,6 +1930,7 @@ namespace TabletTester2025.ViewModels
|
|||||||
{
|
{
|
||||||
_isDissolution2Running = false;
|
_isDissolution2Running = false;
|
||||||
ResetDissolutionSampleState(2);
|
ResetDissolutionSampleState(2);
|
||||||
|
_dissolution2LastRunUpdate = DateTime.MinValue;
|
||||||
Phase = _isDissolution1Running ? TestPhase.Running : TestPhase.Idle;
|
Phase = _isDissolution1Running ? TestPhase.Running : TestPhase.Idle;
|
||||||
UpdateDissolutionClock();
|
UpdateDissolutionClock();
|
||||||
}
|
}
|
||||||
@@ -1822,6 +1947,7 @@ namespace TabletTester2025.ViewModels
|
|||||||
_isDissolution2Running = false;
|
_isDissolution2Running = false;
|
||||||
ResetDissolutionChannel(2);
|
ResetDissolutionChannel(2);
|
||||||
ResetDissolutionSampleState(2);
|
ResetDissolutionSampleState(2);
|
||||||
|
ResetDissolutionRunClock(2);
|
||||||
Phase = _isDissolution1Running ? TestPhase.Running : TestPhase.Idle;
|
Phase = _isDissolution1Running ? TestPhase.Running : TestPhase.Idle;
|
||||||
UpdateDissolutionClock();
|
UpdateDissolutionClock();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -449,6 +449,7 @@
|
|||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
<RowDefinition Height="Auto"/>
|
<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">
|
||||||
@@ -476,7 +477,36 @@
|
|||||||
</WrapPanel>
|
</WrapPanel>
|
||||||
</GroupBox>
|
</GroupBox>
|
||||||
|
|
||||||
<GroupBox Header="测试结果" Grid.Row="1">
|
<GroupBox Header="运行计时" Grid.Row="1">
|
||||||
|
<UniformGrid Columns="4">
|
||||||
|
<Border Style="{StaticResource MetricCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="溶出1累计(min)" Style="{StaticResource MetricLabel}"/>
|
||||||
|
<TextBlock Text="{Binding Dissolution1ElapsedTime, StringFormat=F1}" Foreground="#2E7D32" Style="{StaticResource MetricValue}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
<Border Style="{StaticResource MetricCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="溶出1倒计时(min)" Style="{StaticResource MetricLabel}"/>
|
||||||
|
<TextBlock Text="{Binding Dissolution1Countdown, StringFormat=F1}" Foreground="#2E7D32" Style="{StaticResource MetricValue}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
<Border Style="{StaticResource MetricCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="溶出2累计(min)" Style="{StaticResource MetricLabel}"/>
|
||||||
|
<TextBlock Text="{Binding Dissolution2ElapsedTime, StringFormat=F1}" Foreground="#1565C0" Style="{StaticResource MetricValue}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
<Border Style="{StaticResource MetricCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="溶出2倒计时(min)" Style="{StaticResource MetricLabel}"/>
|
||||||
|
<TextBlock Text="{Binding Dissolution2Countdown, StringFormat=F1}" Foreground="#1565C0" Style="{StaticResource MetricValue}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</UniformGrid>
|
||||||
|
</GroupBox>
|
||||||
|
|
||||||
|
<GroupBox Header="测试结果" Grid.Row="2">
|
||||||
<UniformGrid Columns="4">
|
<UniformGrid Columns="4">
|
||||||
<Border Style="{StaticResource MetricCard}">
|
<Border Style="{StaticResource MetricCard}">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
@@ -505,7 +535,7 @@
|
|||||||
</UniformGrid>
|
</UniformGrid>
|
||||||
</GroupBox>
|
</GroupBox>
|
||||||
|
|
||||||
<GroupBox Header="溶出双曲线和R²值" Grid.Row="2">
|
<GroupBox Header="溶出双曲线和R²值" Grid.Row="3">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="240"/>
|
<RowDefinition Height="240"/>
|
||||||
@@ -533,7 +563,7 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</GroupBox>
|
</GroupBox>
|
||||||
|
|
||||||
<GroupBox Header="取样记录" Grid.Row="3">
|
<GroupBox Header="取样记录" Grid.Row="4">
|
||||||
<DataGrid ItemsSource="{Binding DissolutionSamplePoints}"
|
<DataGrid ItemsSource="{Binding DissolutionSamplePoints}"
|
||||||
AutoGenerateColumns="False"
|
AutoGenerateColumns="False"
|
||||||
HeadersVisibility="Column"
|
HeadersVisibility="Column"
|
||||||
|
|||||||
@@ -123,32 +123,11 @@
|
|||||||
<StackPanel>
|
<StackPanel>
|
||||||
<WrapPanel>
|
<WrapPanel>
|
||||||
<StackPanel Style="{StaticResource ParamRow}">
|
<StackPanel Style="{StaticResource ParamRow}">
|
||||||
<TextBlock Text="剂型规格:" Style="{StaticResource ParamLabel}"/>
|
<TextBlock Text="崩解时间(min):" Style="{StaticResource ParamLabel}"/>
|
||||||
<ComboBox x:Name="DisintegrationDosageFormBox"
|
<TextBox x:Name="DisintegrationTimeMinBox"/>
|
||||||
Width="180"
|
|
||||||
Height="40"
|
|
||||||
FontSize="15"
|
|
||||||
SelectionChanged="DisintegrationDosageFormBox_SelectionChanged">
|
|
||||||
<ComboBoxItem Content="普通片" Tag="900"/>
|
|
||||||
<ComboBoxItem Content="薄膜衣片" Tag="1800"/>
|
|
||||||
<ComboBoxItem Content="糖衣片" Tag="3600"/>
|
|
||||||
<ComboBoxItem Content="胶囊" Tag="1800"/>
|
|
||||||
</ComboBox>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<!--<StackPanel Style="{StaticResource ParamRow}">
|
|
||||||
<TextBlock Text="最长崩解时间(秒):" Style="{StaticResource ParamLabel}"/>
|
|
||||||
<TextBox x:Name="DisintegrationMaxSecBox" helpers:NumericInput.AllowDecimal="False"/>
|
|
||||||
</StackPanel>-->
|
|
||||||
<StackPanel Style="{StaticResource ParamRow}">
|
|
||||||
<TextBlock Text="升降频率(次/min):" Style="{StaticResource ParamLabel}"/>
|
|
||||||
<TextBox x:Name="DisintegrationSpeedBox"/>
|
|
||||||
</StackPanel>
|
|
||||||
<!--<StackPanel Style="{StaticResource ParamRow}">
|
|
||||||
<TextBlock Text="介质温度(℃):" Style="{StaticResource ParamLabel}"/>
|
|
||||||
<TextBox x:Name="DisintegrationTempBox"/>
|
|
||||||
</StackPanel>-->
|
|
||||||
</WrapPanel>
|
</WrapPanel>
|
||||||
<TextBlock Text="默认:升降频率30-32次/min,介质温度37±1℃。不同剂型按药典或品种正文规定时限执行。"
|
<TextBlock Text="崩解时间按具体品种正文或企业批准标准设置,保存后立即写入设备。"
|
||||||
Style="{StaticResource StandardNote}"/>
|
Style="{StaticResource StandardNote}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</GroupBox>
|
</GroupBox>
|
||||||
@@ -170,11 +149,11 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Style="{StaticResource ParamRow}">
|
<StackPanel Style="{StaticResource ParamRow}">
|
||||||
<TextBlock Text="溶出1取样间隔(min):" Style="{StaticResource ParamLabel}"/>
|
<TextBlock Text="溶出1取样间隔(min):" Style="{StaticResource ParamLabel}"/>
|
||||||
<TextBox x:Name="Dissolution1IntervalBox" helpers:NumericInput.AllowDecimal="False"/>
|
<TextBox x:Name="Dissolution1IntervalBox" helpers:NumericInput.AllowDecimal="True"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Style="{StaticResource ParamRow}">
|
<StackPanel Style="{StaticResource ParamRow}">
|
||||||
<TextBlock Text="溶出2取样间隔(min):" Style="{StaticResource ParamLabel}"/>
|
<TextBlock Text="溶出2取样间隔(min):" Style="{StaticResource ParamLabel}"/>
|
||||||
<TextBox x:Name="Dissolution2IntervalBox" helpers:NumericInput.AllowDecimal="False"/>
|
<TextBox x:Name="Dissolution2IntervalBox" helpers:NumericInput.AllowDecimal="True"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</WrapPanel>
|
</WrapPanel>
|
||||||
<TextBlock Text="默认:普通制剂通常取6片,溶出介质温度37±0.5℃;转速和取样间隔应按具体品种正文或企业批准标准录入。"
|
<TextBlock Text="默认:普通制剂通常取6片,溶出介质温度37±0.5℃;转速和取样间隔应按具体品种正文或企业批准标准录入。"
|
||||||
|
|||||||
@@ -25,14 +25,11 @@ namespace TabletTester2025
|
|||||||
await LoadPlcFloatToTextBoxAsync(ResolveFriabilityRpmRegister(), FriabilityRpmBox);
|
await LoadPlcFloatToTextBoxAsync(ResolveFriabilityRpmRegister(), FriabilityRpmBox);
|
||||||
await LoadPlcIntToTextBoxAsync(ResolveFriabilityRoundsRegister(), FriabilityRoundsBox);
|
await LoadPlcIntToTextBoxAsync(ResolveFriabilityRoundsRegister(), FriabilityRoundsBox);
|
||||||
|
|
||||||
// 崩解
|
|
||||||
await LoadPlcFloatToTextBoxAsync(ResolveDisintegrationSpeedRegister(), DisintegrationSpeedBox);
|
|
||||||
|
|
||||||
// 溶出度
|
// 溶出度
|
||||||
await LoadPlcFloatToTextBoxAsync(ResolveDissolution1SpeedRegister(), Dissolution1SpeedBox);
|
await LoadPlcFloatToTextBoxAsync(ResolveDissolution1SpeedRegister(), Dissolution1SpeedBox);
|
||||||
await LoadPlcFloatToTextBoxAsync(ResolveDissolution2SpeedRegister(), Dissolution2SpeedBox);
|
await LoadPlcFloatToTextBoxAsync(ResolveDissolution2SpeedRegister(), Dissolution2SpeedBox);
|
||||||
await LoadPlcIntToTextBoxAsync(ResolveDissolution1IntervalRegister(), Dissolution1IntervalBox);
|
await LoadPlcFloatToTextBoxAsync(ResolveDissolution1IntervalRegister(), Dissolution1IntervalBox);
|
||||||
await LoadPlcIntToTextBoxAsync(ResolveDissolution2IntervalRegister(), Dissolution2IntervalBox);
|
await LoadPlcFloatToTextBoxAsync(ResolveDissolution2IntervalRegister(), Dissolution2IntervalBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadSettings()
|
private void LoadSettings()
|
||||||
@@ -43,9 +40,7 @@ namespace TabletTester2025
|
|||||||
//FriabilityTimeBox.Text = ResolveFriabilityTargetTimeMin(p).ToString("0.###");
|
//FriabilityTimeBox.Text = ResolveFriabilityTargetTimeMin(p).ToString("0.###");
|
||||||
FriabilityRoundsBox.Text = ResolveFriabilityTargetRounds(p).ToString();
|
FriabilityRoundsBox.Text = ResolveFriabilityTargetRounds(p).ToString();
|
||||||
FriabilityMaxLossBox.Text = p.FriabilityMaxLossPercent.ToString();
|
FriabilityMaxLossBox.Text = p.FriabilityMaxLossPercent.ToString();
|
||||||
SelectDisintegrationDosageForm(p.DisintegrationDosageForm);
|
DisintegrationTimeMinBox.Text = ResolveDisintegrationTimeMin(p).ToString("0.###");
|
||||||
//DisintegrationMaxSecBox.Text = p.DisintegrationMaxSeconds.ToString();
|
|
||||||
DisintegrationSpeedBox.Text = p.DisintegrationSpeedRpm.ToString();
|
|
||||||
//DisintegrationTempBox.Text = p.DisintegrationTemperatureC.ToString();
|
//DisintegrationTempBox.Text = p.DisintegrationTemperatureC.ToString();
|
||||||
//DissolutionTempBox.Text = p.DissolutionTemperatureC.ToString();
|
//DissolutionTempBox.Text = p.DissolutionTemperatureC.ToString();
|
||||||
Dissolution1IntervalBox.Text = p.Dissolution1SampleIntervalMin.ToString();
|
Dissolution1IntervalBox.Text = p.Dissolution1SampleIntervalMin.ToString();
|
||||||
@@ -62,7 +57,12 @@ namespace TabletTester2025
|
|||||||
StandardVersion = current.StandardVersion,
|
StandardVersion = current.StandardVersion,
|
||||||
HardnessMin_N = current.HardnessMin_N,
|
HardnessMin_N = current.HardnessMin_N,
|
||||||
HardnessMax_N = current.HardnessMax_N,
|
HardnessMax_N = current.HardnessMax_N,
|
||||||
|
FriabilityTargetTimeMin = current.FriabilityTargetTimeMin,
|
||||||
DissolutionMinPercentAt30min = current.DissolutionMinPercentAt30min,
|
DissolutionMinPercentAt30min = current.DissolutionMinPercentAt30min,
|
||||||
|
DisintegrationDosageForm = current.DisintegrationDosageForm,
|
||||||
|
DisintegrationSpeedRpm = current.DisintegrationSpeedRpm,
|
||||||
|
DisintegrationTemperatureC = current.DisintegrationTemperatureC,
|
||||||
|
DissolutionTemperatureC = current.DissolutionTemperatureC,
|
||||||
Dissolution1TimeMin = current.Dissolution1TimeMin,
|
Dissolution1TimeMin = current.Dissolution1TimeMin,
|
||||||
Dissolution2TimeMin = current.Dissolution2TimeMin,
|
Dissolution2TimeMin = current.Dissolution2TimeMin,
|
||||||
DissolutionSampleTimes = current.DissolutionSampleTimes?.ToArray() ?? Array.Empty<int>()
|
DissolutionSampleTimes = current.DissolutionSampleTimes?.ToArray() ?? Array.Empty<int>()
|
||||||
@@ -75,22 +75,24 @@ namespace TabletTester2025
|
|||||||
//p.FriabilityTargetTimeMin = ParseFiniteDouble(FriabilityTimeBox.Text, "脆碎度试验时间");
|
//p.FriabilityTargetTimeMin = ParseFiniteDouble(FriabilityTimeBox.Text, "脆碎度试验时间");
|
||||||
p.FriabilityTargetRounds = ParsePositiveInt(FriabilityRoundsBox.Text, "脆碎圈数");
|
p.FriabilityTargetRounds = ParsePositiveInt(FriabilityRoundsBox.Text, "脆碎圈数");
|
||||||
p.FriabilityMaxLossPercent = ParseFiniteDouble(FriabilityMaxLossBox.Text, "最大失重率");
|
p.FriabilityMaxLossPercent = ParseFiniteDouble(FriabilityMaxLossBox.Text, "最大失重率");
|
||||||
p.DisintegrationDosageForm = GetSelectedDisintegrationDosageForm();
|
double disintegrationTimeMin = ParsePositiveDouble(DisintegrationTimeMinBox.Text, "崩解时间");
|
||||||
//p.DisintegrationMaxSeconds = int.Parse(DisintegrationMaxSecBox.Text); //崩解最长时间
|
p.DisintegrationMaxSeconds = ToDisintegrationSeconds(disintegrationTimeMin);
|
||||||
p.DisintegrationSpeedRpm = ParseFiniteDouble(DisintegrationSpeedBox.Text, "崩解升降频率");
|
|
||||||
//p.DisintegrationTemperatureC = ParseFiniteDouble(DisintegrationTempBox.Text, "崩解介质温度");
|
//p.DisintegrationTemperatureC = ParseFiniteDouble(DisintegrationTempBox.Text, "崩解介质温度");
|
||||||
//p.DissolutionTemperatureC = ParseFiniteDouble(DissolutionTempBox.Text, "溶出介质温度");
|
//p.DissolutionTemperatureC = ParseFiniteDouble(DissolutionTempBox.Text, "溶出介质温度");
|
||||||
double dissolution1Speed = ParsePositiveDouble(Dissolution1SpeedBox.Text, "溶出速度1");
|
double dissolution1Speed = ParsePositiveDouble(Dissolution1SpeedBox.Text, "溶出速度1");
|
||||||
double dissolution2Speed = ParsePositiveDouble(Dissolution2SpeedBox.Text, "溶出速度2");
|
double dissolution2Speed = ParsePositiveDouble(Dissolution2SpeedBox.Text, "溶出速度2");
|
||||||
p.Dissolution1SampleIntervalMin = ParsePositiveInt(Dissolution1IntervalBox.Text, "溶出1取样间隔");
|
p.Dissolution1SampleIntervalMin = ParsePositiveDouble(Dissolution1IntervalBox.Text, "溶出1取样间隔");
|
||||||
p.Dissolution2SampleIntervalMin = ParsePositiveInt(Dissolution2IntervalBox.Text, "溶出2取样间隔");
|
p.Dissolution2SampleIntervalMin = ParsePositiveDouble(Dissolution2IntervalBox.Text, "溶出2取样间隔");
|
||||||
|
|
||||||
ValidateParameters(p);
|
ValidateParameters(p);
|
||||||
await WriteHardnessPressureAsync(hardnessPressure);
|
await WriteHardnessPressureAsync(hardnessPressure);
|
||||||
await WriteHardnessDamageThresholdAsync(hardnessDamageThreshold);
|
await WriteHardnessDamageThresholdAsync(hardnessDamageThreshold);
|
||||||
await WriteFriabilityRpmAsync(friabilityRpm);
|
await WriteFriabilityRpmAsync(friabilityRpm);
|
||||||
|
await WriteDisintegrationTimeAsync(disintegrationTimeMin);
|
||||||
await WriteDissolution1SpeedAsync(dissolution1Speed);
|
await WriteDissolution1SpeedAsync(dissolution1Speed);
|
||||||
await WriteDissolution2SpeedAsync(dissolution2Speed);
|
await WriteDissolution2SpeedAsync(dissolution2Speed);
|
||||||
|
await WriteDissolution1IntervalAsync(p.Dissolution1SampleIntervalMin);
|
||||||
|
await WriteDissolution2IntervalAsync(p.Dissolution2SampleIntervalMin);
|
||||||
App.CurrentPharmaParams = p;
|
App.CurrentPharmaParams = p;
|
||||||
App.SaveCurrentPharmaParameters();
|
App.SaveCurrentPharmaParameters();
|
||||||
|
|
||||||
@@ -110,33 +112,6 @@ namespace TabletTester2025
|
|||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DisintegrationDosageFormBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
|
||||||
{
|
|
||||||
//if (DisintegrationDosageFormBox.SelectedItem is ComboBoxItem item && item.Tag is string seconds)
|
|
||||||
// DisintegrationMaxSecBox.Text = seconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SelectDisintegrationDosageForm(string dosageForm)
|
|
||||||
{
|
|
||||||
foreach (ComboBoxItem item in DisintegrationDosageFormBox.Items)
|
|
||||||
{
|
|
||||||
if (string.Equals(item.Content?.ToString(), dosageForm, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
DisintegrationDosageFormBox.SelectedItem = item;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DisintegrationDosageFormBox.SelectedIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetSelectedDisintegrationDosageForm()
|
|
||||||
{
|
|
||||||
return DisintegrationDosageFormBox.SelectedItem is ComboBoxItem item
|
|
||||||
? item.Content?.ToString() ?? "普通片"
|
|
||||||
: "普通片";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ValidateParameters(PharmaParameters p)
|
private static void ValidateParameters(PharmaParameters p)
|
||||||
{
|
{
|
||||||
if (!double.IsFinite(p.HardnessMin_N) || !double.IsFinite(p.HardnessMax_N))
|
if (!double.IsFinite(p.HardnessMin_N) || !double.IsFinite(p.HardnessMax_N))
|
||||||
@@ -306,9 +281,21 @@ namespace TabletTester2025
|
|||||||
return App.PlcConfig.FriabilityTestTime != 0 ? App.PlcConfig.FriabilityTestTime : (ushort)410;
|
return App.PlcConfig.FriabilityTestTime != 0 ? App.PlcConfig.FriabilityTestTime : (ushort)410;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ushort ResolveDisintegrationSpeedRegister()
|
private static ushort ResolveDisintegrationTimeRegister()
|
||||||
{
|
{
|
||||||
return App.PlcConfig.DisintegrationSpeed != 0 ? App.PlcConfig.DisintegrationSpeed : (ushort)330;
|
return App.PlcConfig.DisintegrationTime != 0 ? App.PlcConfig.DisintegrationTime : (ushort)420;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task WriteDisintegrationTimeAsync(double value)
|
||||||
|
{
|
||||||
|
ushort registerAddress = ResolveDisintegrationTimeRegister();
|
||||||
|
if (registerAddress == 0)
|
||||||
|
throw new InvalidOperationException("崩解时间PLC寄存器地址未配置。");
|
||||||
|
|
||||||
|
await App.PlcService.WriteRegisterAsync(registerAddress, (ushort)Math.Clamp(
|
||||||
|
(int)Math.Round(value, MidpointRounding.AwayFromZero),
|
||||||
|
1,
|
||||||
|
ushort.MaxValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task WriteDissolution1SpeedAsync(double value)
|
private static async Task WriteDissolution1SpeedAsync(double value)
|
||||||
@@ -329,6 +316,24 @@ namespace TabletTester2025
|
|||||||
await App.PlcService.WriteFloatAsync(registerAddress, (float)value);
|
await App.PlcService.WriteFloatAsync(registerAddress, (float)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async Task WriteDissolution1IntervalAsync(double value)
|
||||||
|
{
|
||||||
|
ushort registerAddress = ResolveDissolution1IntervalRegister();
|
||||||
|
if (registerAddress == 0)
|
||||||
|
throw new InvalidOperationException("溶出1取样间隔PLC寄存器地址未配置。");
|
||||||
|
|
||||||
|
await App.PlcService.WriteFloatAsync(registerAddress, (float)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task WriteDissolution2IntervalAsync(double value)
|
||||||
|
{
|
||||||
|
ushort registerAddress = ResolveDissolution2IntervalRegister();
|
||||||
|
if (registerAddress == 0)
|
||||||
|
throw new InvalidOperationException("溶出2取样间隔PLC寄存器地址未配置。");
|
||||||
|
|
||||||
|
await App.PlcService.WriteFloatAsync(registerAddress, (float)value);
|
||||||
|
}
|
||||||
|
|
||||||
private static ushort ResolveDissolution1SpeedRegister()
|
private static ushort ResolveDissolution1SpeedRegister()
|
||||||
{
|
{
|
||||||
return App.PlcConfig.Dissolution1Speed != 0 ? App.PlcConfig.Dissolution1Speed : (ushort)340;
|
return App.PlcConfig.Dissolution1Speed != 0 ? App.PlcConfig.Dissolution1Speed : (ushort)340;
|
||||||
@@ -368,5 +373,17 @@ namespace TabletTester2025
|
|||||||
|
|
||||||
return 100;
|
return 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static double ResolveDisintegrationTimeMin(PharmaParameters p)
|
||||||
|
{
|
||||||
|
return p.DisintegrationMaxSeconds > 0
|
||||||
|
? p.DisintegrationMaxSeconds / 60.0
|
||||||
|
: 15.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int ToDisintegrationSeconds(double minutes)
|
||||||
|
{
|
||||||
|
return Math.Max(1, (int)Math.Round(minutes * 60, MidpointRounding.AwayFromZero));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,8 +67,8 @@
|
|||||||
"Dissolution2SampleAckCoil": 34,
|
"Dissolution2SampleAckCoil": 34,
|
||||||
"Dissolution1Time": 430,
|
"Dissolution1Time": 430,
|
||||||
"Dissolution2Time": 440,
|
"Dissolution2Time": 440,
|
||||||
"Dissolution1SampleInterval": 432, // 溶出1取样间隔,int类型
|
"Dissolution1SampleInterval": 432, // 溶出1取样间隔,float类型
|
||||||
"Dissolution2SampleInterval": 442 // 溶出2取样间隔,int类型
|
"Dissolution2SampleInterval": 442 // 溶出2取样间隔,float类型
|
||||||
},
|
},
|
||||||
"PharmaStandard": {
|
"PharmaStandard": {
|
||||||
"StandardVersion": "中国药典2025",
|
"StandardVersion": "中国药典2025",
|
||||||
|
|||||||
Reference in New Issue
Block a user