diff --git a/Models/PlcConfiguration.cs b/Models/PlcConfiguration.cs index 74d3e0f..48debee 100644 --- a/Models/PlcConfiguration.cs +++ b/Models/PlcConfiguration.cs @@ -43,8 +43,10 @@ public ushort DissolutionStartCoil { get; set; } public ushort Dissolution1StartCoil { get; set; } public ushort Dissolution1StopCoil { get; set; } + public ushort Dissolution1SampleAckCoil { get; set; } public ushort Dissolution2StartCoil { get; set; } public ushort Dissolution2StopCoil { get; set; } + public ushort Dissolution2SampleAckCoil { get; set; } public ushort Dissolution1Time { get; set; } public ushort Dissolution2Time { get; set; } } diff --git a/ViewModels/StationViewModel.cs b/ViewModels/StationViewModel.cs index 419503f..31feb70 100644 --- a/ViewModels/StationViewModel.cs +++ b/ViewModels/StationViewModel.cs @@ -8,6 +8,8 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; using System.Windows.Threading; using TabletTester2025.Models; using TabletTester2025.Services; @@ -35,6 +37,10 @@ namespace TabletTester2025.ViewModels private DateTime _dissolution2StartTime = DateTime.MinValue; private bool _isDissolution1Running; private bool _isDissolution2Running; + private bool _dissolution1SampleRequestActive; + private bool _dissolution2SampleRequestActive; + private bool _isDissolution1SamplePromptOpen; + private bool _isDissolution2SamplePromptOpen; private string _dissolutionResultChannel = ""; private double _dissolutionResultRate30Min; private double _dissolutionResultRSquared; @@ -309,10 +315,16 @@ namespace TabletTester2025.ViewModels DisintegrationTemp = await _plc.ReadFloatAsync(_plcConfig.DisintegrationTemp); if (_isDissolution1Running) + { + await CheckDissolutionSampleAsync(1); updated |= await ReadDissolutionChannelAsync(1); + } if (_isDissolution2Running) + { + await CheckDissolutionSampleAsync(2); updated |= await ReadDissolutionChannelAsync(2); + } UpdateDissolutionClock(); @@ -320,6 +332,129 @@ namespace TabletTester2025.ViewModels DissolutionPlotModel.InvalidatePlot(true); } + private async Task CheckDissolutionSampleAsync(int channel) + { + ushort coilAddress = channel == 1 + ? _plcConfig.Dissolution1SampleAckCoil + : _plcConfig.Dissolution2SampleAckCoil; + + if (coilAddress == 0) + { + DissolutionCurveStatus = $"溶出{channel}取样确认线圈未配置"; + return; + } + + bool sampleConfirmed = await _plc.ReadCoilAsync(coilAddress); + if (sampleConfirmed) + { + SetDissolutionSampleRequestActive(channel, false); + return; + } + + if (IsDissolutionSampleRequestActive(channel) || IsDissolutionSamplePromptOpen(channel)) + return; + + SetDissolutionSampleRequestActive(channel, true); + SetDissolutionSamplePromptOpen(channel, true); + + try + { + await ShowDissolutionSampleDialogAsync(channel); + await _plc.WriteCoilAsync(coilAddress, true); + LocalAlarm = $"溶出{channel}已确认取样"; + DissolutionCurveStatus = ""; + } + catch (Exception ex) + { + SetDissolutionSampleRequestActive(channel, false); + await App.Current.Dispatcher.InvokeAsync(() => + MessageBox.Show($"溶出{channel}取样确认失败:{ex.Message}", "取样确认失败", MessageBoxButton.OK, MessageBoxImage.Error)); + } + finally + { + SetDissolutionSamplePromptOpen(channel, false); + } + } + + private bool IsDissolutionSampleRequestActive(int channel) + { + return channel == 1 ? _dissolution1SampleRequestActive : _dissolution2SampleRequestActive; + } + + private void SetDissolutionSampleRequestActive(int channel, bool value) + { + if (channel == 1) + _dissolution1SampleRequestActive = value; + else + _dissolution2SampleRequestActive = value; + } + + private bool IsDissolutionSamplePromptOpen(int channel) + { + return channel == 1 ? _isDissolution1SamplePromptOpen : _isDissolution2SamplePromptOpen; + } + + private void SetDissolutionSamplePromptOpen(int channel, bool value) + { + if (channel == 1) + _isDissolution1SamplePromptOpen = value; + else + _isDissolution2SamplePromptOpen = value; + } + + private async Task ShowDissolutionSampleDialogAsync(int channel) + { + bool confirmed = await App.Current.Dispatcher.InvokeAsync(() => + { + var dialog = new Window + { + Title = $"溶出{channel}取样", + Width = 420, + SizeToContent = SizeToContent.Height, + WindowStartupLocation = WindowStartupLocation.CenterOwner, + ResizeMode = ResizeMode.NoResize, + Background = Brushes.White, + Owner = Application.Current.MainWindow + }; + + var panel = new StackPanel { Margin = new Thickness(24) }; + panel.Children.Add(new TextBlock + { + Text = "请取样分析后,取样结束后点击“确定已取样”继续运行。", + FontSize = 16, + Foreground = new SolidColorBrush(Color.FromRgb(16, 42, 67)), + TextWrapping = TextWrapping.Wrap, + Margin = new Thickness(0, 0, 0, 18) + }); + + var button = new Button + { + Content = "确定已取样", + Width = 128, + Height = 38, + HorizontalAlignment = System.Windows.HorizontalAlignment.Right, + Background = new SolidColorBrush(Color.FromRgb(21, 101, 169)), + Foreground = Brushes.White, + BorderThickness = new Thickness(0), + FontSize = 15, + FontWeight = System.Windows.FontWeights.SemiBold, + Cursor = System.Windows.Input.Cursors.Hand + }; + button.Click += (_, _) => + { + dialog.DialogResult = true; + dialog.Close(); + }; + + panel.Children.Add(button); + dialog.Content = panel; + return dialog.ShowDialog() == true; + }); + + if (!confirmed) + throw new InvalidOperationException("取样确认窗口已关闭,未写入确认线圈"); + } + private async Task ReadDissolutionChannelAsync(int channel) { ushort registerAddress = channel == 1 @@ -775,6 +910,7 @@ namespace TabletTester2025.ViewModels Phase = TestPhase.Running; DissolutionPass = false; ResetDissolutionChannel(1); + ResetDissolutionSampleState(1); _dissolution1StartTime = DateTime.Now; _isDissolution1Running = true; DissolutionPlotModel.Title = "溶出曲线"; @@ -792,6 +928,7 @@ namespace TabletTester2025.ViewModels finally { _isDissolution1Running = false; + ResetDissolutionSampleState(1); Phase = _isDissolution2Running ? TestPhase.Running : TestPhase.Idle; UpdateDissolutionClock(); } @@ -810,6 +947,7 @@ namespace TabletTester2025.ViewModels Phase = TestPhase.Running; DissolutionPass = false; ResetDissolutionChannel(2); + ResetDissolutionSampleState(2); _dissolution2StartTime = DateTime.Now; _isDissolution2Running = true; DissolutionPlotModel.Title = "溶出曲线"; @@ -827,6 +965,7 @@ namespace TabletTester2025.ViewModels finally { _isDissolution2Running = false; + ResetDissolutionSampleState(2); Phase = _isDissolution1Running ? TestPhase.Running : TestPhase.Idle; UpdateDissolutionClock(); } @@ -857,6 +996,12 @@ namespace TabletTester2025.ViewModels DissolutionPlotModel.InvalidatePlot(true); } + private void ResetDissolutionSampleState(int channel) + { + SetDissolutionSampleRequestActive(channel, false); + SetDissolutionSamplePromptOpen(channel, false); + } + private async Task FinalizeDissolutionChannelAsync(int channel) { var times = channel == 1 ? _dissolution1Times : _dissolution2Times; diff --git a/appsettings.json b/appsettings.json index 5e35ffe..89af335 100644 --- a/appsettings.json +++ b/appsettings.json @@ -37,8 +37,10 @@ "DissolutionStartCoil": 40, "Dissolution1StartCoil": 40, "Dissolution1StopCoil": 43, + "Dissolution1SampleAckCoil": 44, "Dissolution2StartCoil": 30, "Dissolution2StopCoil": 33, + "Dissolution2SampleAckCoil": 34, "Dissolution1Time": 430, "Dissolution2Time": 440 },