docs: within-timestamp tie-cluster paging + AbCip/TwinCAT UDT member discovery

This commit is contained in:
Joseph Doherty
2026-06-17 20:35:34 -04:00
parent 4a7b0fde7b
commit 163f57b6bc
3 changed files with 106 additions and 11 deletions
+37
View File
@@ -119,6 +119,43 @@ integration fixture is down during normal dev (Docker host fixture currently off
See [Uns.md §Array tags](../Uns.md#array-tags-1-d) for the cross-driver coverage matrix
and the UI authoring flow.
## Controller-discovered UDT member variables
When `EnableControllerBrowse` is set, the driver walks the controller's `@tags` symbol table and
surfaces each UDT-typed tag's **atomic member leaves** as individually addressable OPC UA
Variables — in addition to the UDT container tag itself.
### What is discovered
- **Top-level UDT members** — the Template Object for each controller-browse UDT is fetched via
its template instance ID; each atomic member (BOOL, SINT, INT, DINT, REAL, …) becomes a
separate Variable node under the UDT's `Discovered/` sub-folder, addressable by its member
path (e.g. `MyUdt.Temperature`, `MyUdt.Flags`). Top-level atomic member discovery is
**functional in production**.
- **Nested struct members** — when a UDT member is itself a struct, the driver walks into it up
to a **depth cap of 8 levels**. Nested-struct expansion is a **documented deferral in
production**: the Template Object member block carries no nested template ID, so the
sub-shape cannot be re-fetched from the controller at discovery time. Nested struct leaves are
never mis-emitted — they are simply dropped (the parent member is omitted from discovery if it
is not atomic and its sub-shape is unavailable). Use pre-declared `Members` for nested structs
that must be individually addressed.
### Bare-container reads
Reading the UDT container tag itself (without a member suffix) returns
`BadNotSupported` — address the atomic member leaves instead.
### Member writes
UDT member **writes** discovered via controller browse are **deferred** in this release.
Pre-declared `Members` with `Writable: true` retain full read/write support as before.
### Configuration
No new configuration keys are required. Set `EnableControllerBrowse: true` in the
`AbCipDriverOptions` JSON to enable the `@tags` walk; UDT member expansion is automatic for all
UDT-typed tags found in the walk.
## Operational Notes
- **Native heap is invisible to the GC.** `GetMemoryFootprint()` reports CLR allocations only; libplctag's native `Tag` heap does not show up there. Watch whole-process RSS, and use `ReinitializeAsync` (tears down + re-creates every device's libplctag handles) as the remediation for native-heap growth.
+42
View File
@@ -154,6 +154,48 @@ on PLC re-download (unchanged semantics from scalar nodes).
See [Uns.md §Array tags](../Uns.md#array-tags-1-d) for the cross-driver coverage matrix
and the UI authoring flow.
## Controller-discovered struct/UDT/FB member variables
When `EnableControllerBrowse` is set, `BrowseSymbolsAsync` walks the device's symbol tree
recursively and expands **Struct / UDT / Function-Block typed symbols** into their **atomic
member leaves** as individually addressable OPC UA Variables, surfaced under the `Discovered/`
sub-folder.
### What is discovered
- Each symbol of a struct, UDT, or FB type is recursively expanded. The walk follows the ADS
symbol tree's `SubSymbols` collection.
- **Full instance paths** are preserved for every leaf (e.g. `MAIN.Motor.Speed`,
`GVL.Drive1.Status.Running`).
- The recursion is **depth-capped at 8 levels** to guard against pathological or circular-like
layouts.
- **Unsupported leaves** (types the driver cannot map to an OPC UA data type) are silently
dropped rather than surfaced as error nodes.
### Pre-declared Structure-typed tags
Pre-declared `Tags` with a `DataType` of `Structure` are **still rejected** — the driver cannot
read an opaque struct blob without knowing its member layout. For structs that must be
individually addressed, use `EnableControllerBrowse` to let the driver discover the member
layout from the controller's symbol table, rather than pre-declaring the container tag.
### Member writes
Discovered struct/UDT/FB member **writes** are **deferred** in this release. Scalar atomic
members can be written when pre-declared with `Writable: true`; the controller-browse expansion
path does not yet wire the write channel.
### Live caveat — Flat-mode vs VirtualTree-mode browse
The ADS client's `BrowseSymbolsAsync` may return symbols in **Flat mode** (a flat list of all
instance paths, with no `SubSymbols` populated) on some TC3 runtime configurations, in which
case the recursive struct expansion produces no sub-symbols and the walk yields only top-level
symbols. If a live TC3 browse does not populate `SubSymbols` on struct-typed symbols, the
alternative is **VirtualTree mode** (which returns a hierarchical symbol tree with `SubSymbols`
populated). VirtualTree-mode browse is an unverified follow-up; if Flat-mode struct expansion
produces empty `Discovered/` sub-folders for struct symbols, switch to VirtualTree mode via the
driver configuration.
## Further reading
- [`docs/v2/driver-specs.md §6`](../v2/driver-specs.md) — full per-field spec and