Merge b647-twincat: ADS sum-read (opt-in) + TwinCAT diagnostics on /hosts (#645 #310/#314, #612 docs)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
+48
-1
@@ -185,9 +185,56 @@ notifications; `DInt` over a `STRING(80)` → `BadTypeMismatch` in both paths wi
|
||||
the rest of the subscription Good. No PLC value held a genuine non-ASCII string
|
||||
to exercise Windows-1252 live — that rule is unit-tested only.
|
||||
|
||||
## Sum-read and diagnostics (Gitea #645)
|
||||
|
||||
Two items ported from the stranded `auto/driver-gaps` branch (#310, #314), re-implemented against the
|
||||
current raw-byte read path rather than cherry-picked (the branch predates the type guard, the string codec
|
||||
and the array codec). Unit-tested against the fake client only; the live gate is read-only and pending a
|
||||
route (see `docs/v3/twincat-backlog.md`).
|
||||
|
||||
### Sum-read (opt-in)
|
||||
|
||||
`useSumRead: true` (default **off**) batches a poll's reads for one device into ADS sum-read commands
|
||||
(`IndexGroup 0xF080`) of at most `sumReadMaxItems` symbols (default 200, ceiling 500) and at most 32 KiB of
|
||||
payload (read lengths + 4 bytes each) instead of one round-trip per tag. TC2 CE 2.11 support for the sum
|
||||
command is unverified, hence opt-in.
|
||||
|
||||
- **Eligibility is strict.** The sum path reads raw bytes at the symbol's index group / offset and decodes them
|
||||
as the DECLARED type (the notification rules), so a scalar rides the batch only when it passes
|
||||
`TwinCATTypeGuard.CheckNotification` — same width, same float/integer kind. A tag the lenient single read
|
||||
accepts by value conversion (`DInt` over a PLC `INT`, `Real` over `LREAL` or `DINT`) is read **individually**
|
||||
inside the call, keeping the Beckhoff conversion; the batch never publishes reinterpreted bytes at Good.
|
||||
Arrays use the same raw-bytes rule as on the single path. A type-mismatched item gets `BadTypeMismatch`
|
||||
without joining the batch; a bit-indexed BOOL and a handle-addressed symbol (`0xF005`) are read singly.
|
||||
- Per-item ADS error codes map to that tag only (`MapAdsError`); a response shorter than asked is a size error
|
||||
for that item, not a failed chunk. The overall code is the command's.
|
||||
- **Latch vs read-singly.** Only overall ADS **1793 / 1794** (service / index group not supported) throws
|
||||
`TwinCATSumReadRefusedException`; the driver logs once, marks the device `SumReadUnsupported` and reads
|
||||
singly until re-initialise. **Any other non-comms overall code** (1792 general error, 1795 offset, 1797 size, 1798 data, ...) means
|
||||
*this request* was rejected — typically too big — so that chunk is read singly for this poll, nothing is
|
||||
latched, and a Warning is logged once per device so the live gate can see sum-read is not working there. A
|
||||
comms-level failure (router / port / timeout) is neither: that chunk goes `BadCommunicationError` and the next poll tries again.
|
||||
- A one-item chunk is just a single read. Sum-**write** is not implemented (writes stay per-tag).
|
||||
|
||||
### `/hosts` diagnostics (`IDriverDiagnostics`, opt-in)
|
||||
|
||||
Behind `diagnosticsEnabled` (default **off** — not yet live-gated). The driver implements the optional
|
||||
`IDriverDiagnostics` bag (the S7 `NegotiatedPduSize` precedent, #640), deliberately not synthetic raw tags.
|
||||
When enabled, after each healthy probe tick **and after the host-state transition** it reads up to four
|
||||
`TwinCAT_SystemInfoVarList` symbols per device — `_AppInfo.AppName`, `_AppInfo.OnlineChangeCnt`,
|
||||
`_TaskInfo[1].CycleTime`, `_TaskInfo[1].LastExecTime` (100 ns ticks) — so up to **4 extra read requests per
|
||||
device per probe interval** (default 5 s). The ADS state is the one `ProbeAsync` already read; no second
|
||||
ReadState. One device: flat keys `TwinCAT.AdsState`, `.AppName`, `.OnlineChangeCnt`, `.CycleTimeMs`,
|
||||
`.LastExecTimeMs`, `.HeadroomMs` (cycle − last exec; negative = the task overran; this is spare time, not
|
||||
jitter). Several devices: one line per device, `TwinCAT.<DeviceName or host>` =
|
||||
`Run; Plc30 App; online-changes 7; cycle 10.0 ms; exec 2.5 ms; headroom 7.5 ms`, so the host's 16-entry cap
|
||||
covers 16 devices. Times round to 0.1 ms (the bag's digest is in the publish fingerprint). Each symbol is
|
||||
best-effort: a runtime that does not export one (older TC2) keeps the others, and a symbol answering "not
|
||||
found" is not re-asked until a new client is installed (reconnect).
|
||||
|
||||
## Capability surface
|
||||
|
||||
`TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery, ISubscribable, IHostConnectivityProbe, IPerCallHostResolver, IRediscoverable`
|
||||
`TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery, ISubscribable, IHostConnectivityProbe, IPerCallHostResolver, IRediscoverable, ISkippedTagReporter, IDriverDiagnostics`
|
||||
(`src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDriver.cs`).
|
||||
|
||||
| Capability | Path | Notes |
|
||||
|
||||
+52
-18
@@ -5,27 +5,61 @@ The v2 TwinCAT driver is considered solid: 28 integration tests (14 `[TwinCATFac
|
||||
three latent driver bugs shaken out (notification cycle units, `STRING(N)` mapper,
|
||||
bit-indexed BOOL path). Further work is deferred.
|
||||
|
||||
Archived from `docs/drivers/TwinCAT-Test-Fixture.md` § Follow-up candidates.
|
||||
Archived from `docs/drivers/TwinCAT-Test-Fixture.md` § Follow-up candidates. Refreshed 2026-10-08
|
||||
(Gitea #612 / #645); live-validation results live in `docs/drivers/TwinCAT.md` § "Live validation".
|
||||
|
||||
## Deferred items
|
||||
## Hardware available (as of 2026-10-08)
|
||||
|
||||
1. **TC2 coverage** — spin up a TC2 runtime (Windows CE IPC or legacy XAR)
|
||||
and run the same suite; any delta surfaces. Blocked on hardware.
|
||||
The Elma cleaner fleet (25 presses, 23 live) is the only real TwinCAT hardware in reach, and it is
|
||||
**production — strictly read-only**:
|
||||
|
||||
2. **Notification coalescing under load** — run the subscribe test while
|
||||
the PLC cycle is saturated (bump `lineSim` complexity, watch for
|
||||
dropped notifications). Doable on current rig; deferred as lower
|
||||
priority than v3 feature work.
|
||||
| Press | Runtime | AMS | Notes |
|
||||
|---|---|---|---|
|
||||
| 22 presses (e.g. Z33856 `10.201.45.236.1.1`, Z33733 `5.25.53.46.1.1`) | **TC2 2.11 on WinCE 6.0** | port **801** | read-validated 2026-10-07 |
|
||||
| **Z35474** (`5.118.56.93.1.1`, 10.201.23.227) | **TC3 3.1.4024** (Win NT 10) | port **851** | the **only TC3 press**; "Plc30 App" |
|
||||
| TCBSD fixture `10.100.0.128` (`41.169.163.43.1.1:851`) | TC3 | 851 | not a press; unreachable from the Mac |
|
||||
|
||||
3. **Multi-hop AMS route** — add a test behind an IPC gateway with a
|
||||
chained route entry. Blocked on hardware (gateway IPC).
|
||||
Access facts: ADS is TCP 48898. The driver sends no AddRoute (routes are provisioned out of band) and has
|
||||
no IP field, so a host needs a local AMS router — TwinCAT XAR, or `Beckhoff.TwinCAT.Ads.TcpRouter` hosted
|
||||
in-process (the driver csproj does not reference TcpRouter; the 2026-10-07 run used a scratch harness).
|
||||
The Mac reaches the presses directly over the VPN. **The PLC routes left by earlier tooling are STALE:**
|
||||
they name the Mac's old VPN IP (10.181.198.189), and the Mac is now 10.181.207.140, so a source NetId of
|
||||
`10.181.207.140.1.1` has no route on any press. We must not AddRoute on production, so the **live gate for
|
||||
this backlog is deferred pending a user-approved route** (owner refreshes the route, or the user approves
|
||||
reusing the old source NetId). Nothing below that says "live" has been run against TC3.
|
||||
|
||||
4. **License-rotation automation** — XAR's 7-day trial expires on
|
||||
schedule. Either automate `TcActivate.exe /reactivate` via a scheduled
|
||||
task on the VM (not officially supported; reportedly works for some
|
||||
TC3 builds), or buy a paid runtime license (~$1k one-time per runtime
|
||||
per CPU) to kill the rotation. Ops item, not code.
|
||||
## Status of the original items
|
||||
|
||||
5. **Lab rig** — cheapest IPC (CX7000 / CX9020) on a dedicated network;
|
||||
the only route that covers TC2 + real EtherCAT I/O timing + cycle
|
||||
jitter under CPU load. Blocked on hardware + budget.
|
||||
1. **TC2 coverage — DONE (read side).** No longer "blocked on hardware": the 2026-10-07 Elma run
|
||||
(TwinCAT 2.11) verified browse, scalar/array/string reads and native notifications on TC2. What is
|
||||
still unverified there: sum-read support (see item 6) and anything that writes.
|
||||
|
||||
2. **Notification coalescing under load** — run the subscribe test while the PLC cycle is saturated.
|
||||
Needs the TCBSD fixture (loading a production press is out of the question) and the `GVL_Perf` /
|
||||
`FB_PerfChurn` fixture project from the stranded `auto/driver-gaps` branch. Fixture-gated.
|
||||
|
||||
3. **Multi-hop AMS route** — a test behind an IPC gateway with a chained route entry. No hardware.
|
||||
|
||||
4. **License-rotation automation** — XAR's 7-day trial expires on schedule. Ops item, not code.
|
||||
|
||||
5. **Lab rig** — cheapest IPC on a dedicated network; the only route that covers real EtherCAT I/O
|
||||
timing and cycle jitter under CPU load. Blocked on hardware + budget.
|
||||
|
||||
## Added by #612 / #645
|
||||
|
||||
6. **ADS sum-read (#310) — code DONE, live gate pending.** Opt-in `useSumRead` / `sumReadMaxItems`
|
||||
(strict width+kind eligibility; latch only on ADS 1793/1794; see `docs/drivers/TwinCAT.md` § "Sum-read"). Unit-tested against the fake client only. Live gate
|
||||
(read-only, valid on a press): enable on Z33856 / Z33733 (TC2) and Z35474 (TC3), compare a batched poll
|
||||
to individual reads value-for-value, and record whether TC2 CE 2.11 answers or refuses the sum command
|
||||
(either outcome is a result — a refusal must fall back cleanly). Needs the route above.
|
||||
|
||||
7. **Driver diagnostics (#314) — code DONE (opt-in `diagnosticsEnabled`, default off), live gate pending.** ADS state, app name, online-change
|
||||
count, task cycle time, last exec time and headroom on the `/hosts` diagnostics bag. Live gate: confirm
|
||||
`TwinCAT_SystemInfoVarList._TaskInfo[1]` resolves on TC2 2.11 (the driver tolerates its absence).
|
||||
|
||||
8. **Flat vs VirtualTree browse on TC3 (#612)** — open. Only Z35474 is TC3; read-only symbol-table
|
||||
upload in both modes, compare leaf sets. Add a `SymbolsLoadMode` option only if Flat drops struct
|
||||
members. Needs the route. (Flat on TC2 already proved fine: 45,083 / 42,666 variables.)
|
||||
|
||||
9. **Discovered struct/FB member writes (#612)** — deferred: writes, never on a press; fixture-only.
|
||||
Likewise **sum-write** was deliberately NOT ported from the stranded branch.
|
||||
|
||||
Reference in New Issue
Block a user