feat(twincat): opt-in ADS sum-read and /hosts diagnostics bag; refresh TwinCAT backlog (#645, #612)

Sum-read (#310): batches a poll's reads per device into sum commands capped at sumReadMaxItems,
per-item ADS errors mapped per tag, falls back to individual reads when the target refuses the
command. Diagnostics (#314): ADS state, app name, online-change count, task cycle/exec/jitter via
IDriverDiagnostics. Re-implemented against the current raw-byte read path (stranded auto/driver-gaps
commits predate it). Sum-write not ported. Unit tests only; live gate pending a route.

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
Joseph Doherty
2026-10-08 19:18:42 -04:00
parent 8801631739
commit af19db4ed1
15 changed files with 1055 additions and 48 deletions
@@ -61,6 +61,22 @@ public sealed class TwinCATDriverOptions
/// </summary>
public int NotificationMaxDelayMs { get; init; }
/// <summary>
/// When <c>true</c>, a poll's reads for one device are batched into ADS sum-read commands
/// (IndexGroup <c>0xF080</c>) of up to <see cref="SumReadMaxItems"/> symbols each instead of one
/// round-trip per tag (Gitea #645 / #310). Default <c>false</c> (opt-in): a TwinCAT 2 CE runtime's
/// sum-command support is unverified. A target that refuses the sum command is detected at runtime
/// and that device falls back to individual reads for the rest of the driver's life.
/// </summary>
public bool UseSumRead { get; init; }
/// <summary>
/// Maximum symbols per ADS sum-read command; larger polls are split into chunks of this size.
/// Bounds the request/response size against the target's ADS buffer. Values below 1 are treated as 1.
/// Only consulted when <see cref="UseSumRead"/> is <c>true</c>.
/// </summary>
public int SumReadMaxItems { get; init; } = 200;
/// <summary>
/// Timeout for the AdminUI Test Connect probe, in seconds. The AdminUI clamps to a
/// 60s server-side maximum; this default is what the form pre-fills for new instances.