# 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 `` (the slot the DHRIO module lives in). 2. **DH+** — port `2`, station `` (the DH+ node address of the target PLC-5, in **octal**). Resulting CIP path: `1,,2,`. > 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 `` in `1,,2,`). 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:///1,,2, -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.