From 175d0983d43c0835ebcb8ed505ec12086590b86d Mon Sep 17 00:00:00 2001 From: "GukSang.Jin" Date: Mon, 18 May 2026 09:47:22 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.xaml.cs | 4 + Models/PlcConfiguration.cs | 5 ++ Models/TestBatch.cs | 2 + ViewModels/StationViewModel.cs | 158 ++++++++++++++++++++++++++++++++- Views/MainWindow.xaml | 21 +++-- Views/ShowData.xaml | 10 ++- Views/ShowData.xaml.cs | 3 +- appsettings.json | 7 +- 8 files changed, 197 insertions(+), 13 deletions(-) diff --git a/App.xaml.cs b/App.xaml.cs index 675bba2..a9976f1 100644 --- a/App.xaml.cs +++ b/App.xaml.cs @@ -69,6 +69,8 @@ namespace TabletTester2025 DissolutionTargetRpm REAL, DissolutionRSquared REAL, DissolutionSampleInterval INTEGER, + Dissolution1SampleInterval REAL, + Dissolution2SampleInterval REAL, DissolutionUpDownFreq REAL, -- 综合 @@ -129,6 +131,8 @@ namespace TabletTester2025 ("TestBatches", "DissolutionTargetRpm", "REAL", "50"), ("TestBatches", "DissolutionRSquared", "REAL", "0"), ("TestBatches", "DissolutionSampleInterval", "INTEGER", "5"), + ("TestBatches", "Dissolution1SampleInterval", "REAL", "5"), + ("TestBatches", "Dissolution2SampleInterval", "REAL", "5"), ("TestBatches", "DissolutionUpDownFreq", "REAL", "32"), ("TestBatches", "HardnessRSD", "REAL", "0"), // 已存在但确保 ("TestBatches", "RemainingTubesAtEnd", "INTEGER", "0"), diff --git a/Models/PlcConfiguration.cs b/Models/PlcConfiguration.cs index 48debee..61c835d 100644 --- a/Models/PlcConfiguration.cs +++ b/Models/PlcConfiguration.cs @@ -31,6 +31,7 @@ public ushort DisintegrationMovingUpCoil { get; set; } public ushort DisintegrationStartCoil { get; set; } public ushort DisintegrationStopCoil { get; set; } + public ushort DisintegrationResetCoil { get; set; } public ushort DisintegrationSpeed { get; set; } public ushort DisintegrationTime { get; set; } public ushort[] DisintegrationCompleteCoils { get; set; } = new ushort[6]; @@ -43,11 +44,15 @@ public ushort DissolutionStartCoil { get; set; } public ushort Dissolution1StartCoil { get; set; } public ushort Dissolution1StopCoil { get; set; } + public ushort Dissolution1ResetCoil { get; set; } public ushort Dissolution1SampleAckCoil { get; set; } public ushort Dissolution2StartCoil { get; set; } public ushort Dissolution2StopCoil { get; set; } + public ushort Dissolution2ResetCoil { get; set; } public ushort Dissolution2SampleAckCoil { get; set; } public ushort Dissolution1Time { get; set; } public ushort Dissolution2Time { get; set; } + public ushort Dissolution1SampleInterval { get; set; } + public ushort Dissolution2SampleInterval { get; set; } } } diff --git a/Models/TestBatch.cs b/Models/TestBatch.cs index 519285b..abdfec6 100644 --- a/Models/TestBatch.cs +++ b/Models/TestBatch.cs @@ -37,6 +37,8 @@ namespace TabletTester2025.Models public double DissolutionTargetRpm { get; set; } public double DissolutionRSquared { get; set; } public int DissolutionSampleInterval { get; set; } + public double Dissolution1SampleInterval { get; set; } + public double Dissolution2SampleInterval { get; set; } public double DissolutionUpDownFreq { get; set; } // 综合 diff --git a/ViewModels/StationViewModel.cs b/ViewModels/StationViewModel.cs index 31feb70..7584917 100644 --- a/ViewModels/StationViewModel.cs +++ b/ViewModels/StationViewModel.cs @@ -26,6 +26,8 @@ namespace TabletTester2025.ViewModels private DispatcherTimer _disintegrationTimer; private bool _isLoadingDissolution1Time; private bool _isLoadingDissolution2Time; + private bool _isLoadingDissolution1SampleInterval; + private bool _isLoadingDissolution2SampleInterval; private bool _isLoadingDisintegrationTime; private bool _isLoadingDisintegrationSpeed; @@ -41,6 +43,7 @@ namespace TabletTester2025.ViewModels private bool _dissolution2SampleRequestActive; private bool _isDissolution1SamplePromptOpen; private bool _isDissolution2SamplePromptOpen; + private bool _discardDisintegrationResult; private string _dissolutionResultChannel = ""; private double _dissolutionResultRate30Min; private double _dissolutionResultRSquared; @@ -110,6 +113,8 @@ namespace TabletTester2025.ViewModels [ObservableProperty] private double _dissolutionTargetRpm = 50; [ObservableProperty] private int _dissolution1TimeMin = 30; [ObservableProperty] private int _dissolution2TimeMin = 30; + [ObservableProperty] private double _dissolution1SampleIntervalMin = 5; + [ObservableProperty] private double _dissolution2SampleIntervalMin = 5; [ObservableProperty] private double _dissolutionElapsedTime; [ObservableProperty] private double _dissolutionCountdown; [ObservableProperty] private double _dissolutionRSquared; @@ -123,14 +128,17 @@ namespace TabletTester2025.ViewModels public IAsyncRelayCommand PrintDissolutionCommand { get; } public IAsyncRelayCommand StartDissolution1Command { get; } public IAsyncRelayCommand StopDissolution1Command { get; } + public IAsyncRelayCommand ResetDissolution1Command { get; } public IAsyncRelayCommand StartDissolution2Command { get; } public IAsyncRelayCommand StopDissolution2Command { get; } + public IAsyncRelayCommand ResetDissolution2Command { get; } // 崩解新增 [ObservableProperty] private double _disintegrationTargetFreq = 31; [ObservableProperty] private double _disintegrationSpeedRpm = 31; [ObservableProperty] private double _disintegrationTimeMin = 15; public IAsyncRelayCommand StopDisintegrationCommand { get; } + public IAsyncRelayCommand ResetDisintegrationCommand { get; } public IAsyncRelayCommand PrintDisintegrationCommand { get; } public PlotModel DissolutionPlotModel { get; } @@ -254,13 +262,16 @@ namespace TabletTester2025.ViewModels DissolutionDownCommand = new AsyncRelayCommand(async () => await _plc.WriteCoilAsync(0x23, true)); StartDissolution1Command = new AsyncRelayCommand(StartDissolution1Async); StopDissolution1Command = new AsyncRelayCommand(StopDissolution1Async); + ResetDissolution1Command = new AsyncRelayCommand(ResetDissolution1Async); StartDissolution2Command = new AsyncRelayCommand(StartDissolution2Async); StopDissolution2Command = new AsyncRelayCommand(StopDissolution2Async); + ResetDissolution2Command = new AsyncRelayCommand(ResetDissolution2Async); StopDissolutionCommand = new AsyncRelayCommand(StopDissolution1Async); PrintDissolutionCommand = new AsyncRelayCommand(async () => await PrintReport("溶出度")); // 崩解命令 StopDisintegrationCommand = new AsyncRelayCommand(StopDisintegrationAsync); + ResetDisintegrationCommand = new AsyncRelayCommand(ResetDisintegrationAsync); PrintDisintegrationCommand = new AsyncRelayCommand(async () => await PrintReport("崩解")); _ = LoadDisintegrationTimeAsync(); @@ -559,6 +570,23 @@ namespace TabletTester2025.ViewModels _ = WriteDissolutionTimeAsync(_plcConfig.Dissolution2Time, value); } + partial void OnDissolution1SampleIntervalMinChanged(double value) + { + if (_isLoadingDissolution1SampleInterval || _plcConfig.Dissolution1SampleInterval == 0 || value <= 0) + return; + + _ = WriteDissolutionFloatAsync(_plcConfig.Dissolution1SampleInterval, value); + DissolutionSampleInterval = ToCompatibleSampleInterval(value); + } + + partial void OnDissolution2SampleIntervalMinChanged(double value) + { + if (_isLoadingDissolution2SampleInterval || _plcConfig.Dissolution2SampleInterval == 0 || value <= 0) + return; + + _ = WriteDissolutionFloatAsync(_plcConfig.Dissolution2SampleInterval, value); + } + private async Task LoadDissolutionTimesAsync() { if (_plcConfig.Dissolution1Time != 0) @@ -592,6 +620,8 @@ namespace TabletTester2025.ViewModels _isLoadingDissolution2Time = false; } } + + await LoadDissolutionSampleIntervalsAsync(); } private async Task WriteDissolutionTimeAsync(ushort registerAddress, int value) @@ -606,6 +636,64 @@ namespace TabletTester2025.ViewModels catch { } } + private async Task LoadDissolutionSampleIntervalsAsync() + { + if (_plcConfig.Dissolution1SampleInterval != 0) + { + try + { + _isLoadingDissolution1SampleInterval = true; + float value = await _plc.ReadFloatAsync(_plcConfig.Dissolution1SampleInterval); + if (float.IsFinite(value) && value > 0) + { + Dissolution1SampleIntervalMin = value; + DissolutionSampleInterval = ToCompatibleSampleInterval(value); + } + } + catch { } + finally + { + _isLoadingDissolution1SampleInterval = false; + } + } + + if (_plcConfig.Dissolution2SampleInterval != 0) + { + try + { + _isLoadingDissolution2SampleInterval = true; + float value = await _plc.ReadFloatAsync(_plcConfig.Dissolution2SampleInterval); + if (float.IsFinite(value) && value > 0) + Dissolution2SampleIntervalMin = value; + } + catch { } + finally + { + _isLoadingDissolution2SampleInterval = false; + } + } + } + + private async Task WriteDissolutionFloatAsync(ushort registerAddress, double value) + { + if (registerAddress == 0 || value <= 0 || !double.IsFinite(value)) + return; + + try + { + await _plc.WriteFloatAsync(registerAddress, (float)value); + } + catch { } + } + + private static int ToCompatibleSampleInterval(double value) + { + if (!double.IsFinite(value) || value <= 0) + return 0; + + return (int)Math.Min(int.MaxValue, Math.Round(value, MidpointRounding.AwayFromZero)); + } + partial void OnDisintegrationTimeMinChanged(double value) { if (_isLoadingDisintegrationTime || _plcConfig.DisintegrationTime == 0 || value <= 0) @@ -885,9 +973,12 @@ namespace TabletTester2025.ViewModels int maxSec = DisintegrationTimeMin > 0 ? (int)Math.Ceiling(DisintegrationTimeMin * 60) : App.CurrentPharmaParams.DisintegrationMaxSeconds; - DisintegrationPass = (RemainingTubes == 0 && DisintegrationSeconds <= maxSec); + DisintegrationPass = !_discardDisintegrationResult && RemainingTubes == 0 && DisintegrationSeconds <= maxSec; - await SaveBatchResult(); + if (!_discardDisintegrationResult) + await SaveBatchResult(); + + _discardDisintegrationResult = false; } } @@ -904,6 +995,29 @@ namespace TabletTester2025.ViewModels } } + private async Task ResetDisintegrationAsync() + { + bool wasRunning = CurrentTest == TestType.Disintegration && Phase == TestPhase.Running; + _discardDisintegrationResult = wasRunning; + + try + { + await PulseCoilAsync(_plcConfig.DisintegrationResetCoil); + } + finally + { + _disintegrationTimer?.Stop(); + TubesCompleted = new bool[6]; + RemainingTubes = 6; + DisintegrationSeconds = 0; + DisintegrationPass = false; + Phase = TestPhase.Idle; + + if (!wasRunning) + _discardDisintegrationResult = false; + } + } + private async Task StartDissolution1Async() { CurrentTest = TestType.Dissolution; @@ -915,6 +1029,7 @@ namespace TabletTester2025.ViewModels _isDissolution1Running = true; DissolutionPlotModel.Title = "溶出曲线"; await WriteDissolutionTimeAsync(_plcConfig.Dissolution1Time, Dissolution1TimeMin); + await WriteDissolutionFloatAsync(_plcConfig.Dissolution1SampleInterval, Dissolution1SampleIntervalMin); await PulseCoilAsync(_plcConfig.Dissolution1StartCoil); } @@ -934,6 +1049,22 @@ namespace TabletTester2025.ViewModels } } + private async Task ResetDissolution1Async() + { + try + { + await PulseCoilAsync(_plcConfig.Dissolution1ResetCoil); + } + finally + { + _isDissolution1Running = false; + ResetDissolutionChannel(1); + ResetDissolutionSampleState(1); + Phase = _isDissolution2Running ? TestPhase.Running : TestPhase.Idle; + UpdateDissolutionClock(); + } + } + private async Task StartDissolution2Async() { if (_plcConfig.Dissolution2Percent == 0) @@ -952,6 +1083,7 @@ namespace TabletTester2025.ViewModels _isDissolution2Running = true; DissolutionPlotModel.Title = "溶出曲线"; await WriteDissolutionTimeAsync(_plcConfig.Dissolution2Time, Dissolution2TimeMin); + await WriteDissolutionFloatAsync(_plcConfig.Dissolution2SampleInterval, Dissolution2SampleIntervalMin); await PulseCoilAsync(_plcConfig.Dissolution2StartCoil); } @@ -971,6 +1103,22 @@ namespace TabletTester2025.ViewModels } } + private async Task ResetDissolution2Async() + { + try + { + await PulseCoilAsync(_plcConfig.Dissolution2ResetCoil); + } + finally + { + _isDissolution2Running = false; + ResetDissolutionChannel(2); + ResetDissolutionSampleState(2); + Phase = _isDissolution1Running ? TestPhase.Running : TestPhase.Idle; + UpdateDissolutionClock(); + } + } + private void ResetDissolutionChannel(int channel) { if (channel == 1) @@ -1096,7 +1244,11 @@ namespace TabletTester2025.ViewModels DissolutionRate30Min = dissolutionRate30Min, DissolutionTargetRpm = DissolutionTargetRpm, DissolutionRSquared = rsquared, - DissolutionSampleInterval = DissolutionSampleInterval, + DissolutionSampleInterval = CurrentTest == TestType.Dissolution && _dissolutionResultChannel == "溶出2" + ? ToCompatibleSampleInterval(Dissolution2SampleIntervalMin) + : ToCompatibleSampleInterval(Dissolution1SampleIntervalMin), + Dissolution1SampleInterval = Dissolution1SampleIntervalMin, + Dissolution2SampleInterval = Dissolution2SampleIntervalMin, DissolutionUpDownFreq = DissolutionUpDownFreq, HardnessPass = HardnessPass, FriabilityPass = FriabilityPass, diff --git a/Views/MainWindow.xaml b/Views/MainWindow.xaml index df66afd..ff8f915 100644 --- a/Views/MainWindow.xaml +++ b/Views/MainWindow.xaml @@ -384,8 +384,8 @@ -