Merge branch 'master' into b626-opcuaclient
This commit is contained in:
@@ -211,6 +211,10 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit
|
||||
/// <inheritdoc />
|
||||
public async Task InitializeAsync(string driverConfigJson, CancellationToken cancellationToken)
|
||||
{
|
||||
// #630: InitializeAsync is reachable on a live driver (DriverInstanceActor.AdoptConfigDuringInit runs
|
||||
// it with no teardown first); `Session = session` below would otherwise orphan the previous session,
|
||||
// its keep-alive handler and any reconnect handler.
|
||||
await ReleaseSessionAsync(cancellationToken).ConfigureAwait(false);
|
||||
_health = new DriverHealth(DriverState.Initializing, null, null);
|
||||
try
|
||||
{
|
||||
@@ -775,32 +779,14 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit
|
||||
await InitializeAsync(driverConfigJson, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task ShutdownAsync(CancellationToken cancellationToken)
|
||||
/// <summary>
|
||||
/// Cancels any in-flight reconnect, detaches the keep-alive handler and closes + disposes the current
|
||||
/// session. The session part of <see cref="ShutdownAsync"/>; also run at the top of
|
||||
/// <see cref="InitializeAsync"/> so a re-initialise on a live driver (#630) does not orphan the
|
||||
/// previous session and its reconnect handler. Subscriptions are NOT touched here.
|
||||
/// </summary>
|
||||
private async Task ReleaseSessionAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
// Tear down remote subscriptions first — otherwise Session.Close will try and may fail
|
||||
// with BadSubscriptionIdInvalid noise in the upstream log. _subscriptions is cleared
|
||||
// whether or not the wire-side delete succeeds since the local handles are useless
|
||||
// after close anyway. Before deleting each subscription we detach the Notification
|
||||
// handlers we attached at subscribe time so the SDK's invocation list no longer
|
||||
// holds the driver instance through the closure.
|
||||
foreach (var rs in _subscriptions.Values)
|
||||
{
|
||||
DetachNotificationHandlers(rs.ItemHandlers);
|
||||
try { await rs.Subscription.DeleteAsync(silent: true, cancellationToken).ConfigureAwait(false); }
|
||||
catch { /* best-effort */ }
|
||||
}
|
||||
_subscriptions.Clear();
|
||||
|
||||
foreach (var ras in _alarmSubscriptions.Values)
|
||||
{
|
||||
try { ras.EventItem.Notification -= ras.Handler; }
|
||||
catch { /* best-effort */ }
|
||||
try { await ras.Subscription.DeleteAsync(silent: true, cancellationToken).ConfigureAwait(false); }
|
||||
catch { /* best-effort */ }
|
||||
}
|
||||
_alarmSubscriptions.Clear();
|
||||
|
||||
// Abort any in-flight reconnect attempts before touching the session — BeginReconnect's
|
||||
// retry loop holds a reference to the current session and would fight Session.CloseAsync
|
||||
// if left spinning. Take the handler under _probeLock so a keep-alive callback racing
|
||||
@@ -835,6 +821,35 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit
|
||||
try { if (sessionToClose is Session s) await s.CloseAsync(cancellationToken).ConfigureAwait(false); }
|
||||
catch { /* best-effort */ }
|
||||
try { sessionToClose?.Dispose(); } catch { }
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task ShutdownAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
// Tear down remote subscriptions first — otherwise Session.Close will try and may fail
|
||||
// with BadSubscriptionIdInvalid noise in the upstream log. _subscriptions is cleared
|
||||
// whether or not the wire-side delete succeeds since the local handles are useless
|
||||
// after close anyway. Before deleting each subscription we detach the Notification
|
||||
// handlers we attached at subscribe time so the SDK's invocation list no longer
|
||||
// holds the driver instance through the closure.
|
||||
foreach (var rs in _subscriptions.Values)
|
||||
{
|
||||
DetachNotificationHandlers(rs.ItemHandlers);
|
||||
try { await rs.Subscription.DeleteAsync(silent: true, cancellationToken).ConfigureAwait(false); }
|
||||
catch { /* best-effort */ }
|
||||
}
|
||||
_subscriptions.Clear();
|
||||
|
||||
foreach (var ras in _alarmSubscriptions.Values)
|
||||
{
|
||||
try { ras.EventItem.Notification -= ras.Handler; }
|
||||
catch { /* best-effort */ }
|
||||
try { await ras.Subscription.DeleteAsync(silent: true, cancellationToken).ConfigureAwait(false); }
|
||||
catch { /* best-effort */ }
|
||||
}
|
||||
_alarmSubscriptions.Clear();
|
||||
|
||||
await ReleaseSessionAsync(cancellationToken).ConfigureAwait(false);
|
||||
_namespaceMap = null;
|
||||
_connectedEndpointUrl = null;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user