From 8ed011f91e003e1d710d90dc7fe23d6b4a834dd6 Mon Sep 17 00:00:00 2001 From: "GukSang.Jin" Date: Tue, 19 May 2026 18:44:56 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B02025?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Models/PlcConfiguration.cs | 2 + Services/PlcSimulator.cs | 10 +++-- ViewModels/StationViewModel.cs | 75 +++++++++++++++++++++------------- Views/SettingsWindow.xaml | 14 +++---- Views/SettingsWindow.xaml.cs | 44 ++++++++------------ Views/ShowData.xaml | 4 +- Views/ShowData.xaml.cs | 13 +++++- appsettings.json | 5 ++- 8 files changed, 93 insertions(+), 74 deletions(-) diff --git a/Models/PlcConfiguration.cs b/Models/PlcConfiguration.cs index d0861c3..cca8bf5 100644 --- a/Models/PlcConfiguration.cs +++ b/Models/PlcConfiguration.cs @@ -28,6 +28,8 @@ public ushort HardnessPressure { get; set; } // 脆碎度 public ushort FriabilityStartCoil { get; set; } + public ushort FriabilityRounds { get; set; } + public ushort FriabilityRoundsBox { get; set; } // 兼容旧配置字段 public ushort FriabilityTestTime { get; set; } public ushort FriabilityWeightBefore { get; set; } public ushort FriabilityWeightAfter { get; set; } diff --git a/Services/PlcSimulator.cs b/Services/PlcSimulator.cs index 6cb9217..e0a9665 100644 --- a/Services/PlcSimulator.cs +++ b/Services/PlcSimulator.cs @@ -18,7 +18,7 @@ namespace TabletTester2025.Services float value = startAddress switch { 100 => 40 + (float)_rand.NextDouble() * 20, // 硬度 40~60N - 410 => 4.0f, // 脆碎试验时间(min) + 410 => 100f, // 脆碎圈数 412 => 5.0f + (float)_rand.NextDouble() * 2, // 脆碎度前重 414 => 4.9f + (float)_rand.NextDouble() * 2, // 后重 300 => 37.0f, // 温度 @@ -34,8 +34,12 @@ namespace TabletTester2025.Services // 👇 新增 ReadIntAsync 的模拟实现 public Task ReadIntAsync(ushort startAddress) { - // 模拟整数返回,比如返回0-1000之间的随机数 - return Task.FromResult(_rand.Next(0, 1000)); + int value = startAddress switch + { + 410 => 100, // 脆碎圈数 + _ => _rand.Next(0, 1000) + }; + return Task.FromResult(value); } public Task WriteCoilAsync(ushort coilAddress, bool value) => Task.CompletedTask; diff --git a/ViewModels/StationViewModel.cs b/ViewModels/StationViewModel.cs index 84f8f16..830d527 100644 --- a/ViewModels/StationViewModel.cs +++ b/ViewModels/StationViewModel.cs @@ -33,7 +33,7 @@ namespace TabletTester2025.ViewModels private bool _isLoadingDissolution2SampleInterval; private bool _isLoadingDisintegrationTime; private bool _isLoadingDisintegrationSpeed; - private bool _isLoadingFriabilityTime; + private bool _isLoadingFriabilityRounds; private bool _isUpdatingFriabilityWeightFromPlc; private readonly List _dissolution1Times = new(); @@ -325,7 +325,7 @@ namespace TabletTester2025.ViewModels _isLoadingDissolution2Time = true; _isLoadingDissolution1SampleInterval = true; _isLoadingDissolution2SampleInterval = true; - _isLoadingFriabilityTime = true; + _isLoadingFriabilityRounds = true; try { HardnessInternalMin = p.HardnessMin_N; @@ -333,6 +333,7 @@ namespace TabletTester2025.ViewModels HardnessTestCount = Math.Max(1, p.HardnessTestCount); FriabilityTargetRpm = p.FriabilityTargetRpm > 0 ? p.FriabilityTargetRpm : 25; double defaultRounds = p.FriabilityTargetRounds > 0 ? p.FriabilityTargetRounds : 100; + FriabilityTargetRounds = Math.Max(1, (int)Math.Round(defaultRounds, MidpointRounding.AwayFromZero)); FriabilityTargetTimeMin = p.FriabilityTargetTimeMin > 0 ? p.FriabilityTargetTimeMin : defaultRounds / FriabilityTargetRpm; @@ -360,10 +361,10 @@ namespace TabletTester2025.ViewModels _isLoadingDissolution2Time = false; _isLoadingDissolution1SampleInterval = false; _isLoadingDissolution2SampleInterval = false; - _isLoadingFriabilityTime = false; + _isLoadingFriabilityRounds = false; } - _ = WriteFriabilityTimeAsync(FriabilityTargetTimeMin); + _ = WriteFriabilityRoundsAsync(FriabilityTargetRounds); } private void LoadPharmaDefaults() @@ -999,16 +1000,21 @@ namespace TabletTester2025.ViewModels partial void OnFriabilityTargetTimeMinChanged(double value) { UpdateFriabilityTimingFromTime(); - - if (_isLoadingFriabilityTime) - return; - - _ = WriteFriabilityTimeAsync(value); } partial void OnFriabilityTargetRpmChanged(double value) { - UpdateFriabilityTimingFromTime(); + } + + partial void OnFriabilityTargetRoundsChanged(int value) + { + if (value > 0 && Phase != TestPhase.Running) + FriabilityRemainingRounds = value; + + if (_isLoadingFriabilityRounds || value <= 0) + return; + + _ = WriteFriabilityRoundsAsync(value); } private void UpdateFriabilityTimingFromTime() @@ -1016,9 +1022,7 @@ namespace TabletTester2025.ViewModels 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; @@ -1026,41 +1030,48 @@ namespace TabletTester2025.ViewModels private async Task LoadFriabilitySettingsAsync() { - await LoadFriabilityTimeAsync(); + await LoadFriabilityRoundsAsync(); await LoadFriabilityWeightsAsync(); } - private async Task LoadFriabilityTimeAsync() + private async Task LoadFriabilityRoundsAsync() { - ushort registerAddress = ResolveFriabilityTestTimeRegister(); + ushort registerAddress = ResolveFriabilityRoundsRegister(); if (registerAddress == 0) return; try { - _isLoadingFriabilityTime = true; + _isLoadingFriabilityRounds = true; int value = await _plc.ReadIntAsync(registerAddress); if (value > 0) { - FriabilityTargetTimeMin = value; + ApplyFriabilityRoundsFromPlc(value); } else { - await WriteFriabilityTimeAsync(FriabilityTargetTimeMin); + await WriteFriabilityRoundsAsync(FriabilityTargetRounds); } } catch { - try { await WriteFriabilityTimeAsync(FriabilityTargetTimeMin); } + try { await WriteFriabilityRoundsAsync(FriabilityTargetRounds); } catch { } } finally { - _isLoadingFriabilityTime = false; - UpdateFriabilityTimingFromTime(); + _isLoadingFriabilityRounds = false; } } + private void ApplyFriabilityRoundsFromPlc(int rounds) + { + FriabilityTargetRounds = Math.Max(1, rounds); + + if (Phase != TestPhase.Running) + FriabilityRemainingRounds = FriabilityTargetRounds; + } + private async Task LoadFriabilityWeightsAsync() { try @@ -1100,22 +1111,28 @@ namespace TabletTester2025.ViewModels catch { } } - private async Task WriteFriabilityTimeAsync(double value) + private async Task WriteFriabilityRoundsAsync(int value) { - ushort registerAddress = ResolveFriabilityTestTimeRegister(); - if (registerAddress == 0 || !double.IsFinite(value) || value <= 0) + ushort registerAddress = ResolveFriabilityRoundsRegister(); + if (registerAddress == 0 || value <= 0) return; try { - await _plc.WriteRegisterAsync(registerAddress, ToPlcTimeRegisterValue(value)); + await _plc.WriteRegisterAsync(registerAddress, (ushort)Math.Clamp(value, 0, ushort.MaxValue)); } catch { } } - private ushort ResolveFriabilityTestTimeRegister() + private ushort ResolveFriabilityRoundsRegister() { - return _plcConfig.FriabilityTestTime; + if (_plcConfig.FriabilityRounds != 0) + return _plcConfig.FriabilityRounds; + + if (_plcConfig.FriabilityRoundsBox != 0) + return _plcConfig.FriabilityRoundsBox; + + return _plcConfig.FriabilityTestTime != 0 ? _plcConfig.FriabilityTestTime : (ushort)410; } private ushort ResolveFriabilityWeightBeforeRegister() @@ -1409,14 +1426,14 @@ namespace TabletTester2025.ViewModels if (!double.IsFinite(FriabilityTargetTimeMin) || FriabilityTargetTimeMin <= 0) throw new InvalidOperationException("脆碎试验时间必须大于0"); - await WriteFriabilityTimeAsync(FriabilityTargetTimeMin); + UpdateFriabilityTimingFromTime(); + await WriteFriabilityRoundsAsync(FriabilityTargetRounds); double weightBefore = await ReadFriabilityWeightAsync(ResolveFriabilityWeightBeforeRegister(), "脆碎前重量"); SetFriabilityWeightFromPlc(weightBefore: weightBefore); if (WeightBefore <= 0) throw new InvalidOperationException("脆碎前重量必须大于0"); - UpdateFriabilityTimingFromTime(); double rpm = FriabilityTargetRpm > 0 ? FriabilityTargetRpm : 25; double testTimeMin = FriabilityTargetTimeMin > 0 ? FriabilityTargetTimeMin : 4; int totalRounds = Math.Max(1, FriabilityTargetRounds); diff --git a/Views/SettingsWindow.xaml b/Views/SettingsWindow.xaml index cd4d31d..6894e0a 100644 --- a/Views/SettingsWindow.xaml +++ b/Views/SettingsWindow.xaml @@ -102,27 +102,23 @@ - + - + - + + helpers:NumericInput.AllowDecimal="False"/> - diff --git a/Views/SettingsWindow.xaml.cs b/Views/SettingsWindow.xaml.cs index 0b51ff9..7c81311 100644 --- a/Views/SettingsWindow.xaml.cs +++ b/Views/SettingsWindow.xaml.cs @@ -29,9 +29,7 @@ namespace TabletTester2025 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(); + FriabilityRoundsBox.Text = ResolveFriabilityTargetRounds(p).ToString(); FriabilityMaxLossBox.Text = p.FriabilityMaxLossPercent.ToString(); SelectDisintegrationDosageForm(p.DisintegrationDosageForm); DisintegrationMaxSecBox.Text = p.DisintegrationMaxSeconds.ToString(); @@ -60,7 +58,7 @@ namespace TabletTester2025 double hardnessPressure = ParseFiniteDouble(HardnessPressureBox.Text, "加压压力"); p.FriabilityTargetRpm = ParseFiniteDouble(FriabilityRpmBox.Text, "脆碎度转速"); p.FriabilityTargetTimeMin = ParseFiniteDouble(FriabilityTimeBox.Text, "脆碎度试验时间"); - p.FriabilityTargetRounds = CalculateFriabilityRounds(p.FriabilityTargetTimeMin, p.FriabilityTargetRpm); + p.FriabilityTargetRounds = ParsePositiveInt(FriabilityRoundsBox.Text, "脆碎圈数"); p.FriabilityMaxLossPercent = ParseFiniteDouble(FriabilityMaxLossBox.Text, "最大失重率"); p.DisintegrationDosageForm = GetSelectedDisintegrationDosageForm(); p.DisintegrationMaxSeconds = int.Parse(DisintegrationMaxSecBox.Text); @@ -104,26 +102,6 @@ namespace TabletTester2025 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) @@ -184,6 +162,16 @@ namespace TabletTester2025 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 async Task LoadHardnessPressureAsync() { ushort registerAddress = ResolveHardnessPressureRegister(); @@ -228,12 +216,12 @@ namespace TabletTester2025 return 4.0; } - private static int CalculateFriabilityRounds(double timeMin, double rpm) + private static int ResolveFriabilityTargetRounds(PharmaParameters p) { - if (timeMin <= 0 || rpm <= 0) - return 0; + if (p.FriabilityTargetRounds > 0) + return p.FriabilityTargetRounds; - return Math.Max(1, (int)Math.Round(timeMin * rpm, MidpointRounding.AwayFromZero)); + return 100; } } } diff --git a/Views/ShowData.xaml b/Views/ShowData.xaml index c57566e..4daf42c 100644 --- a/Views/ShowData.xaml +++ b/Views/ShowData.xaml @@ -88,8 +88,8 @@ - - + + diff --git a/Views/ShowData.xaml.cs b/Views/ShowData.xaml.cs index 6cacce0..38607a1 100644 --- a/Views/ShowData.xaml.cs +++ b/Views/ShowData.xaml.cs @@ -146,7 +146,7 @@ namespace 片剂四用仪.Views new PlcParamMapping("txt_HardnessMotorLimit", AddressOrDefault(plcConfig.HardnessLimit, 298), PlcParamType.Float), new PlcParamMapping("txt_HardnessDamageThreshold", AddressOrDefault(plcConfig.HardnessPoSun, 400), PlcParamType.Float), - new PlcParamMapping("txt_BrittlenessTestTime", AddressOrDefault(plcConfig.FriabilityTestTime, 410), PlcParamType.Int), + new PlcParamMapping("txt_FriabilityRounds", ResolveFriabilityRoundsRegister(plcConfig), PlcParamType.Int), new PlcParamMapping("txt_PreBrittlenessMass", ResolveWeightRegister(plcConfig.FriabilityWeightBefore, plcConfig.WeightBefore, 412), PlcParamType.Float), new PlcParamMapping("txt_PostBrittlenessMass", ResolveWeightRegister(plcConfig.FriabilityWeightAfter, plcConfig.WeightAfter, 414), PlcParamType.Float), new PlcParamMapping("txt_WeightLossRate", 416, PlcParamType.Int), @@ -175,6 +175,17 @@ namespace 片剂四用仪.Views return configuredAddress != 0 ? configuredAddress : fallbackAddress; } + private static ushort ResolveFriabilityRoundsRegister(PlcConfiguration plcConfig) + { + if (plcConfig.FriabilityRounds != 0) + return plcConfig.FriabilityRounds; + + if (plcConfig.FriabilityRoundsBox != 0) + return plcConfig.FriabilityRoundsBox; + + return AddressOrDefault(plcConfig.FriabilityTestTime, 410); + } + private static ushort ResolveWeightRegister(ushort primaryAddress, ushort compatibleAddress, ushort fallbackAddress) { if (primaryAddress != 0) diff --git a/appsettings.json b/appsettings.json index d04d812..0c2ddc8 100644 --- a/appsettings.json +++ b/appsettings.json @@ -26,7 +26,8 @@ "HardnessMax": 72, //最大力采集 "HardnessShishilizhi": 1314, //力显示 - "FriabilityRoundsBox": 410, //实验转速 + "FriabilityRounds": 410, // 脆碎圈数 + "FriabilityRoundsBox": 410, // 兼容旧字段:脆碎圈数 "DisintegrationSeconds": 420, //崩解时间 @@ -38,7 +39,7 @@ "FriabilityStartCoilReset": 95, // 脆碎复位启动 - "FriabilityTestTime": 410, // 脆碎试验时间(min) + "FriabilityTestTime": 0, // 脆碎试验时间由药典参数计算,D410用于脆碎圈数 "FriabilityWeightBefore": 412, // 脆碎前质量(g) "FriabilityWeightAfter": 414, // 脆碎后质量(g) "WeightBefore": 412,