AbLegacy PCCC read/write fails against every ab_server family (BadCommunicationError) — path never validated against a simulator #534

Closed
opened 2026-08-01 02:18:12 -04:00 by dohertj2 · 1 comment
Owner

The AbLegacy PCCC read/write path fails against every ab_server PCCC family with BadCommunicationError (0x80050000). Found on 2026-08-01 while deploying the fixture.

Why this is new information. AbLegacyReadSmokeTests has never actually run against a simulator. /opt/otopcua-ablegacy/ did not exist on the Docker host — the stack was written but never deployed — and every ab_server service across BOTH ab fixtures bound :44818. So the AB CIP fixture's long-running ControlLogix container answered the AbLegacy suite's TCP reachability probe, the suite declined to skip, and it ran SLC500-mode PCCC against a ControlLogix. The 4 failures read as "fixture missing" and were dismissed as such (by me, in this session, before checking).

Fixture side is now fixed (branch fix/gitea-532-533-roles-and-string-identity): each family gets its own host port — slc500 :44819, micrologix :44820, plc5 :44821 — all three deployed and running alongside AbCip's ControlLogix on :44818, and the skip guard is per-family so it can no longer mistake a foreign simulator for its own.

What remains, and it is a real finding. With the suite now pointed at correct-family simulators, all 4 tests still fail:

  • Driver_reads_seeded_N_file_from_ab_server_PCCC — all three families, N7:0 read
  • Slc500_write_then_read_round_trip_on_N7_scratch_register — N7:5 write

Reproduced outside the test harness via the driver CLI, so it is not a test-fixture artifact:

dotnet otopcua-ablegacy-cli.dll read -a N7:0 -g "ab://10.100.0.35:44819/1,0" -P Slc500
  Status: 0x80050000 (BadCommunicationError)

Permutations tried, all identical:

Server --path Client cip-path Result
absent 1,0 BadCommunicationError
absent empty BadCommunicationError
1,0 1,0 BadCommunicationError
1,0 empty BadCommunicationError

Notes for whoever picks this up:

  • ab_server logs nothing on success either — I checked against a passing AB CIP read. Log silence proves nothing here; don't read it as "the connection never arrived", as I initially did.
  • Failure returns in ~80 ms, far too fast for a connect timeout, so it is likely rejected at session/tag setup rather than at the read.
  • AbLegacyStatusMapper maps BadCommunicationError from seven distinct libplctag statuses and from its _ => default, so the mapped code does not identify the cause. Getting the raw Status out of LibplctagLegacyTagRuntime.GetStatus() is the obvious first step.
  • The container image ships only ab_server, no tag_rw, so libplctag's own reference client is not available to separate "our driver" from "libplctag ↔ ab_server PCCC" without building it.
  • Open question worth answering first: does libplctag's PCCC emulation work against ab_server at all? If not, this is a fixture-capability limit and the tests should be re-pointed or dropped, not "fixed".

Leaving the 4 tests red rather than skipped on purpose: they are now correctly aimed at a live simulator of the right family, so the failure is a true signal about the driver, and making it skip would hide it.

The AbLegacy PCCC read/write path fails against every ab_server PCCC family with `BadCommunicationError` (`0x80050000`). Found on 2026-08-01 while deploying the fixture. **Why this is new information.** `AbLegacyReadSmokeTests` has never actually run against a simulator. `/opt/otopcua-ablegacy/` did not exist on the Docker host — the stack was written but never deployed — and every ab_server service across BOTH ab fixtures bound `:44818`. So the AB CIP fixture's long-running ControlLogix container answered the AbLegacy suite's TCP reachability probe, the suite declined to skip, and it ran SLC500-mode PCCC against a ControlLogix. The 4 failures read as "fixture missing" and were dismissed as such (by me, in this session, before checking). **Fixture side is now fixed** (branch `fix/gitea-532-533-roles-and-string-identity`): each family gets its own host port — slc500 `:44819`, micrologix `:44820`, plc5 `:44821` — all three deployed and running alongside AbCip's ControlLogix on `:44818`, and the skip guard is per-family so it can no longer mistake a foreign simulator for its own. **What remains, and it is a real finding.** With the suite now pointed at correct-family simulators, all 4 tests still fail: - `Driver_reads_seeded_N_file_from_ab_server_PCCC` — all three families, `N7:0` read - `Slc500_write_then_read_round_trip_on_N7_scratch_register` — `N7:5` write Reproduced outside the test harness via the driver CLI, so it is not a test-fixture artifact: ``` dotnet otopcua-ablegacy-cli.dll read -a N7:0 -g "ab://10.100.0.35:44819/1,0" -P Slc500 Status: 0x80050000 (BadCommunicationError) ``` Permutations tried, all identical: | Server `--path` | Client cip-path | Result | |---|---|---| | absent | `1,0` | BadCommunicationError | | absent | empty | BadCommunicationError | | `1,0` | `1,0` | BadCommunicationError | | `1,0` | empty | BadCommunicationError | Notes for whoever picks this up: - **ab_server logs nothing on success either** — I checked against a *passing* AB CIP read. Log silence proves nothing here; don't read it as "the connection never arrived", as I initially did. - Failure returns in ~80 ms, far too fast for a connect timeout, so it is likely rejected at session/tag setup rather than at the read. - `AbLegacyStatusMapper` maps `BadCommunicationError` from seven distinct libplctag statuses **and** from its `_ =>` default, so the mapped code does not identify the cause. Getting the raw `Status` out of `LibplctagLegacyTagRuntime.GetStatus()` is the obvious first step. - The container image ships only `ab_server`, no `tag_rw`, so libplctag's own reference client is not available to separate "our driver" from "libplctag ↔ ab_server PCCC" without building it. - Open question worth answering first: **does libplctag's PCCC emulation work against ab_server at all?** If not, this is a fixture-capability limit and the tests should be re-pointed or dropped, not "fixed". Leaving the 4 tests **red rather than skipped** on purpose: they are now correctly aimed at a live simulator of the right family, so the failure is a true signal about the driver, and making it skip would hide it.
Author
Owner

Root-caused and fixed in 01538464. It was our bug, in two drivers, and it is a production defect — not a fixture or protocol limitation.

Port was carried and consumed by nobody. AbLegacyTagCreateParams / AbCipTagCreateParams both have a Port field; both runtimes set Tag.Gateway = p.Gateway — the bare host. libplctag does not fail on a portless gateway, it substitutes 44818. So an authored port was not ignored in the harmless sense: the connection was silently redirected to whatever answers on the default port. On a plant network that is another PLC, and the driver would read its tags and report them Good.

That explains every observation in this issue. After each PCCC family moved to its own port, the driver still dialled 44818 and kept talking to the AB CIP fixture's ControlLogix container — so the four path permutations in the table above were never reaching a PCCC server at all. The --path axis was a red herring; I was varying the wrong variable.

The open question is answered: libplctag PCCC works fine against ab_server. A direct libplctag 1.5.2 probe outside the repo:

CIP ControlLogix path=1,0 TestDINT   status=Ok    (positive control, :44818)
SLC500     path="1,0"  N7:0          status=Ok    (:44819)
SLC500     path=null   N7:0          status=Ok
SLC500     path=""     N7:0          THREW ErrorBadData
MicroLogix path=null / "1,0"  N7:0   status=Ok    (:44820)
Plc5       path=null / "1,0"  N7:0   status=Ok    (:44821)

Since the library reached the simulators and the driver could not, the difference had to be in our Tag construction, and Gateway was the only field that differed. The same probe confirmed libplctag accepts host:port in the gateway attribute, which is what the fix emits.

(Note the one genuine trap the probe surfaced: Path = "" throws ErrorBadData where Path = null succeeds. Not hit today — AbLegacyDriver.EffectiveCipPath falls back to the family default when the parsed path is empty — but a real edge if that fallback is ever removed.)

AbCip is fixed alongside, and had never been observable there. Every AbCip fixture profile binds 44818, so no test that driver has could distinguish "uses the authored port" from "always uses 44818". Worth keeping as a shape: a fixture that only ever exercises the default value cannot falsify a rule about non-default values, however many tests point at it.

Result: AbLegacy integration 4/4 green — having never passed once before. AbCip integration 10/13 (3 pre-existing skips), AbCip unit 345, AbLegacy unit 219.

The load-bearing guard is now the integration suite itself: it addresses each family on a non-default port, so it cannot pass unless the port is honoured. Unit tests in both drivers pin the composition for machines without fixtures. Falsified both directions — reverting the runtime line reddens all 4 integration tests; dropping the port > 0 guard reddens exactly the two fallback cases.

Root-caused and fixed in `01538464`. **It was our bug, in two drivers, and it is a production defect — not a fixture or protocol limitation.** **`Port` was carried and consumed by nobody.** `AbLegacyTagCreateParams` / `AbCipTagCreateParams` both have a `Port` field; both runtimes set `Tag.Gateway = p.Gateway` — the bare host. libplctag does not fail on a portless gateway, it **substitutes 44818**. So an authored port was not ignored in the harmless sense: the connection was silently *redirected* to whatever answers on the default port. On a plant network that is another PLC, and the driver would read its tags and report them `Good`. That explains every observation in this issue. After each PCCC family moved to its own port, the driver still dialled 44818 and kept talking to the AB CIP fixture's ControlLogix container — so the four path permutations in the table above were never reaching a PCCC server at all. The `--path` axis was a red herring; I was varying the wrong variable. **The open question is answered: libplctag PCCC works fine against ab_server.** A direct libplctag 1.5.2 probe outside the repo: ``` CIP ControlLogix path=1,0 TestDINT status=Ok (positive control, :44818) SLC500 path="1,0" N7:0 status=Ok (:44819) SLC500 path=null N7:0 status=Ok SLC500 path="" N7:0 THREW ErrorBadData MicroLogix path=null / "1,0" N7:0 status=Ok (:44820) Plc5 path=null / "1,0" N7:0 status=Ok (:44821) ``` Since the library reached the simulators and the driver could not, the difference had to be in our `Tag` construction, and `Gateway` was the only field that differed. The same probe confirmed libplctag accepts `host:port` in the gateway attribute, which is what the fix emits. (Note the one genuine trap the probe surfaced: `Path = ""` throws `ErrorBadData` where `Path = null` succeeds. Not hit today — `AbLegacyDriver.EffectiveCipPath` falls back to the family default when the parsed path is empty — but a real edge if that fallback is ever removed.) **AbCip is fixed alongside, and had never been observable there.** Every AbCip fixture profile binds 44818, so no test that driver has could distinguish "uses the authored port" from "always uses 44818". Worth keeping as a shape: a fixture that only ever exercises the default value cannot falsify a rule about non-default values, however many tests point at it. **Result:** AbLegacy integration **4/4 green — having never passed once before**. AbCip integration 10/13 (3 pre-existing skips), AbCip unit 345, AbLegacy unit 219. The load-bearing guard is now the integration suite itself: it addresses each family on a *non-default* port, so it cannot pass unless the port is honoured. Unit tests in both drivers pin the composition for machines without fixtures. Falsified both directions — reverting the runtime line reddens all 4 integration tests; dropping the `port > 0` guard reddens exactly the two fallback cases.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dohertj2/lmxopcua#534