diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/GalaxyMxSession.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/GalaxyMxSession.cs
index d48d3528..4758b7db 100644
--- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/GalaxyMxSession.cs
+++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/GalaxyMxSession.cs
@@ -40,8 +40,12 @@ public sealed class GalaxyMxSession : IAsyncDisposable
_logger = logger ?? NullLogger.Instance;
}
- /// Gets a value indicating whether the session is connected.
- public bool IsConnected => _session is not null;
+ ///
+ /// Gets a value indicating whether a session is currently established. Set true on a
+ /// successful connect/attach and false after teardown/dispose — tracks the same
+ /// _connected flag that guards on, so the two never disagree.
+ ///
+ public bool IsConnected => _connected;
///
/// Server-side handle returned by MXAccess Register. Zero before
@@ -122,6 +126,7 @@ public sealed class GalaxyMxSession : IAsyncDisposable
ObjectDisposedException.ThrowIf(_disposed, this);
_session = session ?? throw new ArgumentNullException(nameof(session));
_serverHandle = serverHandle;
+ _connected = true; // an attached session means connected — keeps IsConnected + the ConnectAsync no-op guard consistent.
}
///
diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GalaxyMxSessionReconnectTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GalaxyMxSessionReconnectTests.cs
index b1e689eb..2eab49fe 100644
--- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GalaxyMxSessionReconnectTests.cs
+++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GalaxyMxSessionReconnectTests.cs
@@ -98,9 +98,38 @@ public sealed class GalaxyMxSessionReconnectTests
await Should.ThrowAsync(
async () => await session.ConnectAsync(null!, CancellationToken.None));
openCount.ShouldBe(1);
+ session.IsConnected.ShouldBeFalse(); // _connected must NOT be latched by the failed attempt.
// The failed first attempt must not have latched _connected — the retry reaches the body.
await session.ConnectAsync(null!, CancellationToken.None);
openCount.ShouldBe(2);
}
+
+ ///
+ /// tracks the _connected guard across the
+ /// full lifecycle: false when fresh, true after connect, still true after a recreate, and
+ /// false again after dispose.
+ ///
+ [Fact]
+ public async Task IsConnected_reflects_connect_recreate_and_dispose()
+ {
+ var session = NewSession();
+ var openCount = 0;
+ session.OpenAndRegisterOverrideForTests = _ =>
+ {
+ openCount++;
+ return Task.CompletedTask;
+ };
+
+ session.IsConnected.ShouldBeFalse();
+
+ await session.ConnectAsync(null!, CancellationToken.None);
+ session.IsConnected.ShouldBeTrue();
+
+ await session.RecreateAsync(null!, CancellationToken.None);
+ session.IsConnected.ShouldBeTrue();
+
+ await session.DisposeAsync();
+ session.IsConnected.ShouldBeFalse();
+ }
}