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.
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
+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
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.
+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
`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
+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) |
| 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** |
@@ -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.
@@ -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);
}
@@ -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)