fix(ablegacy): translate S/O/I and R status bits for libplctag's PCCC parser (#659)

libplctag 2.6.0 (bundled by libplctag 1.5.2 / NativeImport 1.0.41) pccc.c needs an
explicit file number (parse_pccc_file_num; S is only the status file when a digit
follows) and has no R-file status-bit mnemonics (sub_element_lookup: con/len/pos only;
a /bit is refused on a 6-byte element). So the canonical S:0 (the default probe),
O:0/3, I:0/0 and R6:0.DN failed with BadCommunicationError on a real MicroLogix 1400
while native read them.

LibplctagLegacyName (libplctag adapter only) now emits S2:/O0:/I1: (the native
encoder's implicit file numbers) and R6:0.CON/<bit> for status bits libplctag lacks
(R EN/EU/DN/EM/ER/UL/IN/FD, C UA), bit numbers taken from the native table. The
authored grammar is unchanged. Unit tests pin the translation per family and check,
through a model of the pccc.c parser, that the translated name addresses exactly
what the native encoder does (46 fail without the fix). Live: Z-33323 idle, reads
only - libplctag now equals native for S/O/I/R status bits; controls unchanged.

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
Joseph Doherty
2026-10-09 05:34:09 -04:00
parent e0e8c4267a
commit 0abce36ca3
6 changed files with 404 additions and 14 deletions
+20 -9
View File
@@ -194,8 +194,8 @@ Native routing: SLC500/MicroLogix/PLC-5 are direct; `logixpccc` uses Unconnected
follow-up). PLC-5 writes are refused the same way. libplctag performs a masked bit write here; a native
read-modify-write of the control word would race the PLC updating TT/DN. Reads of status bits are unaffected.
- ⚠️ **On libplctag (the default) a status-bit write is NOT refused** — it goes to the controller as a write to the
timer/counter/control element (libplctag's bit-tag write; not exercised on a wire — `ab_server` has no T/C/R
files). Nothing in authoring prevents it: the address is free text,
timer/counter/control element (libplctag's masked bit-tag write; an `R` status bit is sent as `R<f>:<e>.CON/<bit>`
since #659; not exercised on a wire — `ab_server` has no T/C/R files). Nothing in authoring prevents it: the address is free text,
any `dataType` may be chosen (a status bit is naturally authored `Bit`), and `writable` defaults to `true`.
Author status-bit tags with `"writable": false` unless writing them is genuinely intended.
- **PLC-5 native is N-file reads only**; every other PLC-5 operation is refused with `BadNotSupported`.
@@ -216,25 +216,36 @@ before and after). Read pass, each address on libplctag then native (raw output
| Address(es) | libplctag | Native |
|---|---|---|
| `S:0` (default probe), `S:57` (= 1400), `S:4` | **BadCommunicationError** | Good (4, 1400, clock) |
| `S:0` (default probe), `S:57` (= 1400), `S:4` | Good, equal (4, 1400, clock) since #659; **BadCommunicationError** before | 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 |
| `R6:0.DN` (and `.EN`) | Good, equal since #659; **BadCommunicationError** before | 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 |
| `O:0`, `O:0/3`, `I:0`, `I:0/0`, `I:0/1`, `I:0/6`, `I:0/15` | Good, equal since #659; **BadCommunicationError** before | 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`.
**libplctag `S`/`O`/`I` and control status bits — fixed (#659).** Before the fix libplctag (the default) returned
`BadCommunicationError` for `S:`, `O:`, `I:` and `R6:0.DN`/`.EN` on this controller, so the default probe `S:0`
failed on real hardware. Root cause, from libplctag 2.6.0's `src/protocols/ab/pccc.c` (the core bundled by the
`libplctag` 1.5.2 package): `parse_pccc_file_num` requires a digit after the file letter (and `S` is only the
status file when a digit follows it), and its sub-element table has only `con`/`len`/`pos` for R files — a bare
`R6:0/13` is refused too, because a `/bit` is only allowed on a 2-byte element. The libplctag adapter
(`LibplctagLegacyName`) now translates the canonical address into the form that parser accepts — `S:n` → `S2:n`,
`O:n` → `O0:n`, `I:n` → `I1:n` (the same default file numbers the native encoder uses, on every family) and a status
bit libplctag lacks (`R` `EN/EU/DN/EM/ER/UL/IN/FD`, `C` `UA`) → `R6:0.CON/<bit>` (control word + bit, bit numbers
from the native table). The authored grammar is unchanged; `S2:0`/`O0:0`/`I1:0` stay rejected by the parser (one
spelling per address). Re-run on the same idle press 2026-10-09: every address above reads **identical** on both
transports (`S:4` is a free-running clock and differs read to read on each). ⚠️ The translation also means a
libplctag **write** to an `R` status bit now reaches the controller as libplctag's masked bit write (it used to fail
at tag creation) — the same behaviour T/C status bits always had on libplctag; see the status-bit write note above.
Not tested on hardware: L and A files (no press has them), function files, PLC-5, SLC, `logixpccc`.
Simulator evidence: