Task #222 partial — unblock AB Legacy PCCC via cip-path workaround (5/5 stages) #223

Merged
dohertj2 merged 1 commits from task-222-ablegacy-pccc-unblock into v2 2026-04-21 12:53:01 -04:00
Owner

The README's "ab_server PCCC dispatcher is upstream-broken" claim turned out to be wrong. Actual root cause: libplctag's ab_server rejects empty CIP routing paths at the unconnected-send layer before forwarding to the PCCC dispatcher. pccc.c itself (merged upstream in 2020 via libplctag PR #175) actually works — it just never gets called with an empty path.

The demo

$ dotnet run --project src/.../Driver.AbLegacy.Cli -- read -g "ab://127.0.0.1:44818/" -P Slc500 -a "N7:0" -t Int
Status: 0x80050000 (BadCommunicationError)    ← what the README described

$ dotnet run --project src/.../Driver.AbLegacy.Cli -- read -g "ab://127.0.0.1/1,0" -P Slc500 -a "N7:0" -t Int
Status: 0x00000000 (Good)                      ← what actually works

The /1,0 path is port-1/slot-0 (conventional ControlLogix backplane route) — any well-formed path passes ab_server's gate. Real SLC/PLC-5 hardware has no backplane and accepts empty, so operators targeting real PLCs override via env var.

Changes

Fixture:

  • AbLegacyServerFixture.cs — drop AB_LEGACY_TRUST_WIRE; add AB_LEGACY_CIP_PATH (default 1,0, set = empty for real hardware). Expose CipPath property.
  • AbLegacyReadSmokeTests.cs — use sim.CipPath; new AB_LEGACY_COMPOSE_PROFILE filter (only one container binds :44818 at a time, so the parametric theory runs only the matching family).

Scripts + seed:

  • scripts/e2e/test-ablegacy.ps1 — drop the TRUST_WIRE skip gate.
  • scripts/e2e/e2e-config.sample.json — default gateway flipped from 192.168.1.10 placeholder to 127.0.0.1/1,0 (Docker fixture).
  • scripts/e2e/README.md — AB Legacy row: SKIP → PASS.
  • scripts/smoke/seed-ablegacy-smoke.sql — default HostAddress points at the Docker fixture.

Docs:

  • tests/.../Docker/README.md — "Known limitations" section rewritten from "upstream-broken" to the cip-path explanation; env-var table updated.
  • docs/drivers/AbLegacy-Test-Fixture.md, docs/drivers/README.md, docs/DriverClis.md — status flipped; residual bit-write gap documented.

Verified

  • Full-solution build: 0 errors.
  • Integration suite per-profile (SLC500 / MicroLogix / PLC-5) — all green with AB_LEGACY_COMPOSE_PROFILE set to the running container.
  • CLI round-trip verified for N / F / L file types.

Residual gap (not fixed — documented)

B3:0/5-style bit-file writes surface 0x803D0000 against ab_server --plc=SLC500 (bit reads work). Real hardware / RSEmulate 500 path for bit-write fidelity. Documented in Docker/README.md §"Known limitations".

Test plan

  • Modbus / AB CIP / S7 unchanged — 5/5 each (regression check during e2e run)
  • AB Legacy e2e CLI commands: SLC500 N/F/L round-trip ✓, MicroLogix N round-trip ✓, PLC-5 N round-trip ✓
  • dotnet test tests/.../AbLegacy.IntegrationTests per-profile: all pass
  • Full-solution build: 0 errors
The README's "ab_server PCCC dispatcher is upstream-broken" claim turned out to be wrong. Actual root cause: libplctag's `ab_server` rejects empty CIP routing paths at the unconnected-send layer *before* forwarding to the PCCC dispatcher. `pccc.c` itself (merged upstream in 2020 via libplctag PR #175) actually works — it just never gets called with an empty path. ## The demo ``` $ dotnet run --project src/.../Driver.AbLegacy.Cli -- read -g "ab://127.0.0.1:44818/" -P Slc500 -a "N7:0" -t Int Status: 0x80050000 (BadCommunicationError) ← what the README described $ dotnet run --project src/.../Driver.AbLegacy.Cli -- read -g "ab://127.0.0.1/1,0" -P Slc500 -a "N7:0" -t Int Status: 0x00000000 (Good) ← what actually works ``` The `/1,0` path is port-1/slot-0 (conventional ControlLogix backplane route) — any well-formed path passes ab_server's gate. Real SLC/PLC-5 hardware has no backplane and accepts empty, so operators targeting real PLCs override via env var. ## Changes **Fixture:** - `AbLegacyServerFixture.cs` — drop `AB_LEGACY_TRUST_WIRE`; add `AB_LEGACY_CIP_PATH` (default `1,0`, set `=` empty for real hardware). Expose `CipPath` property. - `AbLegacyReadSmokeTests.cs` — use `sim.CipPath`; new `AB_LEGACY_COMPOSE_PROFILE` filter (only one container binds `:44818` at a time, so the parametric theory runs only the matching family). **Scripts + seed:** - `scripts/e2e/test-ablegacy.ps1` — drop the TRUST_WIRE skip gate. - `scripts/e2e/e2e-config.sample.json` — default gateway flipped from `192.168.1.10` placeholder to `127.0.0.1/1,0` (Docker fixture). - `scripts/e2e/README.md` — AB Legacy row: SKIP → PASS. - `scripts/smoke/seed-ablegacy-smoke.sql` — default HostAddress points at the Docker fixture. **Docs:** - `tests/.../Docker/README.md` — "Known limitations" section rewritten from "upstream-broken" to the cip-path explanation; env-var table updated. - `docs/drivers/AbLegacy-Test-Fixture.md`, `docs/drivers/README.md`, `docs/DriverClis.md` — status flipped; residual bit-write gap documented. ## Verified - Full-solution build: 0 errors. - Integration suite per-profile (SLC500 / MicroLogix / PLC-5) — all green with `AB_LEGACY_COMPOSE_PROFILE` set to the running container. - CLI round-trip verified for N / F / L file types. ## Residual gap (not fixed — documented) `B3:0/5`-style bit-file writes surface `0x803D0000` against `ab_server --plc=SLC500` (bit reads work). Real hardware / RSEmulate 500 path for bit-write fidelity. Documented in Docker/README.md §"Known limitations". ## Test plan - [x] Modbus / AB CIP / S7 unchanged — 5/5 each (regression check during e2e run) - [x] AB Legacy e2e CLI commands: SLC500 N/F/L round-trip ✓, MicroLogix N round-trip ✓, PLC-5 N round-trip ✓ - [x] `dotnet test tests/.../AbLegacy.IntegrationTests` per-profile: all pass - [x] Full-solution build: 0 errors
dohertj2 added 1 commit 2026-04-21 12:40:58 -04:00
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>
dohertj2 merged commit 05d2a7fd00 into v2 2026-04-21 12:53:01 -04:00
dohertj2 deleted branch task-222-ablegacy-pccc-unblock 2026-04-21 12:53:02 -04:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dohertj2/lmxopcua#223