Task #222 partial — unblock AB Legacy PCCC via cip-path workaround (5/5 stages)
Replaced the "ab_server PCCC upstream-broken" skip gate with the actual
root cause: libplctag's ab_server rejects empty CIP routing paths at the
unconnected-send layer before the PCCC dispatcher runs. Real SLC/
MicroLogix/PLC-5 hardware accepts empty paths (no backplane); ab_server
does not. With `/1,0` in place, N (Int16), F (Float32), and L (Int32)
file reads + writes round-trip cleanly across all three compose profiles.
## Fixture changes
- `AbLegacyServerFixture.cs`:
- Drop `AB_LEGACY_TRUST_WIRE` env var + the reachable-but-untrusted
skip branch. Fixture now only skips on TCP unreachability.
- Add `AB_LEGACY_CIP_PATH` env var (default `1,0`) + expose `CipPath`
property. Set `AB_LEGACY_CIP_PATH=` (empty) against real hardware.
- Shorter skip messages on the `[AbLegacyFact]` / `[AbLegacyTheory]`
attributes — one reason: endpoint not reachable.
- `AbLegacyReadSmokeTests.cs`:
- Device URI built from `sim.CipPath` instead of hardcoded empty path.
- New `AB_LEGACY_COMPOSE_PROFILE` env var filters the parametric
theory to the running container's family. Only one container binds
`:44818` at a time, so cross-family params would otherwise fail.
- `Slc500_write_then_read_round_trip` skips cleanly when the running
profile isn't `slc500`.
## E2E + seed + docs
- `scripts/e2e/test-ablegacy.ps1` — drop the `AB_LEGACY_TRUST_WIRE`
skip gate; synopsis calls out the `/1,0` vs empty cip-path split
between the Docker fixture and real hardware.
- `scripts/e2e/e2e-config.sample.json` — sample gateway flipped from
the hardware placeholder (`192.168.1.10`) to the Docker fixture
(`127.0.0.1/1,0`); comment rewritten.
- `scripts/e2e/README.md` — AB Legacy expected-matrix row goes from
SKIP to PASS.
- `scripts/smoke/seed-ablegacy-smoke.sql` — default HostAddress points
at the Docker fixture + header / footer text reflect the new state.
- `tests/.../Docker/README.md` — "Known limitations" section rewritten
to describe the cip-path gate (not a dispatcher gap); env-var table
picks up `AB_LEGACY_CIP_PATH` + `AB_LEGACY_COMPOSE_PROFILE`.
- `docs/drivers/AbLegacy-Test-Fixture.md` + `docs/drivers/README.md`
+ `docs/DriverClis.md` — flip status from blocked to functional;
residual bit-file-write gap (B3:0/5 → 0x803D0000) documented.
## Residual gap
Bit-file writes (`B3:0/5` style) surface `0x803D0000` against
`ab_server --plc=SLC500`; bit reads work. Non-blocking for smoke
coverage — N/F/L round-trip is enough. Real hardware / RSEmulate 500
for bit-write fidelity. Documented in `Docker/README.md` §"Known
limitations" + the `AbLegacy-Test-Fixture.md` follow-ups list.
## Verified
- Full-solution build: 0 errors, 334 pre-existing warnings.
- Integration suite passes per-profile with
`AB_LEGACY_COMPOSE_PROFILE=<slc500|micrologix|plc5>` + matching
compose container up.
- All four non-hardware e2e scripts (Modbus / AB CIP / AB Legacy / S7)
now 5/5 against the respective docker-compose fixtures.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -52,31 +52,33 @@ families stop the current service + start another.
|
||||
| Var | Default | Purpose |
|
||||
|---|---|---|
|
||||
| `AB_LEGACY_ENDPOINT` | `localhost:44818` | `host:port` of the PCCC endpoint. |
|
||||
| `AB_LEGACY_TRUST_WIRE` | *unset* | Opt-in promise that the endpoint is a real PLC or RSEmulate 500 golden-box (not ab_server). Required for integration tests to actually run; without it the tests skip with an upstream-gap message even when TCP reaches a listener. See the **Known limitations** section below. |
|
||||
| `AB_LEGACY_CIP_PATH` | `1,0` | CIP routing path portion of the `ab://host:port/<path>` URI. ab_server rejects empty paths at the CIP unconnected-send layer; real SLC/MicroLogix/PLC-5 hardware accepts empty (no backplane). Set to empty (`AB_LEGACY_CIP_PATH=`) when pointing at real hardware. |
|
||||
| `AB_LEGACY_COMPOSE_PROFILE` | *unset* | When set (e.g. `slc500`), the parametric theory filters to that profile. Only one compose container binds `:44818` at a time; set this to the profile currently up so the suite doesn't try to hit e.g. the Slc500 family against the PLC-5 container. Leave unset for real-hardware runs (all 3 families parameterize). |
|
||||
|
||||
## Run the integration tests
|
||||
|
||||
In a separate shell with a container up:
|
||||
In a separate shell with a container up, tell the suite which profile is
|
||||
running so only the matching theory-parameterization executes:
|
||||
|
||||
```powershell
|
||||
cd C:\Users\dohertj2\Desktop\lmxopcua
|
||||
$env:AB_LEGACY_COMPOSE_PROFILE = "slc500" # or "micrologix" / "plc5"
|
||||
dotnet test tests\ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.IntegrationTests
|
||||
```
|
||||
|
||||
Against the Docker ab_server the suite **skips** with a pointer to the
|
||||
upstream gap (see **Known limitations**). Against real SLC / MicroLogix /
|
||||
PLC-5 hardware or a RSEmulate 500 box:
|
||||
Against real SLC / MicroLogix / PLC-5 hardware, set the endpoint + an
|
||||
empty cip-path + leave the profile unset so all 3 parameterizations
|
||||
run (real PLCs answer any valid family):
|
||||
|
||||
```powershell
|
||||
$env:AB_LEGACY_ENDPOINT = "10.0.1.50:44818"
|
||||
$env:AB_LEGACY_TRUST_WIRE = "1"
|
||||
$env:AB_LEGACY_CIP_PATH = "" # empty — real hardware has no backplane
|
||||
dotnet test tests\ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.IntegrationTests
|
||||
```
|
||||
|
||||
`AbLegacyServerFixture` TCP-probes the endpoint at collection init and sets
|
||||
a skip reason that captures **both** cases: unreachable endpoint *and*
|
||||
reachable-but-wire-untrusted. Tests use `[AbLegacyFact]` / `[AbLegacyTheory]`
|
||||
which check the same gate.
|
||||
`AbLegacyServerFixture` TCP-probes the endpoint at collection init and
|
||||
sets a skip reason when the listener isn't reachable. Tests use
|
||||
`[AbLegacyFact]` / `[AbLegacyTheory]` which check the same gate.
|
||||
|
||||
## What each family seeds
|
||||
|
||||
@@ -97,41 +99,31 @@ implies type:
|
||||
|
||||
## Known limitations
|
||||
|
||||
### ab_server PCCC dispatcher (confirmed upstream gap, verified 2026-04-21)
|
||||
### ab_server rejects empty CIP paths
|
||||
|
||||
**ab_server accepts TCP at `:44818` but its PCCC dispatcher is not
|
||||
functional.** Running with `--plc=SLC500 --debug=5` shows no request
|
||||
logs when libplctag issues a read, and every read surfaces as
|
||||
`BadCommunicationError` (libplctag status `0x80050000`). This matches
|
||||
the libplctag docs' description of PCCC support as less-mature than
|
||||
CIP in the bundled `ab_server` tool.
|
||||
libplctag's `ab_server` enforces a non-empty CIP routing path at the
|
||||
unconnected-send layer before forwarding to the PCCC dispatcher; a
|
||||
client-side `ab://host:port/` with nothing after the `/` surfaces as
|
||||
`BadCommunicationError` (`0x80050000`) with no server-side log line.
|
||||
|
||||
**Fixture behavior.** To avoid a loud row of failing tests on the
|
||||
integration host every time someone `docker compose up`s the SLC500
|
||||
profile, `AbLegacyServerFixture` gates on a second env var
|
||||
`AB_LEGACY_TRUST_WIRE`. The matrix:
|
||||
Real SLC/PLC-5 hardware has no backplane routing, so an empty path is
|
||||
how field devices are addressed. The fixture defaults to `/1,0`
|
||||
(port-1/slot-0 — the conventional ControlLogix backplane path) which
|
||||
the ab_server accepts; operators targeting real hardware set
|
||||
`AB_LEGACY_CIP_PATH=` (empty) to exercise authentic wire semantics.
|
||||
|
||||
| Endpoint reachable? | `AB_LEGACY_TRUST_WIRE` set? | Result |
|
||||
|---|---|---|
|
||||
| No | — | Skip ("not reachable") |
|
||||
| Yes | No | **Skip ("ab_server PCCC gap")** |
|
||||
| Yes | `1` or `true` | **Run** |
|
||||
Previous versions of this README described PCCC as "upstream-broken" —
|
||||
the root cause turned out to be the cip-path gate above, not a gap in
|
||||
`pccc.c`. N-file (Int16), F-file (Float32), and L-file (Int32) round-
|
||||
trip cleanly across SLC500, MicroLogix, and PLC-5 modes.
|
||||
|
||||
The test bodies themselves are correct for real hardware — point
|
||||
`AB_LEGACY_ENDPOINT` at a real SLC 5/05 / MicroLogix 1100/1400 /
|
||||
PLC-5, set `AB_LEGACY_TRUST_WIRE=1`, and the smoke tests round-trip
|
||||
cleanly.
|
||||
### Bit-file writes on ab_server
|
||||
|
||||
Resolution paths (pick one):
|
||||
|
||||
1. **File an ab_server bug** in `libplctag/libplctag` to expand PCCC
|
||||
server-side coverage.
|
||||
2. **Golden-box tier** via Rockwell RSEmulate 500 — closer to real
|
||||
firmware, but license-gated + RSLinx-dependent. Set
|
||||
`AB_LEGACY_TRUST_WIRE=1` when the endpoint points at an Emulate
|
||||
box.
|
||||
3. **Lab rig** — used SLC 5/05 / MicroLogix 1100 on a dedicated
|
||||
network (task #222); the authoritative path.
|
||||
`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.
|
||||
|
||||
### Other known gaps (unchanged from ab_server)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user