b13bdc154a
The AbLegacy integration suite has never 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 across BOTH ab fixtures bound :44818. So the AB CIP fixture's ControlLogix container (up for two weeks) answered this suite's TCP reachability probe, the suite declined to skip, and it ran SLC500-mode PCCC against a ControlLogix. Four failures that read as "fixture missing". Two structural problems, both fixed here: - The theory parameterises over all three PCCC families, but only one container could ever exist on the shared port, so at least two cases failed on every conceivable run. The suite was un-greenable by construction, and AB_LEGACY_COMPOSE_PROFILE existed to paper over it by filtering the theory down to whichever container happened to be up. - A reachability probe that cannot tell one simulator from another is not a skip gate. It answered "is some ab_server listening", which is a different question from the one the caller is asking, and reported the difference as a test failure. Each family now has its own host port — 44819 slc500, 44820 micrologix, 44821 plc5 — so all three run together and alongside AbCip on 44818 (ab_server still listens on 44818 inside each container; only the host mapping differs). Skip is per-family via SkipReasonFor(profile); the class-level gate runs if ANY family is up so one down container cannot silently skip the other two. AB_LEGACY_COMPOSE_PROFILE now applies only in AB_LEGACY_ENDPOINT-pinned real-hardware mode, where the family genuinely is a property of the one device. This finally makes true what the fixture's XML doc has claimed from the start — that it and the AB CIP fixture can point at different containers simultaneously. Deployed to /opt/otopcua-ablegacy/ on 10.100.0.35; all three containers running. The 4 tests still FAIL, and are left red on purpose — see #534. Now aimed at correct-family simulators, the PCCC exchange itself returns BadCommunicationError for every server-path / client-cip-path permutation, reproduced outside the test harness via the driver CLI. That is a true signal about the driver (or about libplctag's PCCC support against ab_server) that skipping would hide. What changed here is that the failure is now diagnostic instead of an artifact of a port collision. Claude-Session: https://claude.ai/code/session_015p7wGqy3YpZNCpDzTpGMKo
152 lines
7.2 KiB
Markdown
152 lines
7.2 KiB
Markdown
# AB Legacy PCCC integration-test fixture — `ab_server` (Docker)
|
|
|
|
[libplctag](https://github.com/libplctag/libplctag)'s `ab_server` supports
|
|
both CIP (ControlLogix / CompactLogix / Micro800) and PCCC (SLC 500 /
|
|
MicroLogix / PLC-5) families from one binary. This fixture reuses the AB
|
|
CIP Docker image (`otopcua-ab-server:libplctag-release`) with different
|
|
`--plc` flags. No new Dockerfile needed — the compose file's `build:`
|
|
block points at the AB CIP `Docker/` folder so `docker compose build`
|
|
from here reuses the same multi-stage build.
|
|
|
|
**Docker is the only supported launch path**; a fresh clone needs Docker
|
|
Desktop and nothing else.
|
|
|
|
| File | Purpose |
|
|
|---|---|
|
|
| [`docker-compose.yml`](docker-compose.yml) | Three per-family services — `slc500` on `:44819`, `micrologix` on `:44820`, `plc5` on `:44821`. Run them together; they no longer collide with each other or with the AB CIP fixture on `:44818`. |
|
|
|
|
## Run
|
|
|
|
From the repo root:
|
|
|
|
```powershell
|
|
# SLC 500 family — widest PCCC coverage
|
|
docker compose -f tests\Drivers\ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.IntegrationTests\Docker\docker-compose.yml --profile slc500 up
|
|
|
|
# Per-family
|
|
docker compose -f tests\...\Docker\docker-compose.yml --profile micrologix up
|
|
docker compose -f tests\...\Docker\docker-compose.yml --profile plc5 up
|
|
```
|
|
|
|
Detached + stop:
|
|
|
|
```powershell
|
|
docker compose -f tests\...\Docker\docker-compose.yml --profile slc500 up -d
|
|
docker compose -f tests\...\Docker\docker-compose.yml --profile slc500 down
|
|
```
|
|
|
|
First run builds the `otopcua-ab-server:libplctag-release` image (~3-5
|
|
min — clones libplctag + compiles `ab_server`). If the AB CIP fixture
|
|
already built the image locally, docker reuses the cached layers + this
|
|
runs in seconds. (Until 2026-08-01 every family bound `:44818`, so only one could run and the theory's other two cases always failed; each now has its own port.) To switch
|
|
families stop the current service + start another.
|
|
|
|
## Endpoint
|
|
|
|
- Default: `10.100.0.35:44819` / `:44820` / `:44821` per family (the shared Docker host)
|
|
- Override with `AB_LEGACY_ENDPOINT=host:port` to point at a real SLC /
|
|
MicroLogix / PLC-5 PLC on its native port.
|
|
|
|
## Env vars
|
|
|
|
| Var | Default | Purpose |
|
|
|---|---|---|
|
|
| `AB_LEGACY_ENDPOINT` | *unset* — per-family ports on `10.100.0.35` | `host:port` of a single PCCC device, pinning **every** family to it (real-hardware mode). |
|
|
| `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* | Only consulted when `AB_LEGACY_ENDPOINT` pins an endpoint: names which family that one device is, so the SLC500-only write test skips against other hardware. Against the Docker fixture it is unnecessary — each family has its own port, and each test skips itself if its own port is down. |
|
|
|
|
## Run the integration tests
|
|
|
|
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\Drivers\ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.IntegrationTests
|
|
```
|
|
|
|
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_CIP_PATH = "" # empty — real hardware has no backplane
|
|
dotnet test tests\Drivers\ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.IntegrationTests
|
|
```
|
|
|
|
`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
|
|
|
|
PCCC tag format is `<file>[<size>]` without a type suffix — file letter
|
|
implies type:
|
|
|
|
- `N` = 16-bit signed integer
|
|
- `F` = 32-bit IEEE 754 float
|
|
- `B` = 1-bit boolean (stored as uint16, bit-addressable via `/n`)
|
|
- `L` = 32-bit signed integer (SLC 5/05 V15+ only)
|
|
- `ST` = 82-byte ASCII string (MicroLogix-specific extension)
|
|
|
|
| Family | Seeded tags | Notes |
|
|
|---|---|---|
|
|
| SLC 500 | `N7[10]`, `F8[10]`, `B3[10]`, `L19[10]` | Baseline; covers the four numeric file types a typical SLC project uses |
|
|
| MicroLogix | `B3[10]`, `N7[10]`, `L19[10]` | No `F8` — MicroLogix 1000 has no float file; use L19 when scaled integers aren't enough |
|
|
| PLC-5 | `N7[10]`, `F8[10]`, `B3[10]` | No `L` — PLC-5 predates the L file type; DINT equivalents went in integer files |
|
|
|
|
## Known limitations
|
|
|
|
### ab_server rejects empty CIP paths
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
### Bit-file writes on ab_server
|
|
|
|
`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)
|
|
|
|
- **Timer / Counter file decomposition** — PCCC T4 / C5 files contain
|
|
three-field structs (`.ACC` / `.PRE` / `.DN`). Not in ab_server's
|
|
scope; tests targeting `T4:0.ACC` stay unit-only.
|
|
- **ST (ASCII string) files** — real MicroLogix ST files have a length
|
|
field plus CRLF-sensitive semantics that don't round-trip cleanly.
|
|
- **Indirect addressing** (`N7:[N10:5]`) — not in ab_server's scope.
|
|
- **DF1 serial wire behaviour** — the whole ab_server path is TCP;
|
|
DF1 radio / serial fidelity needs real hardware.
|
|
|
|
See [`docs/drivers/AbLegacy-Test-Fixture.md`](../../../docs/drivers/AbLegacy-Test-Fixture.md)
|
|
for the full coverage map.
|
|
|
|
## References
|
|
|
|
- [libplctag on GitHub](https://github.com/libplctag/libplctag) — `ab_server`
|
|
lives under `src/tools/ab_server/`
|
|
- [`docs/drivers/AbLegacy-Test-Fixture.md`](../../../docs/drivers/AbLegacy-Test-Fixture.md)
|
|
— coverage map + gap inventory
|
|
- [`docs/v2/dev-environment.md`](../../../docs/v2/dev-environment.md)
|
|
§Docker fixtures — full fixture inventory
|
|
- [`../../ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Docker/`](../../ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Docker/)
|
|
— the shared Dockerfile this compose file's `build:` block references
|