fix(ablegacy): refuse every libplctag write the #659 translation changes (fix round 2)

Review M1: round 1 refused only the rewritten status bits, so S:/O:/I: writes - which
never worked on libplctag because tag creation failed - would have started reaching
the PLC (outputs, status-file words) on the default transport. WriteRefusal now keys
on LibplctagLegacyName.IsTranslated: file number supplied (S/O/I, incl. bit writes
like O:0/3, refused ahead of the parent-word RMW) or status bit rewritten. Reads
unaffected. Docs/comments now true; gate doc notes R bit-number wire evidence is
non-discriminating (L1). 12 new cases fail with the round-1 predicate.

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
Joseph Doherty
2026-10-09 05:43:50 -04:00
parent 7dbb060f92
commit 7ba86f7857
6 changed files with 88 additions and 45 deletions
+9 -7
View File
@@ -243,13 +243,15 @@ status file when a digit follows it), and its sub-element table has only `con`/`
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 is read-side only —
it does not widen the write surface:** before it, a libplctag write to an `R` status bit (or `C…UA`) could never reach
the PLC (tag creation failed), and `R6:0.CON/13` would otherwise turn it into a masked write to the control word. So
on libplctag a write to any status bit the translation rewrites (`R` `EN/EU/DN/EM/ER/UL/IN/FD`, `C` `UA`) is refused
**`BadNotSupported` before a runtime is created** — nothing sent, health unchanged
(`LibplctagLegacyTagRuntime.WriteRefusal`, asked through `IAbLegacyTagFactory.WriteRefusal`, the same plumbing as the
native refusal). T/C status bits libplctag parses itself keep their pre-existing write behaviour (note above).
transports (`S:4` is a free-running clock and differs read to read on each). **The translation is read-side only — it does not widen the write surface:** before it, libplctag could not create a
tag for `S:`/`O:`/`I:` or for an `R`/`C…UA` status bit, so no write to one ever reached the PLC. Now that the
translated names parse, a write would drive a physical output, a status-file word or (masked) a control word. So on
libplctag **every write whose address the translation changes** — `S:`, `O:`, `I:` (word and bit writes; a bit write
such as `O:0/3` is refused ahead of the parent-word read-modify-write) and the rewritten status bits (`R`
`EN/EU/DN/EM/ER/UL/IN/FD`, `C` `UA`) — is refused **`BadNotSupported` before a runtime is created**: nothing read or
sent, health unchanged (`LibplctagLegacyTagRuntime.WriteRefusal`, asked through `IAbLegacyTagFactory.WriteRefusal`, the
same plumbing as the native refusal). Those writes never worked on libplctag, so nothing is lost; the native
transport (opt-in) does write `S`/`O`/`I`. T/C status bits libplctag parses itself keep their pre-existing write behaviour (note above).
Not tested on hardware: L and A files (no press has them), function files, PLC-5, SLC, `logixpccc`.
Simulator evidence: