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(); } private void LoadSettings() { var p = App.CurrentPharmaParams; HardnessMinBox.Text = p.HardnessMin_N.ToString(); HardnessMaxBox.Text = p.HardnessMax_N.ToString(); HardnessCountBox.Text = p.HardnessTestCount.ToString(); FriabilityRpmBox.Text = p.FriabilityTargetRpm.ToString(); FriabilityTimeBox.Text = ResolveFriabilityTargetTimeMin(p).ToString("0.###"); FriabilityRoundsBox.Text = CalculateFriabilityRounds( ResolveFriabilityTargetTimeMin(p), p.FriabilityTargetRpm > 0 ? p.FriabilityTargetRpm : 25).ToString(); FriabilityMaxLossBox.Text = p.FriabilityMaxLossPercent.ToString(); SelectDisintegrationDosageForm(p.DisintegrationDosageForm); DisintegrationMaxSecBox.Text = p.DisintegrationMaxSeconds.ToString(); DisintegrationSpeedBox.Text = p.DisintegrationSpeedRpm.ToString(); DisintegrationTempBox.Text = p.DisintegrationTemperatureC.ToString(); DissolutionMinPercentBox.Text = p.DissolutionMinPercentAt30min.ToString(); DissolutionTempBox.Text = p.DissolutionTemperatureC.ToString(); Dissolution1TimeBox.Text = p.Dissolution1TimeMin.ToString(); Dissolution2TimeBox.Text = p.Dissolution2TimeMin.ToString(); Dissolution1IntervalBox.Text = p.Dissolution1SampleIntervalMin.ToString(); Dissolution2IntervalBox.Text = p.Dissolution2SampleIntervalMin.ToString(); SampleTimesBox.Text = string.Join(",", p.DissolutionSampleTimes ?? Array.Empty()); } private async void SaveButton_Click(object sender, RoutedEventArgs e) { try { var p = new PharmaParameters { StandardVersion = App.CurrentPharmaParams.StandardVersion }; p.HardnessMin_N = ParseFiniteDouble(HardnessMinBox.Text, "硬度内控下限"); p.HardnessMax_N = ParseFiniteDouble(HardnessMaxBox.Text, "硬度内控上限"); p.HardnessTestCount = int.Parse(HardnessCountBox.Text); double hardnessPressure = ParseFiniteDouble(HardnessPressureBox.Text, "加压压力"); p.FriabilityTargetRpm = ParseFiniteDouble(FriabilityRpmBox.Text, "脆碎度转速"); p.FriabilityTargetTimeMin = ParseFiniteDouble(FriabilityTimeBox.Text, "脆碎度试验时间"); p.FriabilityTargetRounds = CalculateFriabilityRounds(p.FriabilityTargetTimeMin, p.FriabilityTargetRpm); p.FriabilityMaxLossPercent = ParseFiniteDouble(FriabilityMaxLossBox.Text, "最大失重率"); p.DisintegrationDosageForm = GetSelectedDisintegrationDosageForm(); p.DisintegrationMaxSeconds = int.Parse(DisintegrationMaxSecBox.Text); p.DisintegrationSpeedRpm = ParseFiniteDouble(DisintegrationSpeedBox.Text, "崩解升降频率"); p.DisintegrationTemperatureC = ParseFiniteDouble(DisintegrationTempBox.Text, "崩解介质温度"); p.DissolutionMinPercentAt30min = ParseFiniteDouble(DissolutionMinPercentBox.Text, "30分钟最低溶出度"); p.DissolutionTemperatureC = ParseFiniteDouble(DissolutionTempBox.Text, "溶出介质温度"); p.Dissolution1TimeMin = int.Parse(Dissolution1TimeBox.Text); p.Dissolution2TimeMin = int.Parse(Dissolution2TimeBox.Text); p.Dissolution1SampleIntervalMin = ParseFiniteDouble(Dissolution1IntervalBox.Text, "溶出1取样间隔"); p.Dissolution2SampleIntervalMin = ParseFiniteDouble(Dissolution2IntervalBox.Text, "溶出2取样间隔"); p.DissolutionSampleTimes = SampleTimesBox.Text .Split(',', StringSplitOptions.RemoveEmptyEntries) .Select(s => int.Parse(s.Trim())) .ToArray(); ValidateParameters(p); await WriteHardnessPressureAsync(hardnessPressure); 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 void DisintegrationDosageFormBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (DisintegrationDosageFormBox.SelectedItem is ComboBoxItem item && item.Tag is string 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.IsFinite(timeMin) && double.TryParse(FriabilityRpmBox?.Text, out double rpm) && double.IsFinite(rpm) && timeMin > 0 && rpm > 0) { FriabilityRoundsBox.Text = CalculateFriabilityRounds(timeMin, rpm).ToString(); } else { FriabilityRoundsBox.Text = ""; } } 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) { 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("溶出取样时间点必须为大于0的分钟数。"); } 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 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 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)); } } }