fix(dcl): robust static-tag seeding — bounded-retry+log initial seed (#1) and re-seed on reconnect (#3)
STATIC tags (no further OnDataChange after advise) depend entirely on the seed read. Pre-fix HandleSubscribe seeded only on Success && Value != null, silently dropping a seed that raced the just-created advise (VT_EMPTY) — so a static tag stayed Uncertain forever while the source read Good. ReSubscribeAll did no seeding at all, so a static tag could not self-heal across reconnect. - New SeedTagsAsync helper: per-tag ReadAsync (not a bulk read — some gateways time out on large batches) with round-based bounded retry (SeedReadMaxAttempts/SeedReadRetryDelay), logging any tag that never yields a value (named — previously zero log trace). - HandleSubscribe seed loop delegates to SeedTagsAsync. - ReSubscribeAll re-seeds re-advised tags after reconnect via the generation-guarded TagValueReceived path (fan-out keys off _subscriptionsByInstance, preserved across reconnect). Diagnosed live on wonder-app-vd03 2026-06-17 (see scadabridge-dcl-static-tag-false-bad). Mechanism #2 (single transient-bad push) left as a follow-up.
This commit is contained in:
@@ -20,4 +20,24 @@ public class DataConnectionOptions
|
||||
/// disconnect toward the failover retry count (DataConnectionLayer-009).
|
||||
/// </summary>
|
||||
public TimeSpan StableConnectionThreshold { get; set; } = TimeSpan.FromSeconds(60);
|
||||
|
||||
/// <summary>
|
||||
/// DataConnectionLayer-027: total number of attempts the seed read makes per tag
|
||||
/// before giving up. The initial subscribe seed (and the reconnect re-seed) reads
|
||||
/// each just-advised tag to capture its current value; on a cold/fresh advise the
|
||||
/// read can race the subscription and return an empty/failed result. A STATIC tag
|
||||
/// (one that never fires another OnDataChange) would then stay Uncertain forever,
|
||||
/// because its bridge value depends entirely on that seed. Re-reading the still-empty
|
||||
/// subset a bounded number of times lets the advise warm up. Minimum 1 (1 = single
|
||||
/// read, no retry).
|
||||
/// </summary>
|
||||
public int SeedReadMaxAttempts { get; set; } = 3;
|
||||
|
||||
/// <summary>
|
||||
/// DataConnectionLayer-027: delay between seed-read attempts for the tags that have
|
||||
/// not yet returned a usable value. Applied once per round across the whole pending
|
||||
/// subset, so the total added latency is bounded to
|
||||
/// <c>(SeedReadMaxAttempts - 1) × SeedReadRetryDelay</c> regardless of tag count.
|
||||
/// </summary>
|
||||
public TimeSpan SeedReadRetryDelay { get; set; } = TimeSpan.FromMilliseconds(250);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user