更新20260518
This commit is contained in:
@@ -7,6 +7,7 @@ namespace TabletTester2025.Models
|
|||||||
public double HardnessMax_N { get; set; } = 60;
|
public double HardnessMax_N { get; set; } = 60;
|
||||||
public int HardnessTestCount { get; set; } = 6;
|
public int HardnessTestCount { get; set; } = 6;
|
||||||
public double FriabilityTargetRpm { get; set; } = 25.0;
|
public double FriabilityTargetRpm { get; set; } = 25.0;
|
||||||
|
public double FriabilityTargetTimeMin { get; set; } = 4.0;
|
||||||
public int FriabilityTargetRounds { get; set; } = 100;
|
public int FriabilityTargetRounds { get; set; } = 100;
|
||||||
public double FriabilityMaxLossPercent { get; set; } = 1.0;
|
public double FriabilityMaxLossPercent { get; set; } = 1.0;
|
||||||
public string DisintegrationDosageForm { get; set; } = "普通片";
|
public string DisintegrationDosageForm { get; set; } = "普通片";
|
||||||
|
|||||||
@@ -25,6 +25,9 @@
|
|||||||
public ushort HardnessPoSun { get; set; }
|
public ushort HardnessPoSun { get; set; }
|
||||||
// 脆碎度
|
// 脆碎度
|
||||||
public ushort FriabilityStartCoil { get; set; }
|
public ushort FriabilityStartCoil { get; set; }
|
||||||
|
public ushort FriabilityTestTime { get; set; }
|
||||||
|
public ushort FriabilityWeightBefore { get; set; }
|
||||||
|
public ushort FriabilityWeightAfter { get; set; }
|
||||||
public ushort WeightBefore { get; set; } // 天平重量寄存器(可选)
|
public ushort WeightBefore { get; set; } // 天平重量寄存器(可选)
|
||||||
public ushort WeightAfter { get; set; }
|
public ushort WeightAfter { get; set; }
|
||||||
public ushort FriabilityStartCoil2 { get; set; }
|
public ushort FriabilityStartCoil2 { get; set; }
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ namespace TabletTester2025.Models
|
|||||||
public double WeightBefore { get; set; }
|
public double WeightBefore { get; set; }
|
||||||
public double WeightAfter { get; set; }
|
public double WeightAfter { get; set; }
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public double FriabilityTargetTimeMin => FriabilityTargetTimeSec / 60.0;
|
||||||
|
|
||||||
// 崩解
|
// 崩解
|
||||||
public double DisintegrationTimeSec { get; set; }
|
public double DisintegrationTimeSec { get; set; }
|
||||||
public int RemainingTubesAtEnd { get; set; }
|
public int RemainingTubesAtEnd { get; set; }
|
||||||
|
|||||||
@@ -92,7 +92,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, "检测时间", "样品名称", "失重率(%)", "设定转速(r/min)", "试验转数", "前重(g)", "后重(g)", "判定");
|
WriteHeader(sheet, "检测时间", "样品名称", "失重率(%)", "设定转速(r/min)", "试验时间(min)", "试验转数", "前重(g)", "后重(g)", "判定");
|
||||||
|
|
||||||
if (data.Count == 0)
|
if (data.Count == 0)
|
||||||
{
|
{
|
||||||
@@ -108,10 +108,11 @@ namespace TabletTester2025.Services
|
|||||||
sheet.Cells[row, 2].Value = b.SampleName;
|
sheet.Cells[row, 2].Value = b.SampleName;
|
||||||
sheet.Cells[row, 3].Value = b.FriabilityLoss;
|
sheet.Cells[row, 3].Value = b.FriabilityLoss;
|
||||||
sheet.Cells[row, 4].Value = b.FriabilityTargetRpm;
|
sheet.Cells[row, 4].Value = b.FriabilityTargetRpm;
|
||||||
sheet.Cells[row, 5].Value = b.FriabilityTargetRounds;
|
sheet.Cells[row, 5].Value = b.FriabilityTargetTimeMin;
|
||||||
sheet.Cells[row, 6].Value = b.WeightBefore;
|
sheet.Cells[row, 6].Value = b.FriabilityTargetRounds;
|
||||||
sheet.Cells[row, 7].Value = b.WeightAfter;
|
sheet.Cells[row, 7].Value = b.WeightBefore;
|
||||||
sheet.Cells[row, 8].Value = b.FriabilityPassText;
|
sheet.Cells[row, 8].Value = b.WeightAfter;
|
||||||
|
sheet.Cells[row, 9].Value = b.FriabilityPassText;
|
||||||
row++;
|
row++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ namespace TabletTester2025.Services
|
|||||||
float value = startAddress switch
|
float value = startAddress switch
|
||||||
{
|
{
|
||||||
100 => 40 + (float)_rand.NextDouble() * 20, // 硬度 40~60N
|
100 => 40 + (float)_rand.NextDouble() * 20, // 硬度 40~60N
|
||||||
|
410 => 4.0f, // 脆碎试验时间(min)
|
||||||
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, // 后重
|
||||||
300 => 37.0f, // 温度
|
300 => 37.0f, // 温度
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ namespace TabletTester2025.ViewModels
|
|||||||
private bool _isLoadingDissolution2SampleInterval;
|
private bool _isLoadingDissolution2SampleInterval;
|
||||||
private bool _isLoadingDisintegrationTime;
|
private bool _isLoadingDisintegrationTime;
|
||||||
private bool _isLoadingDisintegrationSpeed;
|
private bool _isLoadingDisintegrationSpeed;
|
||||||
|
private bool _isLoadingFriabilityTime;
|
||||||
private bool _isUpdatingFriabilityWeightFromPlc;
|
private bool _isUpdatingFriabilityWeightFromPlc;
|
||||||
|
|
||||||
private readonly List<double> _dissolution1Times = new();
|
private readonly List<double> _dissolution1Times = new();
|
||||||
@@ -123,7 +124,8 @@ namespace TabletTester2025.ViewModels
|
|||||||
|
|
||||||
// 脆碎度新增
|
// 脆碎度新增
|
||||||
[ObservableProperty] private double _friabilityTargetRpm = 25;
|
[ObservableProperty] private double _friabilityTargetRpm = 25;
|
||||||
[ObservableProperty] private int _friabilityTargetTimeSec = 4;
|
[ObservableProperty] private double _friabilityTargetTimeMin = 4;
|
||||||
|
[ObservableProperty] private int _friabilityTargetTimeSec = 240;
|
||||||
[ObservableProperty] private int _friabilityTargetRounds = 100;
|
[ObservableProperty] private int _friabilityTargetRounds = 100;
|
||||||
[ObservableProperty] private double _friabilityMaxLossPercent = 1.0;
|
[ObservableProperty] private double _friabilityMaxLossPercent = 1.0;
|
||||||
[ObservableProperty] private bool _friabilityClockwise = true;
|
[ObservableProperty] private bool _friabilityClockwise = true;
|
||||||
@@ -311,7 +313,7 @@ namespace TabletTester2025.ViewModels
|
|||||||
ResetDisintegrationCommand = new AsyncRelayCommand(ResetDisintegrationAsync);
|
ResetDisintegrationCommand = new AsyncRelayCommand(ResetDisintegrationAsync);
|
||||||
PrintDisintegrationCommand = new AsyncRelayCommand(async () => await PrintReport("崩解"));
|
PrintDisintegrationCommand = new AsyncRelayCommand(async () => await PrintReport("崩解"));
|
||||||
|
|
||||||
_ = LoadFriabilityWeightsAsync();
|
_ = LoadFriabilitySettingsAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ApplyPharmaDefaults()
|
public void ApplyPharmaDefaults()
|
||||||
@@ -323,13 +325,18 @@ namespace TabletTester2025.ViewModels
|
|||||||
_isLoadingDissolution2Time = true;
|
_isLoadingDissolution2Time = true;
|
||||||
_isLoadingDissolution1SampleInterval = true;
|
_isLoadingDissolution1SampleInterval = true;
|
||||||
_isLoadingDissolution2SampleInterval = true;
|
_isLoadingDissolution2SampleInterval = true;
|
||||||
|
_isLoadingFriabilityTime = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
HardnessInternalMin = p.HardnessMin_N;
|
HardnessInternalMin = p.HardnessMin_N;
|
||||||
HardnessInternalMax = p.HardnessMax_N;
|
HardnessInternalMax = p.HardnessMax_N;
|
||||||
HardnessTestCount = Math.Max(1, p.HardnessTestCount);
|
HardnessTestCount = Math.Max(1, p.HardnessTestCount);
|
||||||
FriabilityTargetRpm = p.FriabilityTargetRpm > 0 ? p.FriabilityTargetRpm : 25;
|
FriabilityTargetRpm = p.FriabilityTargetRpm > 0 ? p.FriabilityTargetRpm : 25;
|
||||||
FriabilityTargetRounds = p.FriabilityTargetRounds > 0 ? p.FriabilityTargetRounds : 100;
|
double defaultRounds = p.FriabilityTargetRounds > 0 ? p.FriabilityTargetRounds : 100;
|
||||||
|
FriabilityTargetTimeMin = p.FriabilityTargetTimeMin > 0
|
||||||
|
? p.FriabilityTargetTimeMin
|
||||||
|
: defaultRounds / FriabilityTargetRpm;
|
||||||
|
UpdateFriabilityTimingFromTime();
|
||||||
FriabilityMaxLossPercent = p.FriabilityMaxLossPercent;
|
FriabilityMaxLossPercent = p.FriabilityMaxLossPercent;
|
||||||
FriabilityRemainingRounds = FriabilityTargetRounds;
|
FriabilityRemainingRounds = FriabilityTargetRounds;
|
||||||
DisintegrationDosageForm = string.IsNullOrWhiteSpace(p.DisintegrationDosageForm) ? "普通片" : p.DisintegrationDosageForm;
|
DisintegrationDosageForm = string.IsNullOrWhiteSpace(p.DisintegrationDosageForm) ? "普通片" : p.DisintegrationDosageForm;
|
||||||
@@ -353,7 +360,10 @@ namespace TabletTester2025.ViewModels
|
|||||||
_isLoadingDissolution2Time = false;
|
_isLoadingDissolution2Time = false;
|
||||||
_isLoadingDissolution1SampleInterval = false;
|
_isLoadingDissolution1SampleInterval = false;
|
||||||
_isLoadingDissolution2SampleInterval = false;
|
_isLoadingDissolution2SampleInterval = false;
|
||||||
|
_isLoadingFriabilityTime = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_ = WriteFriabilityTimeAsync(FriabilityTargetTimeMin);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadPharmaDefaults()
|
private void LoadPharmaDefaults()
|
||||||
@@ -975,7 +985,7 @@ namespace TabletTester2025.ViewModels
|
|||||||
if (_isUpdatingFriabilityWeightFromPlc)
|
if (_isUpdatingFriabilityWeightFromPlc)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_ = WriteFriabilityWeightAsync(_plcConfig.WeightBefore, value);
|
_ = WriteFriabilityWeightAsync(ResolveFriabilityWeightBeforeRegister(), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnWeightAfterChanged(double value)
|
partial void OnWeightAfterChanged(double value)
|
||||||
@@ -983,7 +993,72 @@ namespace TabletTester2025.ViewModels
|
|||||||
if (_isUpdatingFriabilityWeightFromPlc)
|
if (_isUpdatingFriabilityWeightFromPlc)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_ = WriteFriabilityWeightAsync(_plcConfig.WeightAfter, value);
|
_ = WriteFriabilityWeightAsync(ResolveFriabilityWeightAfterRegister(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnFriabilityTargetTimeMinChanged(double value)
|
||||||
|
{
|
||||||
|
UpdateFriabilityTimingFromTime();
|
||||||
|
|
||||||
|
if (_isLoadingFriabilityTime)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_ = WriteFriabilityTimeAsync(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnFriabilityTargetRpmChanged(double value)
|
||||||
|
{
|
||||||
|
UpdateFriabilityTimingFromTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateFriabilityTimingFromTime()
|
||||||
|
{
|
||||||
|
if (!double.IsFinite(FriabilityTargetTimeMin) || FriabilityTargetTimeMin <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
double rpm = FriabilityTargetRpm > 0 ? FriabilityTargetRpm : 25;
|
||||||
|
FriabilityTargetTimeSec = (int)Math.Ceiling(FriabilityTargetTimeMin * 60);
|
||||||
|
FriabilityTargetRounds = Math.Max(1, (int)Math.Round(FriabilityTargetTimeMin * rpm, MidpointRounding.AwayFromZero));
|
||||||
|
|
||||||
|
if (Phase != TestPhase.Running)
|
||||||
|
FriabilityRemainingRounds = FriabilityTargetRounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadFriabilitySettingsAsync()
|
||||||
|
{
|
||||||
|
await LoadFriabilityTimeAsync();
|
||||||
|
await LoadFriabilityWeightsAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadFriabilityTimeAsync()
|
||||||
|
{
|
||||||
|
ushort registerAddress = ResolveFriabilityTestTimeRegister();
|
||||||
|
if (registerAddress == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_isLoadingFriabilityTime = true;
|
||||||
|
float value = await _plc.ReadFloatAsync(registerAddress);
|
||||||
|
if (float.IsFinite(value) && value > 0)
|
||||||
|
{
|
||||||
|
FriabilityTargetTimeMin = value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await WriteFriabilityTimeAsync(FriabilityTargetTimeMin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
try { await WriteFriabilityTimeAsync(FriabilityTargetTimeMin); }
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isLoadingFriabilityTime = false;
|
||||||
|
UpdateFriabilityTimingFromTime();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LoadFriabilityWeightsAsync()
|
private async Task LoadFriabilityWeightsAsync()
|
||||||
@@ -992,15 +1067,17 @@ namespace TabletTester2025.ViewModels
|
|||||||
{
|
{
|
||||||
_isUpdatingFriabilityWeightFromPlc = true;
|
_isUpdatingFriabilityWeightFromPlc = true;
|
||||||
|
|
||||||
if (_plcConfig.WeightBefore != 0)
|
ushort beforeRegister = ResolveFriabilityWeightBeforeRegister();
|
||||||
|
if (beforeRegister != 0)
|
||||||
{
|
{
|
||||||
double before = await ReadFriabilityWeightAsync(_plcConfig.WeightBefore, "脆碎前重量");
|
double before = await ReadFriabilityWeightAsync(beforeRegister, "脆碎前重量");
|
||||||
WeightBefore = before;
|
WeightBefore = before;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_plcConfig.WeightAfter != 0)
|
ushort afterRegister = ResolveFriabilityWeightAfterRegister();
|
||||||
|
if (afterRegister != 0)
|
||||||
{
|
{
|
||||||
double after = await ReadFriabilityWeightAsync(_plcConfig.WeightAfter, "脆碎后重量");
|
double after = await ReadFriabilityWeightAsync(afterRegister, "脆碎后重量");
|
||||||
WeightAfter = after;
|
WeightAfter = after;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1023,6 +1100,38 @@ namespace TabletTester2025.ViewModels
|
|||||||
catch { }
|
catch { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task WriteFriabilityTimeAsync(double value)
|
||||||
|
{
|
||||||
|
ushort registerAddress = ResolveFriabilityTestTimeRegister();
|
||||||
|
if (registerAddress == 0 || !double.IsFinite(value) || value <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _plc.WriteFloatAsync(registerAddress, (float)value);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
private ushort ResolveFriabilityTestTimeRegister()
|
||||||
|
{
|
||||||
|
return _plcConfig.FriabilityTestTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ushort ResolveFriabilityWeightBeforeRegister()
|
||||||
|
{
|
||||||
|
return _plcConfig.FriabilityWeightBefore != 0
|
||||||
|
? _plcConfig.FriabilityWeightBefore
|
||||||
|
: _plcConfig.WeightBefore;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ushort ResolveFriabilityWeightAfterRegister()
|
||||||
|
{
|
||||||
|
return _plcConfig.FriabilityWeightAfter != 0
|
||||||
|
? _plcConfig.FriabilityWeightAfter
|
||||||
|
: _plcConfig.WeightAfter;
|
||||||
|
}
|
||||||
|
|
||||||
private void SetFriabilityWeightFromPlc(double? weightBefore = null, double? weightAfter = null)
|
private void SetFriabilityWeightFromPlc(double? weightBefore = null, double? weightAfter = null)
|
||||||
{
|
{
|
||||||
_isUpdatingFriabilityWeightFromPlc = true;
|
_isUpdatingFriabilityWeightFromPlc = true;
|
||||||
@@ -1289,18 +1398,24 @@ namespace TabletTester2025.ViewModels
|
|||||||
{
|
{
|
||||||
throw new InvalidOperationException("未配置脆碎度启动线圈地址");
|
throw new InvalidOperationException("未配置脆碎度启动线圈地址");
|
||||||
}
|
}
|
||||||
double weightBefore = await ReadFriabilityWeightAsync(_plcConfig.WeightBefore, "脆碎前重量");
|
if (!double.IsFinite(FriabilityTargetTimeMin) || FriabilityTargetTimeMin <= 0)
|
||||||
|
throw new InvalidOperationException("脆碎试验时间必须大于0");
|
||||||
|
|
||||||
|
await WriteFriabilityTimeAsync(FriabilityTargetTimeMin);
|
||||||
|
|
||||||
|
double weightBefore = await ReadFriabilityWeightAsync(ResolveFriabilityWeightBeforeRegister(), "脆碎前重量");
|
||||||
SetFriabilityWeightFromPlc(weightBefore: weightBefore);
|
SetFriabilityWeightFromPlc(weightBefore: weightBefore);
|
||||||
if (WeightBefore <= 0)
|
if (WeightBefore <= 0)
|
||||||
throw new InvalidOperationException("脆碎前重量必须大于0");
|
throw new InvalidOperationException("脆碎前重量必须大于0");
|
||||||
|
|
||||||
int totalRounds = Math.Max(1, FriabilityTargetRounds);
|
UpdateFriabilityTimingFromTime();
|
||||||
double rpm = FriabilityTargetRpm > 0 ? FriabilityTargetRpm : 25;
|
double rpm = FriabilityTargetRpm > 0 ? FriabilityTargetRpm : 25;
|
||||||
FriabilityTargetTimeSec = (int)Math.Ceiling(totalRounds / rpm * 60);
|
double testTimeMin = FriabilityTargetTimeMin > 0 ? FriabilityTargetTimeMin : 4;
|
||||||
|
int totalRounds = Math.Max(1, FriabilityTargetRounds);
|
||||||
FriabilityRemainingRounds = totalRounds;
|
FriabilityRemainingRounds = totalRounds;
|
||||||
FriabilityCurrentRpm = rpm;
|
FriabilityCurrentRpm = rpm;
|
||||||
await PulseCoilAsync(startCoil);
|
await PulseCoilAsync(startCoil);
|
||||||
int durationMs = (int)((totalRounds / rpm) * 60 * 1000); // 总运行时间(毫秒)
|
int durationMs = (int)Math.Ceiling(testTimeMin * 60 * 1000); // 总运行时间(毫秒)
|
||||||
|
|
||||||
for (int i = 0; i < durationMs; i += 100)
|
for (int i = 0; i < durationMs; i += 100)
|
||||||
{
|
{
|
||||||
@@ -1322,7 +1437,7 @@ namespace TabletTester2025.ViewModels
|
|||||||
if (Phase != TestPhase.Running)
|
if (Phase != TestPhase.Running)
|
||||||
throw new InvalidOperationException("脆碎度测试已停止,未保存结果");
|
throw new InvalidOperationException("脆碎度测试已停止,未保存结果");
|
||||||
|
|
||||||
double weightAfter = await ReadFriabilityWeightAsync(_plcConfig.WeightAfter, "脆碎后重量");
|
double weightAfter = await ReadFriabilityWeightAsync(ResolveFriabilityWeightAfterRegister(), "脆碎后重量");
|
||||||
SetFriabilityWeightFromPlc(weightAfter: weightAfter);
|
SetFriabilityWeightFromPlc(weightAfter: weightAfter);
|
||||||
FriabilityCurrentRpm = rpm;
|
FriabilityCurrentRpm = rpm;
|
||||||
LossPercent = TestCalculationService.CalculateFriabilityLossPercent(WeightBefore, WeightAfter);
|
LossPercent = TestCalculationService.CalculateFriabilityLossPercent(WeightBefore, WeightAfter);
|
||||||
|
|||||||
@@ -197,6 +197,7 @@
|
|||||||
<DataGridTextColumn Header="样品名称" Binding="{Binding SampleName}" Width="120"/>
|
<DataGridTextColumn Header="样品名称" Binding="{Binding SampleName}" Width="120"/>
|
||||||
<DataGridTextColumn Header="失重率(%)" Binding="{Binding FriabilityLoss, StringFormat=F2}" Width="100"/>
|
<DataGridTextColumn Header="失重率(%)" Binding="{Binding FriabilityLoss, StringFormat=F2}" Width="100"/>
|
||||||
<DataGridTextColumn Header="设定转速(r/min)" Binding="{Binding FriabilityTargetRpm, StringFormat=F1}" Width="120"/>
|
<DataGridTextColumn Header="设定转速(r/min)" Binding="{Binding FriabilityTargetRpm, StringFormat=F1}" Width="120"/>
|
||||||
|
<DataGridTextColumn Header="试验时间(min)" Binding="{Binding FriabilityTargetTimeMin, StringFormat=F1}" Width="110"/>
|
||||||
<DataGridTextColumn Header="试验转数" Binding="{Binding FriabilityTargetRounds}" Width="80"/>
|
<DataGridTextColumn Header="试验转数" Binding="{Binding FriabilityTargetRounds}" Width="80"/>
|
||||||
<DataGridTextColumn Header="前重(g)" Binding="{Binding WeightBefore, StringFormat=F3}" Width="90"/>
|
<DataGridTextColumn Header="前重(g)" Binding="{Binding WeightBefore, StringFormat=F3}" Width="90"/>
|
||||||
<DataGridTextColumn Header="后重(g)" Binding="{Binding WeightAfter, StringFormat=F3}" Width="90"/>
|
<DataGridTextColumn Header="后重(g)" Binding="{Binding WeightAfter, StringFormat=F3}" Width="90"/>
|
||||||
|
|||||||
@@ -344,6 +344,10 @@
|
|||||||
|
|
||||||
<GroupBox Header="当前参数" Grid.Row="0">
|
<GroupBox Header="当前参数" Grid.Row="0">
|
||||||
<WrapPanel>
|
<WrapPanel>
|
||||||
|
<StackPanel Style="{StaticResource ParamRow}">
|
||||||
|
<TextBlock Text="试验时间(min):" Style="{StaticResource ParamLabel}"/>
|
||||||
|
<TextBlock Text="{Binding FriabilityTargetTimeMin, StringFormat=F1}" FontSize="18" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||||
|
</StackPanel>
|
||||||
<StackPanel Style="{StaticResource ParamRow}">
|
<StackPanel Style="{StaticResource ParamRow}">
|
||||||
<TextBlock Text="转速设置(r/min):" Style="{StaticResource ParamLabel}"/>
|
<TextBlock Text="转速设置(r/min):" Style="{StaticResource ParamLabel}"/>
|
||||||
<TextBlock Text="{Binding FriabilityTargetRpm, StringFormat=F1}" FontSize="18" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
<TextBlock Text="{Binding FriabilityTargetRpm, StringFormat=F1}" FontSize="18" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||||
@@ -363,14 +367,24 @@
|
|||||||
<UniformGrid Columns="3">
|
<UniformGrid Columns="3">
|
||||||
<Border Style="{StaticResource MetricCard}">
|
<Border Style="{StaticResource MetricCard}">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Text="初始重量(g)" Style="{StaticResource MetricLabel}"/>
|
<TextBlock Text="脆碎前质量(g)" Style="{StaticResource MetricLabel}"/>
|
||||||
<TextBlock Text="{Binding WeightBefore, StringFormat=F3}" Style="{StaticResource MetricValue}"/>
|
<TextBox Text="{Binding WeightBefore, Mode=TwoWay, UpdateSourceTrigger=LostFocus, StringFormat=F3}"
|
||||||
|
Width="150"
|
||||||
|
FontSize="22"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
HorizontalContentAlignment="Center"
|
||||||
|
Margin="0,8,0,0"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
<Border Style="{StaticResource MetricCard}">
|
<Border Style="{StaticResource MetricCard}">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Text="最终重量(g)" Style="{StaticResource MetricLabel}"/>
|
<TextBlock Text="脆碎后质量(g)" Style="{StaticResource MetricLabel}"/>
|
||||||
<TextBlock Text="{Binding WeightAfter, StringFormat=F3}" Style="{StaticResource MetricValue}"/>
|
<TextBox Text="{Binding WeightAfter, Mode=TwoWay, UpdateSourceTrigger=LostFocus, StringFormat=F3}"
|
||||||
|
Width="150"
|
||||||
|
FontSize="22"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
HorizontalContentAlignment="Center"
|
||||||
|
Margin="0,8,0,0"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
<Border Style="{StaticResource MetricCard}">
|
<Border Style="{StaticResource MetricCard}">
|
||||||
|
|||||||
@@ -97,18 +97,27 @@
|
|||||||
<WrapPanel>
|
<WrapPanel>
|
||||||
<StackPanel Style="{StaticResource ParamRow}">
|
<StackPanel Style="{StaticResource ParamRow}">
|
||||||
<TextBlock Text="转速(r/min):" Style="{StaticResource ParamLabel}"/>
|
<TextBlock Text="转速(r/min):" Style="{StaticResource ParamLabel}"/>
|
||||||
<TextBox x:Name="FriabilityRpmBox"/>
|
<TextBox x:Name="FriabilityRpmBox" TextChanged="FriabilityCalculationBox_TextChanged"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Style="{StaticResource ParamRow}">
|
<StackPanel Style="{StaticResource ParamRow}">
|
||||||
<TextBlock Text="试验转数:" Style="{StaticResource ParamLabel}"/>
|
<TextBlock Text="试验时间(min):" Style="{StaticResource ParamLabel}"/>
|
||||||
<TextBox x:Name="FriabilityRoundsBox" helpers:NumericInput.AllowDecimal="False"/>
|
<TextBox x:Name="FriabilityTimeBox" TextChanged="FriabilityCalculationBox_TextChanged"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Style="{StaticResource ParamRow}">
|
||||||
|
<TextBlock Text="试验转数(自动计算):" Style="{StaticResource ParamLabel}"/>
|
||||||
|
<TextBox x:Name="FriabilityRoundsBox"
|
||||||
|
helpers:NumericInput.AllowDecimal="False"
|
||||||
|
helpers:NumericInput.IsEnabled="False"
|
||||||
|
IsReadOnly="True"
|
||||||
|
Background="#F3F7FA"
|
||||||
|
Foreground="#526273"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Style="{StaticResource ParamRow}">
|
<StackPanel Style="{StaticResource ParamRow}">
|
||||||
<TextBlock Text="最大失重率(%):" Style="{StaticResource ParamLabel}"/>
|
<TextBlock Text="最大失重率(%):" Style="{StaticResource ParamLabel}"/>
|
||||||
<TextBox x:Name="FriabilityMaxLossBox"/>
|
<TextBox x:Name="FriabilityMaxLossBox"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</WrapPanel>
|
</WrapPanel>
|
||||||
<TextBlock Text="默认:转速25±1 r/min,转动100次,减失重量不得过1.0%,且不得检出断裂、龟裂或粉碎的片。"
|
<TextBlock Text="默认:转速25±1 r/min,试验时间4min,自动计算100转;减失重量不得过1.0%,且不得检出断裂、龟裂或粉碎的片。"
|
||||||
Style="{StaticResource StandardNote}"/>
|
Style="{StaticResource StandardNote}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</GroupBox>
|
</GroupBox>
|
||||||
|
|||||||
@@ -21,7 +21,10 @@ namespace TabletTester2025
|
|||||||
HardnessMaxBox.Text = p.HardnessMax_N.ToString();
|
HardnessMaxBox.Text = p.HardnessMax_N.ToString();
|
||||||
HardnessCountBox.Text = p.HardnessTestCount.ToString();
|
HardnessCountBox.Text = p.HardnessTestCount.ToString();
|
||||||
FriabilityRpmBox.Text = p.FriabilityTargetRpm.ToString();
|
FriabilityRpmBox.Text = p.FriabilityTargetRpm.ToString();
|
||||||
FriabilityRoundsBox.Text = p.FriabilityTargetRounds.ToString();
|
FriabilityTimeBox.Text = ResolveFriabilityTargetTimeMin(p).ToString("0.###");
|
||||||
|
FriabilityRoundsBox.Text = CalculateFriabilityRounds(
|
||||||
|
ResolveFriabilityTargetTimeMin(p),
|
||||||
|
p.FriabilityTargetRpm > 0 ? p.FriabilityTargetRpm : 25).ToString();
|
||||||
FriabilityMaxLossBox.Text = p.FriabilityMaxLossPercent.ToString();
|
FriabilityMaxLossBox.Text = p.FriabilityMaxLossPercent.ToString();
|
||||||
SelectDisintegrationDosageForm(p.DisintegrationDosageForm);
|
SelectDisintegrationDosageForm(p.DisintegrationDosageForm);
|
||||||
DisintegrationMaxSecBox.Text = p.DisintegrationMaxSeconds.ToString();
|
DisintegrationMaxSecBox.Text = p.DisintegrationMaxSeconds.ToString();
|
||||||
@@ -45,7 +48,8 @@ namespace TabletTester2025
|
|||||||
p.HardnessMax_N = double.Parse(HardnessMaxBox.Text);
|
p.HardnessMax_N = double.Parse(HardnessMaxBox.Text);
|
||||||
p.HardnessTestCount = int.Parse(HardnessCountBox.Text);
|
p.HardnessTestCount = int.Parse(HardnessCountBox.Text);
|
||||||
p.FriabilityTargetRpm = double.Parse(FriabilityRpmBox.Text);
|
p.FriabilityTargetRpm = double.Parse(FriabilityRpmBox.Text);
|
||||||
p.FriabilityTargetRounds = int.Parse(FriabilityRoundsBox.Text);
|
p.FriabilityTargetTimeMin = double.Parse(FriabilityTimeBox.Text);
|
||||||
|
p.FriabilityTargetRounds = CalculateFriabilityRounds(p.FriabilityTargetTimeMin, p.FriabilityTargetRpm);
|
||||||
p.FriabilityMaxLossPercent = double.Parse(FriabilityMaxLossBox.Text);
|
p.FriabilityMaxLossPercent = double.Parse(FriabilityMaxLossBox.Text);
|
||||||
p.DisintegrationDosageForm = GetSelectedDisintegrationDosageForm();
|
p.DisintegrationDosageForm = GetSelectedDisintegrationDosageForm();
|
||||||
p.DisintegrationMaxSeconds = int.Parse(DisintegrationMaxSecBox.Text);
|
p.DisintegrationMaxSeconds = int.Parse(DisintegrationMaxSecBox.Text);
|
||||||
@@ -87,6 +91,24 @@ namespace TabletTester2025
|
|||||||
DisintegrationMaxSecBox.Text = seconds;
|
DisintegrationMaxSecBox.Text = seconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void FriabilityCalculationBox_TextChanged(object sender, TextChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (FriabilityRoundsBox == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (double.TryParse(FriabilityTimeBox?.Text, out double timeMin)
|
||||||
|
&& double.TryParse(FriabilityRpmBox?.Text, out double rpm)
|
||||||
|
&& timeMin > 0
|
||||||
|
&& rpm > 0)
|
||||||
|
{
|
||||||
|
FriabilityRoundsBox.Text = CalculateFriabilityRounds(timeMin, rpm).ToString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FriabilityRoundsBox.Text = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void SelectDisintegrationDosageForm(string dosageForm)
|
private void SelectDisintegrationDosageForm(string dosageForm)
|
||||||
{
|
{
|
||||||
foreach (ComboBoxItem item in DisintegrationDosageFormBox.Items)
|
foreach (ComboBoxItem item in DisintegrationDosageFormBox.Items)
|
||||||
@@ -114,7 +136,7 @@ namespace TabletTester2025
|
|||||||
throw new InvalidOperationException("硬度内控上限必须大于下限。");
|
throw new InvalidOperationException("硬度内控上限必须大于下限。");
|
||||||
if (p.HardnessTestCount <= 0)
|
if (p.HardnessTestCount <= 0)
|
||||||
throw new InvalidOperationException("硬度测试次数必须大于0。");
|
throw new InvalidOperationException("硬度测试次数必须大于0。");
|
||||||
if (p.FriabilityTargetRpm <= 0 || p.FriabilityTargetRounds <= 0 || p.FriabilityMaxLossPercent <= 0)
|
if (p.FriabilityTargetRpm <= 0 || p.FriabilityTargetTimeMin <= 0 || p.FriabilityTargetRounds <= 0 || p.FriabilityMaxLossPercent <= 0)
|
||||||
throw new InvalidOperationException("脆碎度参数必须大于0。");
|
throw new InvalidOperationException("脆碎度参数必须大于0。");
|
||||||
if (p.DisintegrationMaxSeconds <= 0 || p.DisintegrationSpeedRpm <= 0 || p.DisintegrationTemperatureC <= 0)
|
if (p.DisintegrationMaxSeconds <= 0 || p.DisintegrationSpeedRpm <= 0 || p.DisintegrationTemperatureC <= 0)
|
||||||
throw new InvalidOperationException("崩解参数必须大于0。");
|
throw new InvalidOperationException("崩解参数必须大于0。");
|
||||||
@@ -127,5 +149,25 @@ namespace TabletTester2025
|
|||||||
if (p.DissolutionSampleTimes == null || p.DissolutionSampleTimes.Length == 0 || p.DissolutionSampleTimes.Any(t => t <= 0))
|
if (p.DissolutionSampleTimes == null || p.DissolutionSampleTimes.Length == 0 || p.DissolutionSampleTimes.Any(t => t <= 0))
|
||||||
throw new InvalidOperationException("溶出取样时间点必须为大于0的分钟数。");
|
throw new InvalidOperationException("溶出取样时间点必须为大于0的分钟数。");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static double ResolveFriabilityTargetTimeMin(PharmaParameters p)
|
||||||
|
{
|
||||||
|
if (p.FriabilityTargetTimeMin > 0)
|
||||||
|
return p.FriabilityTargetTimeMin;
|
||||||
|
|
||||||
|
double rpm = p.FriabilityTargetRpm > 0 ? p.FriabilityTargetRpm : 25;
|
||||||
|
if (p.FriabilityTargetRounds > 0 && rpm > 0)
|
||||||
|
return p.FriabilityTargetRounds / rpm;
|
||||||
|
|
||||||
|
return 4.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int CalculateFriabilityRounds(double timeMin, double rpm)
|
||||||
|
{
|
||||||
|
if (timeMin <= 0 || rpm <= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return Math.Max(1, (int)Math.Round(timeMin * rpm, MidpointRounding.AwayFromZero));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,9 @@
|
|||||||
"FriabilityStartCoilReset": 95, // 脆碎复位启动
|
"FriabilityStartCoilReset": 95, // 脆碎复位启动
|
||||||
|
|
||||||
|
|
||||||
|
"FriabilityTestTime": 410, // 脆碎试验时间(min)
|
||||||
|
"FriabilityWeightBefore": 412, // 脆碎前质量(g)
|
||||||
|
"FriabilityWeightAfter": 414, // 脆碎后质量(g)
|
||||||
"WeightBefore": 412,
|
"WeightBefore": 412,
|
||||||
"WeightAfter": 414,
|
"WeightAfter": 414,
|
||||||
"DisintegrationTemp": 1430,
|
"DisintegrationTemp": 1430,
|
||||||
@@ -64,6 +67,7 @@
|
|||||||
"HardnessMax_N": 60,
|
"HardnessMax_N": 60,
|
||||||
"HardnessTestCount": 6,
|
"HardnessTestCount": 6,
|
||||||
"FriabilityTargetRpm": 25.0,
|
"FriabilityTargetRpm": 25.0,
|
||||||
|
"FriabilityTargetTimeMin": 4.0,
|
||||||
"FriabilityTargetRounds": 100,
|
"FriabilityTargetRounds": 100,
|
||||||
"FriabilityMaxLossPercent": 1.0,
|
"FriabilityMaxLossPercent": 1.0,
|
||||||
"DisintegrationDosageForm": "普通片",
|
"DisintegrationDosageForm": "普通片",
|
||||||
|
|||||||
Reference in New Issue
Block a user