using System; using System.Linq; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using TabletTester2025.Models; namespace TabletTester2025 { public partial class SettingsWindow : Window { public SettingsWindow() { InitializeComponent(); LoadSettings(); Loaded += SettingsWindow_Loaded; } private async void SettingsWindow_Loaded(object sender, RoutedEventArgs e) { await LoadHardnessPressureAsync(); await LoadHardnessSpeedAsync(); await LoadHardnessDamageThresholdAsync(); // 脆碎度 await LoadPlcFloatToTextBoxAsync(ResolveFriabilityRpmRegister(), FriabilityRpmBox); await LoadPlcIntToTextBoxAsync(ResolveFriabilityRoundsRegister(), FriabilityRoundsBox); // 溶出度 await LoadPlcFloatToTextBoxAsync(ResolveDissolution1SpeedRegister(), Dissolution1SpeedBox); await LoadPlcFloatToTextBoxAsync(ResolveDissolution2SpeedRegister(), Dissolution2SpeedBox); await LoadPlcFloatToTextBoxAsync(ResolveDissolution1IntervalRegister(), Dissolution1IntervalBox); await LoadPlcFloatToTextBoxAsync(ResolveDissolution2IntervalRegister(), Dissolution2IntervalBox); } private void LoadSettings() { var p = App.CurrentPharmaParams; HardnessCountBox.Text = p.HardnessTestCount.ToString(); FriabilityRpmBox.Text = p.FriabilityTargetRpm.ToString(); //FriabilityTimeBox.Text = ResolveFriabilityTargetTimeMin(p).ToString("0.###"); FriabilityRoundsBox.Text = ResolveFriabilityTargetRounds(p).ToString(); FriabilityMaxLossBox.Text = p.FriabilityMaxLossPercent.ToString(); DisintegrationTimeMinBox.Text = ResolveDisintegrationTimeMin(p).ToString("0.###"); //DisintegrationTempBox.Text = p.DisintegrationTemperatureC.ToString(); //DissolutionTempBox.Text = p.DissolutionTemperatureC.ToString(); Dissolution1IntervalBox.Text = p.Dissolution1SampleIntervalMin.ToString(); Dissolution2IntervalBox.Text = p.Dissolution2SampleIntervalMin.ToString(); } private async void SaveButton_Click(object sender, RoutedEventArgs e) { try { var current = App.CurrentPharmaParams; var p = new PharmaParameters { StandardVersion = current.StandardVersion, HardnessMin_N = current.HardnessMin_N, HardnessMax_N = current.HardnessMax_N, FriabilityTargetTimeMin = current.FriabilityTargetTimeMin, DissolutionMinPercentAt30min = current.DissolutionMinPercentAt30min, DisintegrationDosageForm = current.DisintegrationDosageForm, DisintegrationSpeedRpm = current.DisintegrationSpeedRpm, DisintegrationTemperatureC = current.DisintegrationTemperatureC, DissolutionTemperatureC = current.DissolutionTemperatureC, Dissolution1TimeMin = current.Dissolution1TimeMin, Dissolution2TimeMin = current.Dissolution2TimeMin, DissolutionSampleTimes = current.DissolutionSampleTimes?.ToArray() ?? Array.Empty() }; p.HardnessTestCount = int.Parse(HardnessCountBox.Text); double hardnessPressure = ParseFiniteDouble(HardnessPressureBox.Text, "加压压力"); double hardnessSpeed = ParsePositiveDouble(HardnessSpeedBox.Text, "加压速度"); double hardnessDamageThreshold = ParsePositiveDouble(HardnessDamageThresholdBox.Text, "硬度破损判定"); double friabilityRpm = ParseFiniteDouble(FriabilityRpmBox.Text, "脆碎度转速"); p.FriabilityTargetRpm = friabilityRpm; //p.FriabilityTargetTimeMin = ParseFiniteDouble(FriabilityTimeBox.Text, "脆碎度试验时间"); p.FriabilityTargetRounds = ParsePositiveInt(FriabilityRoundsBox.Text, "脆碎圈数"); p.FriabilityMaxLossPercent = ParseFiniteDouble(FriabilityMaxLossBox.Text, "最大失重率"); double disintegrationTimeMin = ParsePositiveDouble(DisintegrationTimeMinBox.Text, "崩解时间"); p.DisintegrationMaxSeconds = ToDisintegrationSeconds(disintegrationTimeMin); //p.DisintegrationTemperatureC = ParseFiniteDouble(DisintegrationTempBox.Text, "崩解介质温度"); //p.DissolutionTemperatureC = ParseFiniteDouble(DissolutionTempBox.Text, "溶出介质温度"); double dissolution1Speed = ParsePositiveDouble(Dissolution1SpeedBox.Text, "溶出速度1"); double dissolution2Speed = ParsePositiveDouble(Dissolution2SpeedBox.Text, "溶出速度2"); p.Dissolution1SampleIntervalMin = ParsePositiveDouble(Dissolution1IntervalBox.Text, "溶出1取样间隔"); p.Dissolution2SampleIntervalMin = ParsePositiveDouble(Dissolution2IntervalBox.Text, "溶出2取样间隔"); ValidateParameters(p); await WriteHardnessPressureAsync(hardnessPressure); await WriteHardnessSpeedAsync(hardnessSpeed); await WriteHardnessDamageThresholdAsync(hardnessDamageThreshold); await WriteFriabilityRpmAsync(friabilityRpm); await WriteDisintegrationTimeAsync(disintegrationTimeMin); await WriteDissolution1SpeedAsync(dissolution1Speed); await WriteDissolution2SpeedAsync(dissolution2Speed); await WriteDissolution1IntervalAsync(p.Dissolution1SampleIntervalMin); await WriteDissolution2IntervalAsync(p.Dissolution2SampleIntervalMin); App.CurrentPharmaParams = p; App.SaveCurrentPharmaParameters(); MessageBox.Show("参数已保存并立即生效", "提示", MessageBoxButton.OK, MessageBoxImage.Information); DialogResult = true; Close(); } catch (Exception ex) { MessageBox.Show($"输入格式错误: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } private void CancelButton_Click(object sender, RoutedEventArgs e) { DialogResult = false; Close(); } private static void ValidateParameters(PharmaParameters p) { if (!double.IsFinite(p.HardnessMin_N) || !double.IsFinite(p.HardnessMax_N)) throw new InvalidOperationException("硬度内控参数必须为有效数字。"); if (p.HardnessMin_N < 0 || p.HardnessMax_N <= p.HardnessMin_N) throw new InvalidOperationException("硬度内控上限必须大于下限。"); if (p.HardnessTestCount <= 0) throw new InvalidOperationException("硬度测试次数必须大于0。"); if (!double.IsFinite(p.FriabilityTargetRpm) || !double.IsFinite(p.FriabilityTargetTimeMin) || !double.IsFinite(p.FriabilityMaxLossPercent)) throw new InvalidOperationException("脆碎度参数必须为有效数字。"); if (p.FriabilityTargetRpm <= 0 || p.FriabilityTargetTimeMin <= 0 || p.FriabilityTargetRounds <= 0 || p.FriabilityMaxLossPercent <= 0) throw new InvalidOperationException("脆碎度参数必须大于0。"); if (!double.IsFinite(p.DisintegrationSpeedRpm) || !double.IsFinite(p.DisintegrationTemperatureC)) throw new InvalidOperationException("崩解参数必须为有效数字。"); if (p.DisintegrationMaxSeconds <= 0 || p.DisintegrationSpeedRpm <= 0 || p.DisintegrationTemperatureC <= 0) throw new InvalidOperationException("崩解参数必须大于0。"); if (!double.IsFinite(p.DissolutionMinPercentAt30min) || !double.IsFinite(p.DissolutionTemperatureC) || !double.IsFinite(p.Dissolution1SampleIntervalMin) || !double.IsFinite(p.Dissolution2SampleIntervalMin)) throw new InvalidOperationException("溶出度参数必须为有效数字。"); if (p.DissolutionMinPercentAt30min < 0 || p.DissolutionMinPercentAt30min > 150) throw new InvalidOperationException("溶出度Q值应在0到150之间。"); if (p.DissolutionTemperatureC <= 0 || p.Dissolution1TimeMin <= 0 || p.Dissolution2TimeMin <= 0) throw new InvalidOperationException("溶出温度和运行时间必须大于0。"); if (p.Dissolution1SampleIntervalMin <= 0 || p.Dissolution2SampleIntervalMin <= 0) throw new InvalidOperationException("溶出取样间隔必须大于0。"); if (p.DissolutionSampleTimes == null || p.DissolutionSampleTimes.Length == 0 || p.DissolutionSampleTimes.Any(t => t <= 0)) throw new InvalidOperationException("溶出取样配置必须为有效的正数分钟值。"); } private static double ParseFiniteDouble(string text, string fieldName) { if (!double.TryParse(text, out double value) || !double.IsFinite(value)) throw new InvalidOperationException($"{fieldName}必须为有效数字。"); if (value < 0) throw new InvalidOperationException($"{fieldName}不能小于0。"); return value; } private static int ParsePositiveInt(string text, string fieldName) { if (!int.TryParse(text, out int value)) throw new InvalidOperationException($"{fieldName}必须为整数。"); if (value <= 0) throw new InvalidOperationException($"{fieldName}必须大于0。"); return value; } private static double ParsePositiveDouble(string text, string fieldName) { double value = ParseFiniteDouble(text, fieldName); if (value <= 0) throw new InvalidOperationException($"{fieldName}必须大于0。"); return value; } private async Task LoadHardnessPressureAsync() { ushort registerAddress = ResolveHardnessPressureRegister(); if (registerAddress == 0) return; try { float value = await App.PlcService.ReadFloatAsync(registerAddress); if (float.IsFinite(value)) HardnessPressureBox.Text = value.ToString("0.###"); } catch { HardnessPressureBox.Text = ""; } } private static async Task WriteHardnessPressureAsync(double value) { ushort registerAddress = ResolveHardnessPressureRegister(); if (registerAddress == 0) throw new InvalidOperationException("加压压力PLC寄存器地址未配置。"); await App.PlcService.WriteFloatAsync(registerAddress, (float)value); } private static ushort ResolveHardnessPressureRegister() { return App.PlcConfig.HardnessPressure != 0 ? App.PlcConfig.HardnessPressure : (ushort)1480; } private async Task LoadHardnessSpeedAsync() { ushort registerAddress = ResolveHardnessSpeedRegister(); if (registerAddress == 0) return; try { float value = await App.PlcService.ReadFloatAsync(registerAddress); if (float.IsFinite(value) && value > 0) HardnessSpeedBox.Text = value.ToString("0.###"); } catch { HardnessSpeedBox.Text = ""; } } private static async Task WriteHardnessSpeedAsync(double value) { ushort registerAddress = ResolveHardnessSpeedRegister(); if (registerAddress == 0) throw new InvalidOperationException("加压速度PLC寄存器地址未配置。"); await App.PlcService.WriteFloatAsync(registerAddress, (float)value); } private static ushort ResolveHardnessSpeedRegister() { return App.PlcConfig.HardnessSudu != 0 ? App.PlcConfig.HardnessSudu : (ushort)300; } private async Task LoadHardnessDamageThresholdAsync() { ushort registerAddress = ResolveHardnessDamageThresholdRegister(); if (registerAddress == 0) return; try { float value = await App.PlcService.ReadFloatAsync(registerAddress); if (float.IsFinite(value) && value >= 0) HardnessDamageThresholdBox.Text = value.ToString("0.###"); } catch { HardnessDamageThresholdBox.Text = ""; } } private static async Task WriteHardnessDamageThresholdAsync(double value) { ushort registerAddress = ResolveHardnessDamageThresholdRegister(); if (registerAddress == 0) throw new InvalidOperationException("硬度破损判定PLC寄存器地址未配置。"); await App.PlcService.WriteFloatAsync(registerAddress, (float)value); } private static ushort ResolveHardnessDamageThresholdRegister() { return App.PlcConfig.HardnessPoSun != 0 ? App.PlcConfig.HardnessPoSun : (ushort)400; } private async Task LoadPlcFloatToTextBoxAsync(ushort address, TextBox textBox) { if (address == 0) return; try { float value = await App.PlcService.ReadFloatAsync(address); if (float.IsFinite(value)) textBox.Text = value.ToString("0.###"); } catch { textBox.Text = ""; } } private async Task LoadPlcIntToTextBoxAsync(ushort address, TextBox textBox) { if (address == 0) return; try { int value = await App.PlcService.ReadIntAsync(address); if (value >= 0) textBox.Text = value.ToString(); } catch { textBox.Text = ""; } } private static async Task WriteFriabilityRpmAsync(double value) { ushort registerAddress = ResolveFriabilityRpmRegister(); if (registerAddress == 0) throw new InvalidOperationException("脆碎度转速PLC寄存器地址未配置。"); await App.PlcService.WriteFloatAsync(registerAddress, (float)value); } private static ushort ResolveFriabilityRpmRegister() { return App.PlcConfig.FriabilityRpm != 0 ? App.PlcConfig.FriabilityRpm : (ushort)320; } private static ushort ResolveFriabilityRoundsRegister() { if (App.PlcConfig.FriabilityRounds != 0) return App.PlcConfig.FriabilityRounds; if (App.PlcConfig.FriabilityRoundsBox != 0) return App.PlcConfig.FriabilityRoundsBox; return App.PlcConfig.FriabilityTestTime != 0 ? App.PlcConfig.FriabilityTestTime : (ushort)410; } private static ushort ResolveDisintegrationTimeRegister() { 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) { ushort registerAddress = ResolveDissolution1SpeedRegister(); if (registerAddress == 0) throw new InvalidOperationException("溶出速度1 PLC寄存器地址未配置。"); await App.PlcService.WriteFloatAsync(registerAddress, (float)value); } private static async Task WriteDissolution2SpeedAsync(double value) { ushort registerAddress = ResolveDissolution2SpeedRegister(); if (registerAddress == 0) throw new InvalidOperationException("溶出速度2 PLC寄存器地址未配置。"); 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() { return App.PlcConfig.Dissolution1Speed != 0 ? App.PlcConfig.Dissolution1Speed : (ushort)340; } private static ushort ResolveDissolution2SpeedRegister() { return App.PlcConfig.Dissolution2Speed != 0 ? App.PlcConfig.Dissolution2Speed : (ushort)350; } private static ushort ResolveDissolution1IntervalRegister() { return App.PlcConfig.Dissolution1SampleInterval != 0 ? App.PlcConfig.Dissolution1SampleInterval : (ushort)432; } private static ushort ResolveDissolution2IntervalRegister() { return App.PlcConfig.Dissolution2SampleInterval != 0 ? App.PlcConfig.Dissolution2SampleInterval : (ushort)442; } 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 ResolveFriabilityTargetRounds(PharmaParameters p) { if (p.FriabilityTargetRounds > 0) return p.FriabilityTargetRounds; 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)); } } }