From 12aa293cdc75521ce13170490603879706266041 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Fri, 9 Oct 2026 04:49:19 -0400 Subject: [PATCH] docs(abcip): #611 live member-write gate on Z35162 (native DINT/BIT/REAL members, restored) Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe --- CLAUDE.md | 4 ++-- docs/drivers/AbCip.md | 20 ++++++++++--------- ...-10-07-native-ab-transport-gate-results.md | 17 ++++++++++++++++ 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index d5af8cc9..f18f9991 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1087,7 +1087,7 @@ re-emit a concrete default. The defaults differ per driver (final review, 2026-1 because the driver factory AND the admin-node Test Connect probe both read it. ⚠️ So **the upgrade restart alone moves no-key AbCip drivers to Native**; to stay on libplctag, set the node key — NOT a per-driver key through the old AdminUI, whose typed form drops unknown keys (#576). Evidence: AbCip reads proven on a real - CompactLogix (Z-35162); scalar native writes (DINT/BOOL/REAL) proven on it once (2026-10-09, user-approved one-off, restored) — array/STRING/UDT-member and fragmented writes are `ab_server`-only. + CompactLogix (Z-35162); scalar native writes (DINT/BOOL/REAL) proven on it once (2026-10-09, user-approved one-off, restored) — and native UDT member writes (DINT/BIT/REAL, #611 gate, same day); array/STRING, libplctag member and fragmented writes are `ab_server`-only. - **AbLegacy: an absent key resolves to `Libplctag` for every family; `Native` is opt-in** (no node key). On a wire the native PCCC path is proven only for **N-file reads/writes and one F8 read**, all on `ab_server`; `S:0` (the default probe), B/T/C/R/S/L/ST/A/O/I and the T/C/R sub-element offsets are encoded from the PCCC @@ -1102,7 +1102,7 @@ once per tag — only genuine transport failures evict and degrade (a runtime `N it used to latch `Degraded`). AbCip width-checks every scalar **write** against the controller's bytes before sending, on both transports (#611; libplctag used to patch part of its buffer and send it all). Controller-discovered UDT members are writable once browse-committed and authored `ReadWrite` (BIT members included — the controller -applies the bit) — ab_server-proven only, member and BIT-member writes are unverified on real hardware; whole-UDT writes are refused `BadNotSupported` by decision (`docs/drivers/AbCip.md` §Member writes). `DeviceSessionPool.Shared` logs through the +applies the bit) — native member writes incl. a BIT member are live-gated on Z35162 (2026-10-09); sibling-bit preservation and the libplctag path are ab_server-only; whole-UDT writes are refused `BadNotSupported` by decision (`docs/drivers/AbCip.md` §Member writes). `DeviceSessionPool.Shared` logs through the host logger factory (wired in `DriverFactoryBootstrap`; the FOCAS and TwinCAT factories' `Register(registry, ILoggerFactory?)` take one too, #602 CONV-3). The AbCip `browsePageDelayMs` knob has an AdminUI field (`AbCipDriverForm` "Browse page delay (ms)"). libplctag is confined to `Libplctag*.cs` adapters by `LibplctagConfinementGuardTests`; do not reference it elsewhere. See `docs/drivers/AbCip.md` §Transport, `docs/drivers/AbLegacy.md` §Transport + §Wire evidence, `docs/Configuration.md` §`Drivers:AbCip`, and diff --git a/docs/drivers/AbCip.md b/docs/drivers/AbCip.md index 9e570ec1..214790cb 100644 --- a/docs/drivers/AbCip.md +++ b/docs/drivers/AbCip.md @@ -240,14 +240,14 @@ The driver has two interchangeable wire transports, selected by one `DriverConfi - ⚠️ **Native hardware evidence is narrow.** The read-only real-press gate (Task 21, 2026-10-08) proved native reads, browse and templates on Z-35162; a user-approved one-off on 2026-10-09 added **scalar native writes on a real CompactLogix** (DINT `test2`, BOOL `Program:UN01_Press.xxtest`, REAL `Program:ES03_EM03_Zone3._ff_test`: write, read back, restore, all - Good). Still ab_server-only: array, STRING and UDT-member writes and fragmented writes. Mixed STRING/array/scalar + Good), and the #611 gate added **native UDT member writes** (DINT, BIT and REAL members of `_EmptyTemperatureSegment`). + Still ab_server-only: array and STRING writes, libplctag member writes, and fragmented writes. Mixed STRING/array/scalar Multiple Service Packet reads (#582) are proven on the press (one 5-read MSP, values equal to single reads). Results: [native-ab-transport-gate-results](../plans/2026-10-07-native-ab-transport-gate-results.md). - **libplctag cannot be deleted yet.** Scalar native writes are proven on a CompactLogix (2026-10-09); what remains is #567 - (fragmented wide-struct writes), array/STRING/UDT-member writes on hardware, and AbLegacy native (simulator-only, no PCCC hardware). -- Discovered UDT **member writes** (#611) are likewise `ab_server`-only on both transports; the - BIT-member argument rests on a read-only hardware observation (the member reads as a 1-byte `0xC1` - BOOL). See [Member writes](#member-writes) for the candidate gate tag. + (fragmented wide-struct writes), array/STRING writes on hardware, and AbLegacy native (simulator-only, no PCCC hardware). +- UDT **member writes** (#611) are proven on a real CompactLogix on the **native** transport only (2026-10-09); the + libplctag member-write path is `ab_server`-only. See [Member writes](#member-writes). - ⚠️ RESIDUAL (#567): wide-struct **fragmented writes** (8-byte alignment) are unit-tested only. Production presses are read-only and there is no bench controller, so this cannot be proven; it stays open until a bench controller exists. @@ -465,10 +465,12 @@ when it is browse-committed in `/raw`, and that tag writes like any other atomic **libplctag** exposes only the size, cannot tell the two apart, and refuses both `BadNotSupported`. Native additionally refuses a same-width mismatch (REAL vs DINT) from the CIP type code — see [Native transport behaviour](#native-transport-behaviour). A refusal keeps the handle, warns once per tag and **does not touch driver health**. -- **Not verified on a real controller.** Production presses are read-only, so member writes are proven on - `ab_server` only (Docker patch 0003 serves member paths). A candidate for a future *approved* write gate: - Z-35162 controller tag `_EmptyTemperatureSegment` (`UDT_TemperatureProfile_Segment`), which no logic - references — its members (`ID`, `Enable`, `RampTime`, …) are all zero and resolve as described above. +- **Live gate (2026-10-09, user-approved, native transport, restored):** on Z-35162, controller tag + `_EmptyTemperatureSegment` (`UDT_TemperatureProfile_Segment`, referenced by no logic) — `ID` (DINT) 0→4242→0, + `Enable` (BIT member) false→true→false, `HoldTime` (REAL) 0→42.5→0; every write and read Good, and a full snapshot + of all ten members after each write and restore showed only the written member changed. The BIT member is the + only BIT in that UDT, so preservation of *sibling bits in the same host SINT* is still `ab_server`-proven only. + The libplctag member-write path was not exercised on hardware (ab_server only, Docker patch 0003). Pre-declared `Members` with `Writable: true` keep working as before — same write path, now width-checked (a mistyped declared member is refused where libplctag used to write it). diff --git a/docs/plans/2026-10-07-native-ab-transport-gate-results.md b/docs/plans/2026-10-07-native-ab-transport-gate-results.md index 6777ebd2..906d3ec1 100644 --- a/docs/plans/2026-10-07-native-ab-transport-gate-results.md +++ b/docs/plans/2026-10-07-native-ab-transport-gate-results.md @@ -559,3 +559,20 @@ bit 5, clears bit 3, asserts the whole word each time so neighbour bits are prov writes are refused before the wire (#571, `NativeAbLegacyTagRuntime.WriteRefusal`). The test pins it. It is not the original observation. - No fix, so no revert-to-red proof; the test would fail if the word RMW clobbered neighbour bits. + +## #611 UDT member-write gate — 2026-10-09 (user-approved, native transport) + +Target: Z-35162, controller tag `_EmptyTemperatureSegment` (`UDT_TemperatureProfile_Segment`; no routine, alias or +trend references it in the 2026-09-11 L5X export; not in the Galaxy map). Tool: AbCip CLI from master `6c6800e5`, +`ab://10.209.70.160/1,0`, family CompactLogix, default (native) transport. Each leg: write, snapshot all ten members, +restore, snapshot again. + +| Member | Type | Before | Wrote | Read back | Restored | Other 9 members | +|---|---|---|---|---|---|---| +| `ID` | DINT | 0 | 4242 | 4242 | 0 | unchanged (0/false) | +| `Enable` | BIT (BOOL member) | false | true | true | false | unchanged | +| `HoldTime` | REAL | 0 | 42.5 | 42.5 | 0 | unchanged | + +Every write and read returned Good (`0x00000000`); the final snapshot equals the pre-gate snapshot. Not covered: +sibling-bit preservation inside one hidden host SINT (this UDT has a single BIT member) and the libplctag +member-write path. Raw output: the session scratchpad `hw4/gate611.txt`.