Summary
When connecting via a configuration file (opcilloscope config.cfg or File → Open Config), the status-bar indicator briefly shows ● Connected, then flips to ○ Not Connected within ~1–2 seconds and stays there — while the session is actually healthy: subscriptions are live and monitored values keep updating.
Connecting to the same server via the Connect dialog does not exhibit this; the indicator stays ● Connected indefinitely (verified over several minutes).
This is not a Terminal.Gui 2.4.5 migration regression — it reproduces identically on main at d3ad3dd (Terminal.Gui 2.0.0). Discovered while capturing screenshots for #165.
Reproduction
- Start the in-repo test server:
dotnet run --project Tests/Opcilloscope.TestServer
- Create a config pointing at it with a few monitored nodes:
{
"version": "1.0",
"server": { "endpointUrl": "opc.tcp://localhost:4840/UA/OpcilloscopeTest", "securityMode": "None" },
"settings": { "publishingIntervalMs": 250, "samplingIntervalMs": 100 },
"monitoredNodes": [
{ "nodeId": "ns=1;s=SineWave", "displayName": "SineWave", "enabled": true },
{ "nodeId": "ns=1;s=Counter", "displayName": "Counter", "enabled": true }
]
}
dotnet run -- --insecure demo.cfg
- Watch the bottom-right indicator.
Observed on main (d3ad3dd), polling once per second after launch:
t=4s: ● Connected
t=5s: ○ Not Connected ← and it never recovers
...meanwhile the Monitored Variables table keeps updating with Good values
No WARN Connection lost or reconnect activity appears in the log — the session is fine; only the label is wrong.
Analysis
MainWindow.UpdateConnectionStatus latches whatever the last ConnectionManager.StateChanged event said. The suspect is a stale Disconnected event arriving after Connected:
ConnectionManager.OnClientDisconnected (ConnectionManager.cs ~line 384) forwards the client wrapper's Disconnected event as StateChanged(Disconnected) guarded only by the _isReconnecting flag — it does not check whether the client is currently connected.
- The config-load path tears down twice before connecting (
MainWindow.LoadConfigurationAsync → DisconnectAsync(), then ConnectionManager.ConnectAsync → internal DisconnectAsync()), and the old session's close completes asynchronously (ConfigureAwait(false) continuations off the UI thread). A Disconnected event from that teardown can land after the new session has already reported Connected.
- Each event is marshalled with
UiThread.Run, so out-of-order delivery latches the label at "Not Connected".
Possible fix directions:
- In
OnClientDisconnected, ignore the event when _client.IsConnected is true (or tie events to a session/connection generation counter so stale-session events are dropped).
- Alternatively, have
MainWindow.OnConnectionStateChanged re-check _connectionManager.IsConnected before painting Disconnected.
Impact
Cosmetic but misleading: users loading a config see "Not Connected" while data is streaming, which undermines trust in the indicator (and in genuine disconnect warnings).
Summary
When connecting via a configuration file (
opcilloscope config.cfgor File → Open Config), the status-bar indicator briefly shows● Connected, then flips to○ Not Connectedwithin ~1–2 seconds and stays there — while the session is actually healthy: subscriptions are live and monitored values keep updating.Connecting to the same server via the Connect dialog does not exhibit this; the indicator stays
● Connectedindefinitely (verified over several minutes).This is not a Terminal.Gui 2.4.5 migration regression — it reproduces identically on
mainatd3ad3dd(Terminal.Gui 2.0.0). Discovered while capturing screenshots for #165.Reproduction
dotnet run --project Tests/Opcilloscope.TestServer{ "version": "1.0", "server": { "endpointUrl": "opc.tcp://localhost:4840/UA/OpcilloscopeTest", "securityMode": "None" }, "settings": { "publishingIntervalMs": 250, "samplingIntervalMs": 100 }, "monitoredNodes": [ { "nodeId": "ns=1;s=SineWave", "displayName": "SineWave", "enabled": true }, { "nodeId": "ns=1;s=Counter", "displayName": "Counter", "enabled": true } ] }dotnet run -- --insecure demo.cfgObserved on
main(d3ad3dd), polling once per second after launch:No
WARN Connection lostor reconnect activity appears in the log — the session is fine; only the label is wrong.Analysis
MainWindow.UpdateConnectionStatuslatches whatever the lastConnectionManager.StateChangedevent said. The suspect is a staleDisconnectedevent arriving afterConnected:ConnectionManager.OnClientDisconnected(ConnectionManager.cs ~line 384) forwards the client wrapper'sDisconnectedevent asStateChanged(Disconnected)guarded only by the_isReconnectingflag — it does not check whether the client is currently connected.MainWindow.LoadConfigurationAsync→DisconnectAsync(), thenConnectionManager.ConnectAsync→ internalDisconnectAsync()), and the old session's close completes asynchronously (ConfigureAwait(false)continuations off the UI thread). ADisconnectedevent from that teardown can land after the new session has already reportedConnected.UiThread.Run, so out-of-order delivery latches the label at "Not Connected".Possible fix directions:
OnClientDisconnected, ignore the event when_client.IsConnectedis true (or tie events to a session/connection generation counter so stale-session events are dropped).MainWindow.OnConnectionStateChangedre-check_connectionManager.IsConnectedbefore paintingDisconnected.Impact
Cosmetic but misleading: users loading a config see "Not Connected" while data is streaming, which undermines trust in the indicator (and in genuine disconnect warnings).