docs(ablegacy): #592 real MicroLogix 1400 evidence (native reads/writes pass; libplctag fails S/O/I/status bits)

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
Joseph Doherty
2026-10-09 05:23:28 -04:00
parent 12aa293cdc
commit beedac8218
3 changed files with 106 additions and 8 deletions
+5 -5
View File
@@ -1089,11 +1089,11 @@ re-emit a concrete default. The defaults differ per driver (final review, 2026-1
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) — 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
table and **unverified natively**. Explicit Native on PLC-5/`LogixPccc` logs a once-per-instance Warning.
AbLegacy native refuses status-bit sub-element writes (`T4:0.DN`…, `BadNotSupported`) and its ST byte order
is unverified.
a real MicroLogix 1400 (Z-33323, idle, 2026-10-09) native read S/N/F/B/T/C/R/ST/O/I incl. `S:0`, T/C/R
sub-elements and status bits, and ST byte order, all correct, and wrote N/F8/B-bit (restored); ⚠️ **libplctag (the
default) fails `S:`/`O:`/`I:` and control status bits there** — the default probe `S:0` fails on real hardware.
L/A files, function files, SLC, PLC-5 and `logixpccc` remain unverified natively. Explicit Native on PLC-5/`LogixPccc` logs a once-per-instance Warning.
AbLegacy native refuses status-bit sub-element writes (`T4:0.DN`…, `BadNotSupported`).
Refusals are not comms faults: an Unsupported refusal (`BadNotSupported`) and an AbCip native **type
mismatch** (`BadTypeMismatch`, where libplctag returned Good garbage for a same-width mis-authored type) both
+30 -3
View File
@@ -210,18 +210,45 @@ Native routing: SLC500/MicroLogix/PLC-5 are direct; `logixpccc` uses Unconnected
### Wire evidence (native PCCC)
Everything below is against the `ab_server` simulator; no real PCCC controller has been exercised.
**Real controller (2026-10-09, user-directed):** MicroLogix 1400 **Z-33323** (`ab://10.201.44.231/`, empty path),
chosen because it was idle (`N10:150/0` cycle-in-progress and `B3:0/4` false, pump input `I:0/0` off, `O:0` = 0
before and after). Read pass, each address on libplctag then native (raw output in
[the gate-results doc](../plans/2026-10-07-native-ab-transport-gate-results.md)):
| Address(es) | libplctag | Native |
|---|---|---|
| `S:0` (default probe), `S:57` (= 1400), `S:4` | **BadCommunicationError** | Good (4, 1400, clock) |
| `N9:0`, `N9:4`, `N7:255` | Good | Good, equal |
| `F8:226` (2.1), `F8:214` (200.0) | Good | Good, equal |
| `B3:24`, `B3:28` (0x0905), bits `/0`, `/8`, `/9`, `B3:132/14` | Good | Good, equal |
| `T4:15/93/14/11.PRE`, `.ACC`, `.DN`; `T40:3/12.PRE` | Good | Good, equal |
| `C5:0.PRE/.ACC`, `C5:1.PRE`, `C5:8.DN` | Good | Good, equal |
| `R6:0.LEN`, `.POS` | Good | Good, equal |
| `R6:0.DN` (and `.EN`) | **BadCommunicationError** | Good |
| `ST14:10` (`"Z-33323"`), `ST14:0` | Good | Good, equal — **ST byte order correct** |
| `O:0`, `O:0/3`, `I:0`, `I:0/0` | **BadCommunicationError** | Good |
Native writes on the same idle press, each restored to 0 and verified: `N7:23` 0→4242→0 (neighbours `N7:22/24`
unchanged), `F8:190` 0→42.5→0, `B3:200` bit RMW `/5` set → 32, `/3` set → 40, `/5` clear → 8, `/3` clear → 0
(neighbours `B3:199/201` unchanged). Addresses vetted against the sibling Z-33756 program export (no ladder,
symbol, HMI, panel or migration reference) and Z-33323's own symbol table, and read 0 beforehand.
⚠️ **libplctag (the default) cannot read `S`, `O`, `I` or control status bits on this controller**, and the
numbered forms (`S2:0`, `O0:0`, `I1:0`) are rejected by the address parser — so the default probe `S:0` fails on a
real MicroLogix under the default transport (tracked as a follow-up issue). Not tested on hardware: L and A files
(no press has them), function files, PLC-5, SLC, `logixpccc`.
Simulator evidence:
| What | Evidence |
|---|---|
| N-file read (`N7:0`) | libplctag captures `slc500-read-n7`, `micrologix-read-n7`, `plc5-read-n7` (codec byte parity) + native `AbLegacyReadSmokeTests` read on all three profiles |
| N-file write (`N7:5`) | capture `slc500-write-n7` + native `AbLegacyReadSmokeTests` write/read round trip (SLC 500) |
| 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. `S:0`), B, T, C, R, ST, O, I | **real MicroLogix 1400 (above)**; L and A: none |
| 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 (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** |
| T/C/R sub-elements (`.PRE`/`.ACC`/`.LEN`/`.POS`, status bits) | **real MicroLogix 1400 (above)** |
### Native known gaps
@@ -576,3 +576,74 @@ restore, snapshot again.
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`.
## AbLegacy on a real MicroLogix 1400 — 2026-10-09 (Z-33323, idle, user-directed)
Press selection: Z-33470 was mid-cycle (`N10:150/0` = `B3:0/4` = true) and was left alone; Z-33323 read idle on both, with the pump input `I:0/0` off. The other North presses time out on TCP from the VPN (ICMP answers). AbLegacy CLI from master, `ab://10.201.44.231/`, `-P MicroLogix`.
```
=== 10.201.44.231
ST14:10 Value: "Z-33323" Status: 0x00000000 (Good)
N10:150/0 Value: false Status: 0x00000000 (Good)
B3:0/4 Value: false Status: 0x00000000 (Good)
I:0/0 Value: <null> Status: 0x80050000 (BadCommunicationError)
I:0/1 Value: <null> Status: 0x80050000 (BadCommunicationError)
=== 10.201.44.249
ST14:10 Value: "Z-33470" Status: 0x00000000 (Good)
N10:150/0 Value: true Status: 0x00000000 (Good)
B3:0/4 Value: true Status: 0x00000000 (Good)
I:0/0 Value: <null> Status: 0x80050000 (BadCommunicationError)
I:0/1 Value: <null> Status: 0x80050000 (BadCommunicationError)
IDLE CHECK N10:150/0=false 0x00000000 (Good) B3:0/4=false 0x00000000 (Good)
S:0 Int | lib: <null> 0x80050000 (BadCommunicationError) | nat: 4 0x00000000 (Good)
S:57 Int | lib: <null> 0x80050000 (BadCommunicationError) | nat: 1400 0x00000000 (Good)
S:4 Int | lib: <null> 0x80050000 (BadCommunicationError) | nat: 9952 0x00000000 (Good)
N9:0 Int | lib: 2331 0x00000000 (Good) | nat: 2331 0x00000000 (Good)
N9:4 Int | lib: 3867 0x00000000 (Good) | nat: 3867 0x00000000 (Good)
N7:255 Int | lib: 32000 0x00000000 (Good) | nat: 32000 0x00000000 (Good)
F8:226 Float | lib: 2.1 0x00000000 (Good) | nat: 2.1 0x00000000 (Good)
F8:214 Float | lib: 200 0x00000000 (Good) | nat: 200 0x00000000 (Good)
B3:24 Int | lib: 1 0x00000000 (Good) | nat: 1 0x00000000 (Good)
B3:24/0 Bit | lib: true 0x00000000 (Good) | nat: true 0x00000000 (Good)
B3:28 Int | lib: 2309 0x00000000 (Good) | nat: 2309 0x00000000 (Good)
B3:28/8 Bit | lib: true 0x00000000 (Good) | nat: true 0x00000000 (Good)
B3:28/9 Bit | lib: false 0x00000000 (Good) | nat: false 0x00000000 (Good)
B3:132/14 Bit | lib: true 0x00000000 (Good) | nat: true 0x00000000 (Good)
T4:15.PRE Int | lib: 6000 0x00000000 (Good) | nat: 6000 0x00000000 (Good)
T4:15.ACC Int | lib: 6000 0x00000000 (Good) | nat: 6000 0x00000000 (Good)
T4:15.DN Bit | lib: true 0x00000000 (Good) | nat: true 0x00000000 (Good)
T4:93.PRE Int | lib: 7000 0x00000000 (Good) | nat: 7000 0x00000000 (Good)
T4:93.DN Bit | lib: false 0x00000000 (Good) | nat: false 0x00000000 (Good)
T4:14.PRE Int | lib: 3600 0x00000000 (Good) | nat: 3600 0x00000000 (Good)
T4:14.ACC Int | lib: 2922 0x00000000 (Good) | nat: 2922 0x00000000 (Good)
T4:11.PRE Int | lib: 32767 0x00000000 (Good) | nat: 32767 0x00000000 (Good)
T40:3.PRE Int | lib: 500 0x00000000 (Good) | nat: 500 0x00000000 (Good)
T40:12.PRE Int | lib: 500 0x00000000 (Good) | nat: 500 0x00000000 (Good)
C5:0.PRE Int | lib: 999 0x00000000 (Good) | nat: 999 0x00000000 (Good)
C5:0.ACC Int | lib: 111 0x00000000 (Good) | nat: 111 0x00000000 (Good)
C5:8.DN Bit | lib: true 0x00000000 (Good) | nat: true 0x00000000 (Good)
C5:1.PRE Int | lib: 32767 0x00000000 (Good) | nat: 32767 0x00000000 (Good)
R6:0.LEN Int | lib: 32 0x00000000 (Good) | nat: 32 0x00000000 (Good)
R6:0.POS Int | lib: 0 0x00000000 (Good) | nat: 0 0x00000000 (Good)
R6:0.DN Bit | lib: <null> 0x80050000 (BadCommunicationError) | nat: false 0x00000000 (Good)
ST14:10 String | lib: "Z-33323" 0x00000000 (Good) | nat: "Z-33323" 0x00000000 (Good)
ST14:0 String | lib: "EQ_TEST_LARGE_MOLD" 0x00000000 (Good) | nat: "EQ_TEST_LARGE_MOLD" 0x00000000 (Good)
O:0 Int | lib: <null> 0x80050000 (BadCommunicationError) | nat: 0 0x00000000 (Good)
O:0/3 Bit | lib: <null> 0x80050000 (BadCommunicationError) | nat: false 0x00000000 (Good)
I:0 Int | lib: <null> 0x80050000 (BadCommunicationError) | nat: -29376 0x00000000 (Good)
I:0/0 Bit | lib: <null> 0x80050000 (BadCommunicationError) | nat: false 0x00000000 (Good)
IDLE RECHECK N10:150/0=false 0x00000000 (Good)
pre: cycle=falseG pump=falseG
-- N7:23 write 4242: 0x00000000 (Good) -> N7:22=0G N7:23=4242G N7:24=0G
-- N7:23 restore 0: 0x00000000 (Good) -> N7:23=0G
-- F8:190 write 42.5: 0x00000000 (Good) -> F8:190=42.5G
-- F8:190 restore 0: 0x00000000 (Good) -> F8:190=0G
-- B3:200/5 set: 0x00000000 (Good) -> B3:200=32G (exp 32)
-- B3:200/3 set: 0x00000000 (Good) -> B3:200=40G (exp 40)
-- B3:200/5 clear: 0x00000000 (Good) -> B3:200=8G (exp 8)
-- B3:200/3 clear: 0x00000000 (Good) -> B3:200=0G (exp 0)
-- neighbours: B3:199=0G B3:201=0G
post: cycle=falseG pump=falseG N7:23=0G F8:190=0G B3:200=0G
```