From 0f156e527c5215a065e9a6f970f537e87eec21cf Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Fri, 16 Oct 2026 18:28:32 +0700 Subject: [PATCH 1/5] M2.17-F: inline GOOSE subscribe/stop with exact wire GoCB fallback --- MainWindow.IedBrowser.cs | 52 +++++++++++++++++-- MainWindow.xaml | 12 ++++- Models/IedBrowserGooseInspection.cs | 15 ++++-- Models/IedBrowserPresentation.cs | 2 +- .../IedBrowserGooseInspectionTests.cs | 41 +++++++++++++++ 5 files changed, 112 insertions(+), 10 deletions(-) diff --git a/MainWindow.IedBrowser.cs b/MainWindow.IedBrowser.cs index 1c9648bf3..e8c822baf 100644 --- a/MainWindow.IedBrowser.cs +++ b/MainWindow.IedBrowser.cs @@ -472,10 +472,43 @@ await _runtime.ReadBrowserValuesAsync(device, targets, private void IedBrowserGooseSubscribe_Click(object sender, RoutedEventArgs e) { if (SelectedDevice is not { IsDemo: false } device || - _iedBrowserRenderedPage?.Kind != "GoCB") + _iedBrowserRenderedPage?.Kind != "GoCB" || GooseActionBusy) return; - // Shared Npcap-based subscriber, no second GOOSE receive/decoder path. - OpenIedGooseSubscriber(device); + // The Browser is an independent view of the SAME Npcap subscriber; + // never navigate to the Sniff tab or instantiate another receiver. + if (IsGooseCapturing) + { + UpdateIedBrowserGooseDetails(); + return; + } + if (GooseAdapters.Count == 0) + RefreshGooseAdapters(); + if (SelectedGooseAdapter is null) + { + var proposed = ResolveGooseAdapterForIed(device, out var route); + if (proposed is not null) + SelectedGooseAdapter = proposed; + else + IedBrowserDetailStatus.Text = + "Select the station capture adapter here, then press Subscribe GOOSE. " + route; + } + IedBrowserGooseAdapterCombo.Visibility = Visibility.Visible; + if (SelectedGooseAdapter is null) + { + IedBrowserGooseAdapterCombo.Focus(); + RefreshIedBrowserReadButtons(); + return; + } + // Existing start path owns decoder, bound model, lifetime and UI queue. + StartGooseSubscriber_Click(sender, e); + RefreshIedBrowserReadButtons(); + } + + private async void IedBrowserGooseStop_Click(object sender, RoutedEventArgs e) + { + if (!IsGooseCapturing || GooseActionBusy) return; + await StopGooseSubscriberAsync(); + RefreshIedBrowserReadButtons(); } private void RefreshIedBrowserReadButtons() @@ -499,7 +532,18 @@ private void RefreshIedBrowserReadButtons() IedBrowserGooseSubscribeButton.IsEnabled = device is { IsDemo: false } && _iedBrowserReadOperation == null; IedBrowserGooseSubscribeButton.Content = IsGooseCapturing - ? "View GOOSE Capture" : "Subscribe GOOSE"; + ? "View live GOOSE" : "Subscribe GOOSE"; + IedBrowserGooseSubscribeButton.IsEnabled = + device is { IsDemo: false } && !GooseActionBusy; + IedBrowserGooseStopButton.Visibility = + _iedBrowserRenderedPage?.Kind == "GoCB" && IsGooseCapturing + ? Visibility.Visible : Visibility.Collapsed; + IedBrowserGooseStopButton.IsEnabled = !GooseActionBusy; + IedBrowserGooseAdapterCombo.Visibility = + _iedBrowserRenderedPage?.Kind == "GoCB" && + (!IsGooseCapturing || GooseAdapters.Count > 0) + ? Visibility.Visible : Visibility.Collapsed; + IedBrowserGooseAdapterCombo.IsEnabled = !IsGooseCapturing && !GooseActionBusy; } IedBrowserReadPageButton.Content = selectedKind == IedBrowserReadSelectionKind.ReadOnlyRcb ? "Inspect RCB" diff --git a/MainWindow.xaml b/MainWindow.xaml index 2bcd87084..3ff2ab194 100644 --- a/MainWindow.xaml +++ b/MainWindow.xaml @@ -1991,10 +1991,20 @@ Style="{StaticResource SoftButton}" Margin="0,0,6,0" Click="IedBrowserReadValues_Click" ToolTip="Read model-proven IEC data attributes sequentially; progress appears on the IED card."/> + +