feat(otopcua): re-run driver discovery on reconnect
This commit is contained in:
@@ -421,6 +421,7 @@ public sealed class DriverInstanceActor : ReceiveActor, IWithTimers
|
|||||||
ResubscribeDesired();
|
ResubscribeDesired();
|
||||||
AttachAlarmSource();
|
AttachAlarmSource();
|
||||||
SubscribeDesiredAlarms();
|
SubscribeDesiredAlarms();
|
||||||
|
StartDiscovery(); // re-run discovery on reconnect — keeps the injected tree fresh if the backend's capabilities changed
|
||||||
});
|
});
|
||||||
// A failure here is a no-op regardless of generation — the retry timer keeps trying the
|
// A failure here is a no-op regardless of generation — the retry timer keeps trying the
|
||||||
// current config; only a (generation-matched) InitializeSucceeded transitions state.
|
// current config; only a (generation-matched) InitializeSucceeded transitions state.
|
||||||
|
|||||||
+41
@@ -77,6 +77,47 @@ public sealed class DriverInstanceActorDiscoveryTests : RuntimeActorTestBase
|
|||||||
parent.ExpectNoMsg(TimeSpan.FromMilliseconds(300));
|
parent.ExpectNoMsg(TimeSpan.FromMilliseconds(300));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Discovery RE-RUNS on every return to Connected: after the initial discovery settles, a
|
||||||
|
/// <see cref="DriverInstanceActor.ForceReconnect"/> drives the actor through Reconnecting and
|
||||||
|
/// back to Connected (via the auto-retry timer, the same path the existing reconnect tests use),
|
||||||
|
/// and a fresh bounded discovery loop fires — keeping the injected tree current if the backend's
|
||||||
|
/// capabilities changed across the reconnect. The new init bumps the generation, so any
|
||||||
|
/// pre-reconnect tick is discarded by the generation guard (the initial loop has already settled
|
||||||
|
/// here, so none are in flight).
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public void Discovery_reruns_after_reconnect()
|
||||||
|
{
|
||||||
|
var driver = new DiscoverableStubDriver();
|
||||||
|
var parent = CreateTestProbe();
|
||||||
|
// Tiny reconnect + rediscover intervals so the whole reconnect-then-rediscover cycle runs fast.
|
||||||
|
var actor = parent.ChildActorOf(DriverInstanceActor.Props(
|
||||||
|
driver,
|
||||||
|
reconnectInterval: TimeSpan.FromMilliseconds(50),
|
||||||
|
rediscoverInterval: TimeSpan.FromMilliseconds(20)));
|
||||||
|
|
||||||
|
actor.Tell(new DriverInstanceActor.InitializeRequested("{}"));
|
||||||
|
|
||||||
|
// Drain the initial settling passes (0,0,3,3) and confirm the first loop stopped.
|
||||||
|
for (var i = 0; i < 4; i++)
|
||||||
|
parent.ExpectMsg<DriverInstanceActor.DiscoveredNodesReady>(TimeSpan.FromSeconds(2));
|
||||||
|
parent.ExpectNoMsg(TimeSpan.FromMilliseconds(200));
|
||||||
|
var passesBeforeReconnect = driver.DiscoverCount; // 4
|
||||||
|
|
||||||
|
// Force a reconnect: Connected → Reconnecting → (auto retry-connect) → Connected again.
|
||||||
|
actor.Tell(new DriverInstanceActor.ForceReconnect());
|
||||||
|
|
||||||
|
// A fresh discovery pass must arrive after the reconnect — the cache is warm now, so it sees
|
||||||
|
// the stable 3-node set immediately.
|
||||||
|
var afterReconnect = parent.ExpectMsg<DriverInstanceActor.DiscoveredNodesReady>(TimeSpan.FromSeconds(3));
|
||||||
|
afterReconnect.Nodes.Count.ShouldBe(3);
|
||||||
|
afterReconnect.DriverInstanceId.ShouldBe(driver.DriverInstanceId);
|
||||||
|
|
||||||
|
// The driver was discovered again — proves a fresh loop ran, not a replay of the old one.
|
||||||
|
driver.DiscoverCount.ShouldBeGreaterThan(passesBeforeReconnect);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A <see cref="StubDriver"/> that also exposes <see cref="ITagDiscovery"/>. Each <c>DiscoverAsync</c>
|
/// A <see cref="StubDriver"/> that also exposes <see cref="ITagDiscovery"/>. Each <c>DiscoverAsync</c>
|
||||||
/// pass is counted; passes 1–2 yield nothing (cache warming), passes 3+ yield a stable 3-node set —
|
/// pass is counted; passes 1–2 yield nothing (cache warming), passes 3+ yield a stable 3-node set —
|
||||||
|
|||||||
Reference in New Issue
Block a user