refactor(otopcua): extract authored-only send helper + empty-authored dropped-path test (follow-up D)
This commit is contained in:
@@ -1250,16 +1250,26 @@ public sealed class DriverHostActor : ReceiveActor, IWithTimers
|
||||
// authored values once per cached driver per redeploy. Net effect: exactly ONE send per driver per pass.
|
||||
var cachedDriverIds = _discoveredByDriver.Keys.ToHashSet(StringComparer.Ordinal);
|
||||
|
||||
// One authored-only push (value refs + alarm refs from the maps built above), shared by the bulk loop AND
|
||||
// the dropped-driver fallback so the two CANNOT drift: the fallback's correctness depends on sending the
|
||||
// SAME payload the bulk loop would have, so it's a shared helper (structural), not a comment-maintained
|
||||
// invariant. An EMPTY set is valid — the child's Connected handler routes it to Unsubscribe (dropping a
|
||||
// stale handle) rather than a spurious subscribe. Returns the value-ref count for the bulk-loop log total.
|
||||
int SendAuthoredOnly(IActorRef actor, string driverId)
|
||||
{
|
||||
var refs = refsByDriver.TryGetValue(driverId, out var r) ? r : Array.Empty<string>();
|
||||
var alarmRefs = alarmRefsByDriver.TryGetValue(driverId, out var ar) ? ar : Array.Empty<string>();
|
||||
actor.Tell(new DriverInstanceActor.SetDesiredSubscriptions(refs, SubscriptionPublishingInterval, alarmRefs));
|
||||
return refs.Count;
|
||||
}
|
||||
|
||||
var total = 0;
|
||||
foreach (var (driverId, entry) in _children)
|
||||
{
|
||||
// Cached drivers are owned exclusively by the re-inject tail (one send each) — skip here. Non-cached
|
||||
// drivers keep the bulk authored-only send exactly as before.
|
||||
if (cachedDriverIds.Contains(driverId)) continue;
|
||||
var refs = refsByDriver.TryGetValue(driverId, out var r) ? r : Array.Empty<string>();
|
||||
var alarmRefs = alarmRefsByDriver.TryGetValue(driverId, out var ar) ? ar : Array.Empty<string>();
|
||||
entry.Actor.Tell(new DriverInstanceActor.SetDesiredSubscriptions(refs, SubscriptionPublishingInterval, alarmRefs));
|
||||
total += refs.Count;
|
||||
total += SendAuthoredOnly(entry.Actor, driverId);
|
||||
}
|
||||
|
||||
if (total > 0)
|
||||
@@ -1371,13 +1381,11 @@ public sealed class DriverHostActor : ReceiveActor, IWithTimers
|
||||
// set NOW (the SAME payload the bulk loop computes), so the authored tags subscribe in THIS pass.
|
||||
// (The TriggerRediscovery above handles the async FixedTree re-graft separately; this just keeps
|
||||
// the authored values live meanwhile.) Guarded on the child still existing — a driver removed by
|
||||
// ReconcileDrivers has no child and correctly gets no send.
|
||||
// ReconcileDrivers has no child and correctly gets no send. Shares SendAuthoredOnly with the bulk
|
||||
// loop so the payload can't drift; a ZERO-authored driver sends an empty set → Unsubscribe (drops
|
||||
// the stale FixedTree handle without a spurious subscribe).
|
||||
if (_children.TryGetValue(driverId, out var fallbackEntry))
|
||||
{
|
||||
var refs = refsByDriver.TryGetValue(driverId, out var r) ? r : Array.Empty<string>();
|
||||
var alarmRefs = alarmRefsByDriver.TryGetValue(driverId, out var ar) ? ar : Array.Empty<string>();
|
||||
fallbackEntry.Actor.Tell(new DriverInstanceActor.SetDesiredSubscriptions(refs, SubscriptionPublishingInterval, alarmRefs));
|
||||
}
|
||||
SendAuthoredOnly(fallbackEntry.Actor, driverId);
|
||||
continue;
|
||||
}
|
||||
ApplyDiscoveredPlansForDriver(driverId, plansByEquipment);
|
||||
|
||||
Reference in New Issue
Block a user