New project tests/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.IntegrationTests/ with four pieces. AbLegacyServerFixture — TCP probe against localhost:44818 (or AB_LEGACY_ENDPOINT override), distinct from AB_SERVER_ENDPOINT so both CIP + PCCC containers can run simultaneously. Single-public-ctor to satisfy xunit collection-fixture constraint. AbLegacyServerProfile + KnownProfiles carry the per-family (SLC500 / MicroLogix / PLC-5) ComposeProfile + Notes; drives per-theory parameterisation. AbLegacyFactAttribute / AbLegacyTheoryAttribute match the AB CIP skip-attribute pattern. Docker/docker-compose.yml reuses the AB CIP otopcua-ab-server:libplctag-release image — `build:` block points at ../../AbCip.IntegrationTests/Docker context so `docker compose build` from here produces / reuses the same multi-stage build. Three compose profiles (slc500 / micrologix / plc5) with per-family `--plc` + `--tag=<file>[<size>]` flags matching the PCCC tag syntax (different from CIP's `Name:Type[size]`). AbLegacyReadSmokeTests — one parametric theory reading N7:0 across all three families + one SLC500 write-then-read on N7:5. Targets the shape the driver would use against real hardware. Verified 2026-04-20 against a live SLC500 container: TCP probe passes + container accepts connections + libplctag negotiates session, but read/write returns BadCommunicationError (libplctag status 0x80050000). Root-caused to ab_server's PCCC server-side opcode coverage being narrower than libplctag's PCCC client expects — not a driver-side bug, not a scaffold bug, just an ab_server upstream limitation. Documented honestly in Docker/README.md + AbLegacy-Test-Fixture.md rather than skipping the tests or weakening assertions; tests now skip cleanly when container is absent, fail with clear message when container is up but the protocol gap surfaces. Operator resolves by filing an ab_server upstream patch, pointing AB_LEGACY_ENDPOINT at real hardware, or scaffolding an RSEmulate 500 golden-box tier. Docker/README.md — Known limitations section leads with the PCCC round-trip gap (test date, failure signature, possible root causes, three resolution paths) before the pre-existing limitations (T/C file decomposition, ST file quirks, indirect addressing, DF1 serial). Reader can't miss the "scaffolded but blocked on upstream" framing. docs/drivers/AbLegacy-Test-Fixture.md — TL;DR flipped from "no integration fixture" to "Docker scaffold in place; wire-level round-trip currently blocked by ab_server PCCC gap". What-the-fixture-is gains an Integration section. Follow-up candidates rewritten: #1 is now "fix ab_server PCCC upstream", #2 is RSEmulate 500 golden-box (with cost callouts matching our existing Logix Emulate + TwinCAT XAR scaffolds — license + Hyper-V conflict + binary project format), #3 is lab rig. Key-files list adds the four new files. docs/drivers/README.md coverage-map row updated from "no integration fixture" to "Docker scaffold via ab_server PCCC; wire-level round-trip currently blocked, docs call out resolution paths". Solution file picks up the new tests/.../AbLegacy.IntegrationTests entry. AbLegacyDataType.Int used throughout (not Int16 — the enum uses SLC file-type naming). Build 0 errors; 2 smoke tests skip cleanly without container + fail with clear errors when container up (proving the infrastructure works end-to-end + the gap is specifically the ab_server protocol coverage, not the scaffold). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
141 lines
5.8 KiB
Markdown
141 lines
5.8 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` / `micrologix` / `plc5`); all bind `:44818` |
|
|
|
|
## Run
|
|
|
|
From the repo root:
|
|
|
|
```powershell
|
|
# SLC 500 family — widest PCCC coverage
|
|
docker compose -f tests\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. Only one family binds `:44818` at a time; to switch
|
|
families stop the current service + start another.
|
|
|
|
## Endpoint
|
|
|
|
- Default: `localhost:44818` (EtherNet/IP standard)
|
|
- Override with `AB_LEGACY_ENDPOINT=host:port` to point at a real SLC /
|
|
MicroLogix / PLC-5 PLC on its native port.
|
|
|
|
## Run the integration tests
|
|
|
|
In a separate shell with a container up:
|
|
|
|
```powershell
|
|
cd C:\Users\dohertj2\Desktop\lmxopcua
|
|
dotnet test tests\ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.IntegrationTests
|
|
```
|
|
|
|
`AbLegacyServerFixture` TCP-probes `localhost:44818` at collection init +
|
|
records a skip reason when unreachable. Tests use `[AbLegacyFact]` /
|
|
`[AbLegacyTheory]` which check the same probe.
|
|
|
|
## 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 PCCC read/write round-trip (verified 2026-04-20)
|
|
|
|
**Scaffold is in place; wire-level round-trip does NOT currently pass
|
|
against `ab_server --plc=SLC500`.** With the SLC500 compose profile up,
|
|
TCP 44818 accepts connections and libplctag negotiates the session,
|
|
but the three smoke tests in `AbLegacyReadSmokeTests.cs` all fail at
|
|
read/write with `BadCommunicationError` (libplctag status `0x80050000`).
|
|
Possible root causes:
|
|
|
|
- ab_server's PCCC server-side opcode coverage may be narrower than
|
|
libplctag's PCCC client expects — the tool is primarily a CIP
|
|
server; PCCC was added later + is noted in libplctag docs as less
|
|
mature.
|
|
- libplctag's PCCC-over-CIP encapsulation may assume a real SLC 5/05
|
|
EtherNet/IP NIC's framing that ab_server doesn't emit.
|
|
|
|
The scaffold ships **as-is** because:
|
|
|
|
1. The Docker infrastructure + fixture pattern works cleanly (probe
|
|
passes, container lifecycle is clean, tests skip when absent).
|
|
2. The test classes target the correct shape for what the AB Legacy
|
|
driver would do against real hardware.
|
|
3. Pointing `AB_LEGACY_ENDPOINT` at a real SLC 5/05 / MicroLogix
|
|
1100 / 1400 should make the tests pass outright — the failure
|
|
mode is ab_server-specific, not driver-specific.
|
|
|
|
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.
|
|
3. **Lab rig** — used SLC 5/05 / MicroLogix 1100 on a dedicated
|
|
network; the authoritative path.
|
|
|
|
### 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
|