feat(otopcua): DriverInstanceActor honors RediscoverPolicy (Never/Once/UntilStable) (follow-up B)

This commit is contained in:
Joseph Doherty
2026-06-26 12:32:28 -04:00
parent efbdaf853c
commit ce34816a50
2 changed files with 90 additions and 4 deletions
@@ -745,12 +745,19 @@ public sealed class DriverInstanceActor : ReceiveActor, IWithTimers
/// driver exposes <see cref="ITagDiscovery"/> (nothing to inject otherwise). Self-sends the first
/// <see cref="RediscoverTick"/> tagged with the current init generation so a tick that outlives a reconnect
/// is rejected by the generation guard in <see cref="HandleRediscoverAsync"/>.
/// <para>Generic by design: re-discovery runs for EVERY <see cref="ITagDiscovery"/> driver on each
/// (re)connect, bounded by stop-on-stable (the discovered-set signature repeats) + the attempt cap.
/// Narrowing this to opt-in for heavy network drivers (Galaxy / OpcUaClient) is a follow-up.</para></summary>
/// <para>Honours the driver's <see cref="ITagDiscovery.RediscoverPolicy"/>: <c>Never</c> opts out entirely
/// (no tick scheduled); <c>Once</c> runs a single pass (the loop stops after the first publish in
/// <see cref="HandleRediscoverAsync"/>); <c>UntilStable</c> retries each (re)connect, bounded by
/// stop-on-stable (the discovered-set signature repeats) + the attempt cap.</para></summary>
private void StartDiscovery()
{
if (_driver is not ITagDiscovery) return; // driver doesn't expose discovery — nothing to inject
if (_driver is not ITagDiscovery discovery) return; // driver doesn't expose discovery — nothing to inject
if (discovery.RediscoverPolicy == DiscoveryRediscoverPolicy.Never)
{
// Driver opts out of post-connect discovery — don't even schedule the first tick.
_log.Debug("DriverInstance {Id}: RediscoverPolicy=Never — skipping post-connect discovery", _driverInstanceId);
return;
}
Self.Tell(new RediscoverTick(_initGeneration, Attempt: 0, PreviousSignature: string.Empty));
}
@@ -798,6 +805,16 @@ public sealed class DriverInstanceActor : ReceiveActor, IWithTimers
Context.Parent.Tell(new DiscoveredNodesReady(_driverInstanceId, nodes));
// Honour the driver's re-discovery policy. A Once driver discovers synchronously in its single
// DiscoverAsync, so a single published pass is complete — do not schedule another tick. (Never never
// reaches here — StartDiscovery returns before the first tick.) UntilStable falls through to the
// stop-on-stable + attempt-cap logic below.
if (discovery.RediscoverPolicy == DiscoveryRediscoverPolicy.Once)
{
_log.Debug("DriverInstance {Id}: RediscoverPolicy=Once — single discovery pass, not scheduling another", _driverInstanceId);
return;
}
// Stop when the non-empty discovered SET has stabilised (its signature repeats), or the attempt cap
// is hit. Keep retrying while empty (a FixedTree cache may still be populating). First tick carries "".
var signature = string.Join('\u0001',