Files
lmxopcua/docs/drivers/AbLegacy-DH-Bridging.md
2026-04-26 08:56:23 -04:00

142 lines
5.7 KiB
Markdown

# AB Legacy — DH+ via 1756-DHRIO bridging
PR ablegacy-13 / [#256](https://github.com/dohertj2/lmxopcua/issues/256). The AB
Legacy driver can address a PLC-5 sitting on a DH+ link by routing CIP requests
through a 1756-DHRIO module installed in a ControlLogix chassis. This is the
canonical way to keep an installed-base PLC-5 fleet alive after the chassis-
level migration to ControlLogix; the DHRIO module exposes a DH+ "side" that
talks to the legacy PLC-5 / SLC-DH+ peers and a backplane "side" that the
ControlLogix CPU + Ethernet bridge can route through.
## Wire layout
```
OtOpcUa server ──EtherNet/IP──► 1756-EN2T (slot 0) ──backplane──► 1756-DHRIO (slot N) ──DH+──► PLC-5
```
Two CIP hops:
1. **Backplane** — port `1`, slot `<N>` (the slot the DHRIO module lives in).
2. **DH+** — port `2`, station `<S>` (the DH+ node address of the target PLC-5,
in **octal**).
Resulting CIP path: `1,<N>,2,<S>`.
> The first port `1` is always the backplane; port `2` is the DH+ side of the
> 1756-DHRIO module. This mirrors the convention Rockwell uses in RSLinx + RSLogix
> 5.
## Octal station number
The DH+ network was specified with **octal** node addresses. Rockwell tooling
displays them in octal too (RSLogix 5 → "DH+ Node Address" field on the
controller properties dialog). The driver follows suit — the station segment
of the CIP path **must be parsed as octal** (digits 0..7 only; `8`, `9`, and
multi-byte garbage are rejected).
DH+ addresses run `0..77` octal == `0..63` decimal. Quick reference:
| Octal | Decimal | Octal | Decimal | Octal | Decimal | Octal | Decimal |
|------:|--------:|------:|--------:|------:|--------:|------:|--------:|
| 00 | 0 | 20 | 16 | 40 | 32 | 60 | 48 |
| 01 | 1 | 21 | 17 | 41 | 33 | 61 | 49 |
| 02 | 2 | 22 | 18 | 42 | 34 | 62 | 50 |
| 03 | 3 | 23 | 19 | 43 | 35 | 63 | 51 |
| 04 | 4 | 24 | 20 | 44 | 36 | 64 | 52 |
| 05 | 5 | 25 | 21 | 45 | 37 | 65 | 53 |
| 06 | 6 | 26 | 22 | 46 | 38 | 66 | 54 |
| 07 | 7 | 27 | 23 | 47 | 39 | 67 | 55 |
| 10 | 8 | 30 | 24 | 50 | 40 | 70 | 56 |
| 11 | 9 | 31 | 25 | 51 | 41 | 71 | 57 |
| 12 | 10 | 32 | 26 | 52 | 42 | 72 | 58 |
| 13 | 11 | 33 | 27 | 53 | 43 | 73 | 59 |
| 14 | 12 | 34 | 28 | 54 | 44 | 74 | 60 |
| 15 | 13 | 35 | 29 | 55 | 45 | 75 | 61 |
| 16 | 14 | 36 | 30 | 56 | 46 | 76 | 62 |
| 17 | 15 | 37 | 31 | 57 | 47 | 77 | 63 |
Anything past `77` octal (i.e. decimal > 63) is invalid on a real DH+ network
and rejected by the parser.
## PLC-5 only
DHRIO bridging is **PLC-5-only**. The driver enforces this at
`AbLegacyDriver.InitializeAsync` time: a DH+ bridge path combined with
`PlcFamily=Slc500 / MicroLogix / LogixPccc` throws
`InvalidOperationException("DHRIO bridging is PLC-5-only")` immediately rather
than letting reads silently fail with `BadCommunicationError` on the wire.
Background: the 1756-DHRIO module only speaks DH+ to PLC-5 / SLC-DH+ peers, and
libplctag's PCCC stack only exposes the PLC-5 side. SLC 5/04 boxes on DH+
**can** be physically reached through a DHRIO module, but the protocol stack
needed to drive them isn't exposed by libplctag — out of scope for this driver.
## CLI worked example
PLC-5 at DH+ node `07` (octal == 7 decimal), DHRIO module in slot 3, gateway
`192.168.1.10`:
```powershell
otopcua-ablegacy-cli probe `
-g ab://192.168.1.10/1,3,2,07 `
-P Plc5 `
-a N7:0
```
```powershell
# Read N7:10 from the PLC-5 across the DHRIO bridge
otopcua-ablegacy-cli read `
-g ab://192.168.1.10/1,3,2,07 `
-P Plc5 `
-a N7:10 `
-t Int
```
The driver surfaces the parsed bridge form on the host-address record:
`BackplaneSlot=3`, `DhPlusPort=2`, `DhPlusStation=7` (decimal-translated). Use
those values when reading driver-diagnostics output to confirm the bridge was
recognised — a non-bridge CIP path leaves all three fields null.
## Manual smoke procedure
There is no automated end-to-end coverage for DH+ bridging because the only
path to wire-level validation is real hardware (libplctag's `ab_server` Docker
image doesn't simulate the DHRIO + DH+ + PLC-5 stack). The unit-test layer
covers parser positive / negative cases.
Hardware smoke checklist:
1. Confirm the 1756-DHRIO module is present in the target ControlLogix chassis.
RSLinx Classic should show `DH+, 1` under the chassis tree with the PLC-5
nodes enumerated underneath.
2. Note the DHRIO module's slot number (the `<N>` in `1,<N>,2,<S>`).
3. Note the target PLC-5's DH+ node address — read it off the front-panel switch
bank, or the controller properties in RSLogix 5. **Read it as octal**.
4. From an OtOpcUa box that can reach the EtherNet/IP gateway:
```powershell
otopcua-ablegacy-cli probe -g ab://<gateway>/1,<slot>,2,<station-octal> -P Plc5 -a S:0
```
`S:0` (status file word 0) is non-destructive and present on every PLC-5.
5. If the probe succeeds, exercise an N file read against a known
non-zero address. Compare against the value displayed in RSLogix 5 →
Online → Data → N7.
If the probe fails with `BadCommunicationError`:
- Wrong slot number — re-check via RSLinx.
- Wrong octal node — convert from RSLogix 5's display value (already octal); a
decimal-thinking conversion mistake is the most common smoke failure.
- DHRIO module's DH+ baud rate doesn't match the PLC-5's switch setting (57.6k
/ 115.2k / 230.4k) — driver-side problem this can't paper over.
- A scanner on the DHRIO is in scheduled-mode and starving unscheduled
PCCC traffic — bump the DHRIO's unscheduled-message slice in RSLogix 5000.
## See also
- [`Driver.AbLegacy.Cli.md`](../Driver.AbLegacy.Cli.md) — the family / CIP-path
cheat sheet now carries a DHRIO row.
- [`drivers/AbLegacy-Test-Fixture.md`](AbLegacy-Test-Fixture.md) — DH+ bridging
is unit-only; no Docker fixture supports it.