feat(abcip): #611 discovered UDT member writes + whole-UDT refusal by decision
Item 2 — controller-discovered UDT members are writable on both transports once browse-committed and authored ReadWrite (writability is the operator's: the commit lands AccessLevel Read with no writable key). The write names the member (one CIP symbolic segment per member), its own type, its own bytes. BIT members are supported: Z-35162 (read-only probe) exposes a UDT BIT member as a 1-byte 0xC1 BOOL, so the controller applies the bit and the hidden host's other bits are never in the request. - AbCipDriver: pre-write width check on BOTH transports (libplctag patched part of its read buffer and sent it all): BadTypeMismatch, Bool over a wider value BadNotSupported; nothing sent, handle kept, health untouched, warn once. - A runtime NotSupportedException is a refusal: no longer latches Degraded. Item 3 — whole-UDT writes refused BadNotSupported before any runtime exists, by decision (a structure write overwrites hidden hosts + padding), documented. ab_server patch 0003: serve member paths from dotted flat tags (UdtSeg.*), and accept libplctag's pad byte on 1-byte writes (stock ab_server refused every libplctag BOOL/SINT write with 0x03). New AbCipUdtMemberWriteTests (both transports), AbCipDiscoveredMemberWriteTests, native request-byte theory, and a browse-commit mapper pin. Docs: AbCip.md Member writes / Whole-UDT writes / Known gaps, fixture docs, CLAUDE.md AB section. Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
@@ -43,16 +43,20 @@ libplctag passed on all seven profiles; on `micro800` libplctag failed on the em
|
||||
smoke test passes on both transports. Evidence:
|
||||
[native-ab-transport-gate-results](../plans/2026-10-07-native-ab-transport-gate-results.md).
|
||||
|
||||
- Write-capable tests (the Logix Emulate ALMD test) require `AB_SERVER_ALLOW_WRITES=1` and refuse while
|
||||
`AB_DIFF_ENDPOINT` is set.
|
||||
- Write-capable tests (the Logix Emulate ALMD test, and `AbCipUdtMemberWriteTests` — #611 discovered-member
|
||||
writes, both transports, against the `UdtSeg.*` member tags of the `controllogix` profile) require
|
||||
`AB_SERVER_ALLOW_WRITES=1` and refuse while `AB_DIFF_ENDPOINT` is set.
|
||||
- **Differential harness** (`AbCip.IntegrationTests/Differential`, `LiveIntegration`) compares libplctag and
|
||||
native on browse, template and read parity against one endpoint. It is read-only by construction, aborts on
|
||||
the first connection failure, never retries, and caps the native phase at one Forward Open. Env:
|
||||
`AB_DIFF_ENDPOINT`, `AB_DIFF_PATH`, `AB_DIFF_FAMILY`, `AB_DIFF_TAGS`, `AB_DIFF_MAX_TAGS` (at most 500),
|
||||
`AB_DIFF_MAX_TEMPLATES`, `AB_DIFF_PACE_MS` (default 20), `AB_DIFF_COUNT_OPENS`. Always run with
|
||||
`--filter "FullyQualifiedName~Differential"` and `AB_SERVER_ENDPOINT=127.0.0.1:1`.
|
||||
- ab_server serves no UDTs and refuses 1-byte writes, so template parity is vacuous there and BOOL/SINT
|
||||
parity is weak evidence. Native writes remain simulator-only.
|
||||
- ab_server serves no UDTs, so template parity is vacuous there. Docker patch 0003 serves a UDT **member
|
||||
path** from a flat tag named by that dotted path (`UdtSeg.ID`, `UdtSeg.Sub.Code`, `UdtSeg.Points[2]`) — the
|
||||
wire of a member read/write, not a structure — and accepts the pad byte libplctag adds to a 1-byte write,
|
||||
which stock ab_server refused (`0x03`) for every libplctag BOOL/SINT write. Native writes remain
|
||||
simulator-only.
|
||||
- **Golden-frame capture:** `scripts/ab-capture/capture.sh` (capture client in `scripts/ab-capture/client`);
|
||||
fixtures live in `tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/` with a README.
|
||||
|
||||
@@ -76,7 +80,9 @@ ControlLogix profile `Notes`: *"ab_server lacks full UDT emulation."*
|
||||
|
||||
Unverified against `ab_server`:
|
||||
|
||||
- PR 6 structured read/write (`AbCipStructureMember` fan-out)
|
||||
- PR 6 structured read/write (`AbCipStructureMember` fan-out) — except the **member-path wire** of a
|
||||
write, which patch 0003 serves (`AbCipUdtMemberWriteTests`, #611); offsets and BIT-member semantics are
|
||||
not emulated
|
||||
- #179 Template Object shape reader (`CipTemplateObjectDecoder` + `FetchUdtShapeAsync`)
|
||||
- #194 whole-UDT read optimization (`AbCipUdtReadPlanner` +
|
||||
`AbCipUdtMemberLayout` + the `ReadGroupAsync` path in `AbCipDriver`)
|
||||
|
||||
Reference in New Issue
Block a user