Auto: twincat-2.2 — handle-based access with caching

Closes #311
This commit is contained in:
Joseph Doherty
2026-04-25 22:03:20 -04:00
parent 4a071b6d5a
commit b67eb6c8d0
8 changed files with 687 additions and 15 deletions

View File

@@ -77,6 +77,14 @@ otopcua-twincat-cli read -n 192.168.1.40.1.1 -s "Recipe[3]" -t Real
otopcua-twincat-cli read -n 192.168.1.40.1.1 -s GVL.sMessage -t WString
```
ADS variable handles for `read` / `write` symbols are cached transparently
inside the CLI's underlying `AdsTwinCATClient`. The first read of a symbol
resolves a handle; repeats reuse the cached handle for smaller AMS payloads
and skipped name resolution. The cache wipes on reconnect, on
`DeviceSymbolVersionInvalid` (with a one-shot retry), and on CLI exit. See
`docs/drivers/TwinCAT-Test-Fixture.md §Handle caching` for the full story
including the staleness caveat after an online change.
### `write`
```powershell
@@ -99,3 +107,7 @@ otopcua-twincat-cli subscribe -n 192.168.1.40.1.1 -s GVL.Counter -t DInt -i 500
The subscribe banner announces which mechanism is in play — "ADS notification"
or "polling" — so it's obvious in screen-recorded bug reports.
`--poll-only` polls go through the same cached-handle path as `read`, so
repeated polls of the same symbol carry only a 4-byte handle on the wire
rather than the full symbolic path.

View File

@@ -154,6 +154,36 @@ The required fixture state (1000-DINT GVL + churn POU) is documented in
`TwinCatProject/README.md §Performance scenarios`; XAE-form sources land at
`TwinCatProject/PLC/GVLs/GVL_Perf.TcGVL` + `TwinCatProject/PLC/POUs/FB_PerfChurn.TcPOU`.
### Handle caching (PR 2.2)
Per-tag reads / writes route through an in-process ADS variable-handle cache.
The first read of a symbol resolves a handle via `CreateVariableHandleAsync`;
subsequent reads / writes of the same symbol issue against the cached handle.
On the wire this trades a multi-byte symbolic path (`GVL_Perf.aTags[742]` =
20+ bytes) for a 4-byte handle, and the device server skips name resolution
on every subsequent op. Cache lifetime is process-scoped; entries are evicted
on `AdsErrorCode.DeviceSymbolVersionInvalid` (with one retry against a fresh
handle), wiped on reconnect (handles are per-AMS-session), and deleted
best-effort on driver disposal.
`TwinCATHandleCachePerfTests.Driver_handle_cache_avoids_repeat_symbol_resolution`
asserts the contract on real XAR by reading 50 symbols twice and verifying
the second pass issues zero new `CreateVariableHandleAsync` calls. It runs
under the standard `[TwinCATFact]` gate (XAR reachable; no `TWINCAT_PERF`
opt-in needed because 50 symbols is cheap).
**Staleness caveat**: handles can go stale after a TwinCAT online change
(POU edit + activate). Until PR 2.3 ships the proactive Symbol-Version
invalidation listener, the safety net is twofold: (1) the
`DeviceSymbolVersionInvalid` evict-and-retry path catches cases where the
descriptor moves but the symbol survives, and (2) operators can call
`ITwinCATClient.FlushOptionalCachesAsync` manually after a known online
change to wipe the cache without forcing a full reconnect. The bulk
Sum-read / Sum-write path remains on symbolic paths in PR 2.2 (the bulk
path's per-call symbol resolution is already amortised across N tags;
the perf delta vs. handle-batched bulk is marginal — tracked as a
follow-up for the Phase-2 perf sweep).
## Follow-up candidates
1. **XAR VM live-population** — scaffolding is in place (this PR); the