From 9e894decb0c848b48a2e077fecbfac330303ab53 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Fri, 9 Oct 2026 04:12:53 -0400 Subject: [PATCH] docs(ab): hw4 evidence fix round 1 - bit-write gap provenance, README/CLI/fixture docs, libplctag-deletion gap Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe --- docs/DriverClis.md | 6 ++--- docs/drivers/AbCip.md | 3 ++- docs/drivers/AbLegacy-Test-Fixture.md | 14 +++++----- docs/drivers/AbLegacy.md | 2 +- ...-10-07-native-ab-transport-gate-results.md | 26 +++++++++---------- .../Differential/EvidenceTests.cs | 1 + .../Docker/README.md | 16 ++++++++---- 7 files changed, 37 insertions(+), 31 deletions(-) diff --git a/docs/DriverClis.md b/docs/DriverClis.md index d2e38da9..1ed29e47 100644 --- a/docs/DriverClis.md +++ b/docs/DriverClis.md @@ -77,9 +77,9 @@ their flag values to the already-shipped driver. non-empty CIP routing path before forwarding to the PCCC dispatcher. Pass `--gateway "ab://127.0.0.1:44818/1,0"` against the Docker fixture; real SLC / MicroLogix / PLC-5 hardware accepts an empty - path (`ab://host:44818/`). Bit-file writes (`B3:0/5`) still surface - `0x803D0000` against ab_server — route operator-critical bit writes - to real hardware until upstream fixes this. + path (`ab://host:44818/`). Bit-file writes (`B3:0/5`) pass against + ab_server SLC500/MicroLogix on both transports (the old `0x803D0000` no longer + reproduces; native PLC-5 refuses all writes with the same code, #571). - **S7 PUT/GET communication** must be enabled in TIA Portal for any S7-1200/1500. See [Driver.S7.Cli.md](Driver.S7.Cli.md). - **TwinCAT AMS router** must be reachable (local XAR, standalone Router diff --git a/docs/drivers/AbCip.md b/docs/drivers/AbCip.md index 15da2cea..3df8810e 100644 --- a/docs/drivers/AbCip.md +++ b/docs/drivers/AbCip.md @@ -243,7 +243,8 @@ The driver has two interchangeable wire transports, selected by one `DriverConfi Good). Still ab_server-only: array, STRING and UDT-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** until a bench controller allows write verification. +- **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). - ⚠️ 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. diff --git a/docs/drivers/AbLegacy-Test-Fixture.md b/docs/drivers/AbLegacy-Test-Fixture.md index 95bc253b..f8cfc591 100644 --- a/docs/drivers/AbLegacy-Test-Fixture.md +++ b/docs/drivers/AbLegacy-Test-Fixture.md @@ -11,8 +11,8 @@ for N-file (Int16), F-file (Float32), and L-file (Int32) reads across all three families when `AB_LEGACY_CIP_PATH=1,0` (the default). The earlier `BadCommunicationError` was traced to `ab_server` requiring a non-empty CIP routing path before forwarding to the PCCC dispatcher — the `/1,0` workaround -resolves it (see `Docker/README.md §Known limitations`). Residual gap: bit-file -writes (`B3:0/5`) still surface `0x803D0000` against `ab_server`. Unit tests +resolves it (see `Docker/README.md §Known limitations`). The old bit-file write +gap (`B3:0/5` giving `0x803D0000`) no longer reproduces (2026-10-09, `AbLegacyBitWriteTests`). Unit tests via `FakeAbLegacyTag` carry full contract coverage for all paths. ## What the fixture is @@ -105,12 +105,10 @@ cover the common ones but uncommon ones (`R` counters, `S` status files, ## Follow-up candidates -1. **Close residual ab_server bit-file write gap** — N (Int16), F (Float32), - and L (Int32) files round-trip cleanly across SLC500 / MicroLogix / PLC-5 - modes with the `/1,0` cip-path workaround in place. Remaining gap: bit-file - writes (`B3:0/5`) surface `0x803D0000` against `ab_server --plc=SLC500`. - Contributing a patch to `libplctag/libplctag` to close this would remove - the last Docker-vs-hardware divergence for bit writes. +1. **(Closed, 2026-10-09) ab_server bit-file write gap** — N, F and L files + round-trip with the `/1,0` workaround, and `B3:0/5` bit writes now pass on + SLC500/MicroLogix (both transports; `AbLegacyBitWriteTests`). The May 2026 + `0x803D0000` does not reproduce; its cause is undetermined. 2. **Rockwell RSEmulate 500 golden-box tier** — Rockwell's real emulator for SLC/MicroLogix/PLC-5. Would close UDT-equivalent (integer-file indirection), timer/counter decomposition, and real ladder execution diff --git a/docs/drivers/AbLegacy.md b/docs/drivers/AbLegacy.md index 383a4eab..6758ef01 100644 --- a/docs/drivers/AbLegacy.md +++ b/docs/drivers/AbLegacy.md @@ -219,7 +219,7 @@ Everything below is against the `ab_server` simulator; no real PCCC controller h | F-file read (`F8`) | one capture, `slc500-read-f8` (codec parity only; no native F write on a wire) | | S (incl. the default probe `S:0`), B, T, C, R, L, ST, A, O, I | **none** — encoded from the PCCC file-type table | | MicroLogix function files (RTC/HSC/...), PLC-5 octal I/O | **none** — parsed only; native refuses function files as Unsupported | -| Bit-within-word write (`B3:0/5`) | `ab_server` SLC500 + MicroLogix, both transports (`AbLegacyBitWriteTests`): the driver RMWs the parent word, only the addressed bit changes. PLC-5 native refuses it (`BadNotSupported`, #571) | +| Bit-within-word write (`B3:0/5`) | `ab_server` SLC500 + MicroLogix, both transports (`AbLegacyBitWriteTests`): the driver RMWs the parent word, only the addressed bit changes (the parent-word read-modify-write is not atomic against ladder writes to the same word). PLC-5 native refuses it (`BadNotSupported`, #571) | | Indirect addressing (`N7:[N7:0]`) | `ab_server` N-file, both transports (#646) - resolved by the driver, the transport sees the composed address | | T/C/R sub-elements (`.PRE`/`.ACC`/`.LEN`/`.POS`), 6-byte T/C/R element layout | **none** | 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 6877f513..6777ebd2 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 @@ -539,23 +539,23 @@ pages** (controller scope 8 pages, then each of the 15 programs, up to 6 pages f alike; this is evidence only, no code changed. - One empty page (`Program:Auxiliary`, 0 entries) was observed and handled. -### ab_server PCCC bit-file write (`B3:0/5`): not an ab_server gap (no patch needed) +### ab_server PCCC bit-file write (`B3:0/5`): the documented gap does not reproduce (no patch) -The reported failure `0x803D0000` is `BadNotSupported`. Reproduced with new `AbLegacyBitWriteTests` -(writes `B3:0` = 0x0A0A, sets bit 5, clears bit 3, asserts the whole word each time so neighbour bits are proven preserved), -local ab_server (libplctag v2.7.0 + patches 0001/0002, built in OrbStack), `AB_LEGACY_ENDPOINT` pinned to a lane-local port: +Provenance: `AbLegacy.IntegrationTests/Docker/README.md` ("Bit-file writes on ab_server", May 2026, libplctag + `--plc=SLC500`, +before the native transport) recorded `B3:0/5` -> `0x803D0000`. New `AbLegacyBitWriteTests` (writes `B3:0` = 0x0A0A, sets +bit 5, clears bit 3, asserts the whole word each time so neighbour bits are proven preserved) against local ab_server +(libplctag v2.7.0 + patches 0001/0002, OrbStack, `AB_LEGACY_ENDPOINT` pinned to a lane-local port): | Family | Libplctag | Native | |---|---|---| | SLC500 | pass | pass | | MicroLogix | pass | pass | -| PLC-5 | pass | `BadNotSupported` (0x803D0000), by design | - -- The driver does a per-parent-word read-modify-write (typed read, then typed write of the word), never a masked CMD 0x0F/FNC - 0xAB write, so the bit write needs nothing ab_server lacks. (ab_server does implement FNC 0xAB, `handle_slc_rmw_request`, for - 16-bit elements; the driver simply does not use it.) -- The 0x803D0000 is the **native PLC-5 write refusal** (#571, `NativeAbLegacyTagRuntime.WriteRefusal`: all PLC-5 writes are - refused before the wire). The test pins it for PLC-5 + Native and requires success everywhere else. Because nothing was broken, - there is no fix to revert for a red/green proof; the PLC-5 expectation was the part that failed first (red) when the test - wrongly assumed success, which shows the assertions discriminate. No Dockerfile or patch change. +| PLC-5 | pass | `BadNotSupported` (0x803D0000) | +- **Finding:** the originally documented gap does not reproduce on the current fixture and driver; the cause of the original + failure is undetermined. No ab_server patch or Dockerfile change is needed. The driver does a per-parent-word read-modify-write + (typed read, typed write of the word), not a masked FNC 0xAB write (ab_server does implement 0xAB for 16-bit elements). +- The PLC-5 native `0x803D0000` is a **separate, intended** behaviour that merely shares the status code: all native PLC-5 + 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. diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Differential/EvidenceTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Differential/EvidenceTests.cs index 6deb8453..bd62ddf4 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Differential/EvidenceTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Differential/EvidenceTests.cs @@ -193,6 +193,7 @@ public sealed class EvidenceTests $"max={(ids.Count > 0 ? ids.Max() : 0)} more={more} ascending={strictly} ids=[{sample}]"); } Log($"Non-ascending pages: {nonAscending}"); + Log(nonAscending == 0 ? "RESULT: every listing page strictly ascending." : $"RESULT: {nonAscending} NON-ASCENDING page(s) observed."); pages.ShouldNotBeEmpty(); opens.ShouldBe(1); } diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.IntegrationTests/Docker/README.md b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.IntegrationTests/Docker/README.md index 18b8aef2..f750e658 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.IntegrationTests/Docker/README.md +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.IntegrationTests/Docker/README.md @@ -121,11 +121,17 @@ trip cleanly across SLC500, MicroLogix, and PLC-5 modes. ### Bit-file writes on ab_server -`B3:0/5`-style bit-in-boolean writes currently surface `0x803D0000` -against `ab_server --plc=SLC500`; bit reads work. Non-blocking for the -smoke suite (which targets N-file Int16 + F-file float reads), but -bit-write fidelity isn't simulator-verified — route operator-critical -bit writes to real hardware or RSEmulate 500 until upstream resolves. +Originally (May 2026, libplctag only, before the native transport) a `B3:0/5` +bit write surfaced `0x803D0000` against `ab_server --plc=SLC500`. **That does +not reproduce any more** (2026-10-09): `AbLegacyBitWriteTests` writes `B3:0`, +sets bit 5 and clears bit 3 and asserts the whole word each time, and passes +on SLC500 and MicroLogix on both libplctag and native, and on PLC-5 with +libplctag. The cause of the original failure was never determined (the +driver's parent-word read-modify-write and ab_server's PCCC handlers are +both unchanged in shape, so an older libplctag/driver path is the likely +difference). PLC-5 on the native transport returns `0x803D0000` +(`BadNotSupported`) for a different reason: all native PLC-5 writes are +refused before the wire (#571); the test pins that. ### Other known gaps (unchanged from ab_server)