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
This commit is contained in:
Joseph Doherty
2026-10-09 04:12:53 -04:00
parent 14ec145487
commit 9e894decb0
7 changed files with 37 additions and 31 deletions
+3 -3
View File
@@ -77,9 +77,9 @@ their flag values to the already-shipped driver.
non-empty CIP routing path before forwarding to the PCCC dispatcher. non-empty CIP routing path before forwarding to the PCCC dispatcher.
Pass `--gateway "ab://127.0.0.1:44818/1,0"` against the Docker Pass `--gateway "ab://127.0.0.1:44818/1,0"` against the Docker
fixture; real SLC / MicroLogix / PLC-5 hardware accepts an empty fixture; real SLC / MicroLogix / PLC-5 hardware accepts an empty
path (`ab://host:44818/`). Bit-file writes (`B3:0/5`) still surface path (`ab://host:44818/`). Bit-file writes (`B3:0/5`) pass against
`0x803D0000` against ab_server — route operator-critical bit writes ab_server SLC500/MicroLogix on both transports (the old `0x803D0000` no longer
to real hardware until upstream fixes this. 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 PUT/GET communication** must be enabled in TIA Portal for any
S7-1200/1500. See [Driver.S7.Cli.md](Driver.S7.Cli.md). S7-1200/1500. See [Driver.S7.Cli.md](Driver.S7.Cli.md).
- **TwinCAT AMS router** must be reachable (local XAR, standalone Router - **TwinCAT AMS router** must be reachable (local XAR, standalone Router
+2 -1
View File
@@ -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 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: 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). [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 - ⚠️ 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 presses are read-only and there is no bench controller, so this cannot be proven; it stays open until a
bench controller exists. bench controller exists.
+6 -8
View File
@@ -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 three families when `AB_LEGACY_CIP_PATH=1,0` (the default). The earlier
`BadCommunicationError` was traced to `ab_server` requiring a non-empty CIP `BadCommunicationError` was traced to `ab_server` requiring a non-empty CIP
routing path before forwarding to the PCCC dispatcher — the `/1,0` workaround routing path before forwarding to the PCCC dispatcher — the `/1,0` workaround
resolves it (see `Docker/README.md §Known limitations`). Residual gap: bit-file resolves it (see `Docker/README.md §Known limitations`). The old bit-file write
writes (`B3:0/5`) still surface `0x803D0000` against `ab_server`. Unit tests gap (`B3:0/5` giving `0x803D0000`) no longer reproduces (2026-10-09, `AbLegacyBitWriteTests`). Unit tests
via `FakeAbLegacyTag` carry full contract coverage for all paths. via `FakeAbLegacyTag` carry full contract coverage for all paths.
## What the fixture is ## What the fixture is
@@ -105,12 +105,10 @@ cover the common ones but uncommon ones (`R` counters, `S` status files,
## Follow-up candidates ## Follow-up candidates
1. **Close residual ab_server bit-file write gap** — N (Int16), F (Float32), 1. **(Closed, 2026-10-09) ab_server bit-file write gap** — N, F and L files
and L (Int32) files round-trip cleanly across SLC500 / MicroLogix / PLC-5 round-trip with the `/1,0` workaround, and `B3:0/5` bit writes now pass on
modes with the `/1,0` cip-path workaround in place. Remaining gap: bit-file SLC500/MicroLogix (both transports; `AbLegacyBitWriteTests`). The May 2026
writes (`B3:0/5`) surface `0x803D0000` against `ab_server --plc=SLC500`. `0x803D0000` does not reproduce; its cause is undetermined.
Contributing a patch to `libplctag/libplctag` to close this would remove
the last Docker-vs-hardware divergence for bit writes.
2. **Rockwell RSEmulate 500 golden-box tier** — Rockwell's real emulator 2. **Rockwell RSEmulate 500 golden-box tier** — Rockwell's real emulator
for SLC/MicroLogix/PLC-5. Would close UDT-equivalent (integer-file for SLC/MicroLogix/PLC-5. Would close UDT-equivalent (integer-file
indirection), timer/counter decomposition, and real ladder execution indirection), timer/counter decomposition, and real ladder execution
+1 -1
View File
@@ -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) | | 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 | | 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 | | 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 | | 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** | | T/C/R sub-elements (`.PRE`/`.ACC`/`.LEN`/`.POS`), 6-byte T/C/R element layout | **none** |
@@ -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. alike; this is evidence only, no code changed.
- One empty page (`Program:Auxiliary`, 0 entries) was observed and handled. - 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` Provenance: `AbLegacy.IntegrationTests/Docker/README.md` ("Bit-file writes on ab_server", May 2026, libplctag + `--plc=SLC500`,
(writes `B3:0` = 0x0A0A, sets bit 5, clears bit 3, asserts the whole word each time so neighbour bits are proven preserved), before the native transport) recorded `B3:0/5` -> `0x803D0000`. New `AbLegacyBitWriteTests` (writes `B3:0` = 0x0A0A, sets
local ab_server (libplctag v2.7.0 + patches 0001/0002, built in OrbStack), `AB_LEGACY_ENDPOINT` pinned to a lane-local port: 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 | | Family | Libplctag | Native |
|---|---|---| |---|---|---|
| SLC500 | pass | pass | | SLC500 | pass | pass |
| MicroLogix | pass | pass | | MicroLogix | pass | pass |
| PLC-5 | pass | `BadNotSupported` (0x803D0000), by design | | PLC-5 | pass | `BadNotSupported` (0x803D0000) |
- 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.
- **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.
@@ -193,6 +193,7 @@ public sealed class EvidenceTests
$"max={(ids.Count > 0 ? ids.Max() : 0)} more={more} ascending={strictly} ids=[{sample}]"); $"max={(ids.Count > 0 ? ids.Max() : 0)} more={more} ascending={strictly} ids=[{sample}]");
} }
Log($"Non-ascending pages: {nonAscending}"); Log($"Non-ascending pages: {nonAscending}");
Log(nonAscending == 0 ? "RESULT: every listing page strictly ascending." : $"RESULT: {nonAscending} NON-ASCENDING page(s) observed.");
pages.ShouldNotBeEmpty(); pages.ShouldNotBeEmpty();
opens.ShouldBe(1); opens.ShouldBe(1);
} }
@@ -121,11 +121,17 @@ trip cleanly across SLC500, MicroLogix, and PLC-5 modes.
### Bit-file writes on ab_server ### Bit-file writes on ab_server
`B3:0/5`-style bit-in-boolean writes currently surface `0x803D0000` Originally (May 2026, libplctag only, before the native transport) a `B3:0/5`
against `ab_server --plc=SLC500`; bit reads work. Non-blocking for the bit write surfaced `0x803D0000` against `ab_server --plc=SLC500`. **That does
smoke suite (which targets N-file Int16 + F-file float reads), but not reproduce any more** (2026-10-09): `AbLegacyBitWriteTests` writes `B3:0`,
bit-write fidelity isn't simulator-verified — route operator-critical sets bit 5 and clears bit 3 and asserts the whole word each time, and passes
bit writes to real hardware or RSEmulate 500 until upstream resolves. 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) ### Other known gaps (unchanged from ab_server)