8.7 KiB
AbCip — ControlLogix HSBY paired-IP support
PR abcip-5.1 adds non-transparent HSBY (Hot-Standby) awareness to the AB CIP driver. Each device may declare a partner gateway; when both gateways are up the driver concurrently probes a role tag on each chassis and reports which one is currently Active.
PR abcip-5.1 only gathers + reports the role. PR abcip-5.2 is the
follow-up that wires the resolved active address into
AbCipDriver.ResolveHost so reads and writes route to whichever chassis is
Active without operator intervention.
When to use HSBY paired IPs
You have a redundant ControlLogix chassis pair (1756-RM redundancy module, two CPUs, one acting + one standby) and the SCADA / OPC UA layer needs to keep talking to whichever chassis is currently Active without an operator manually re-pointing the connection.
Pre-5.1 the driver only knew about a single HostAddress. After a
hot-standby switch-over, the standby (now Active) carried a different IP
and the driver kept probing the dead-but-was-Active address until someone
edited the config.
PR abcip-5.1 closes the visibility half of that gap by reading the role tag
on both chassis. PR abcip-5.2 closes the routing half by re-pointing
ResolveHost at the Active address each tick.
Configuration
{
"Devices": [
{
"HostAddress": "ab://10.0.0.5/1,0",
"PartnerHostAddress": "ab://10.0.0.6/1,0",
"Hsby": {
"Enabled": true,
"RoleTagAddress": "WallClockTime.SyncStatus",
"ProbeIntervalMs": 2000
}
}
]
}
| Field | Default | Notes |
|---|---|---|
PartnerHostAddress |
null |
Canonical ab://gateway[:port]/cip-path of the partner chassis. null = no HSBY pair; the driver behaves exactly like every pre-5.1 build. |
Hsby.Enabled |
false |
Master switch. When false (or Hsby omitted) no role probing happens, even if PartnerHostAddress is set. |
Hsby.RoleTagAddress |
WallClockTime.SyncStatus |
Address of the role tag on each chassis. See role-tag detection matrix. |
Hsby.ProbeIntervalMs |
2000 |
How often each chassis is sampled. 2 s is a good default — tight enough to detect a switch-over within one Admin-UI refresh, loose enough to leave headroom for the regular probe loop. |
Feature-flag gate (Redundancy.Hsby.Enabled)
Hsby.Enabled = false (the default) is the off-switch for the entire
feature. The role-probe loop never starts, the diagnostics keys are not
emitted, and the driver behaves identically to a pre-5.1 build. This is the
gate to flip when an operator wants to roll the feature out cautiously
across a fleet — set Hsby.Enabled = true per-device in driver config (no
build flag, no env var).
When the gate is on but the partner gateway is unreachable, the role-probe
loop reports HsbyRole.Unknown for the partner each tick. The primary's
role still drives the active-chassis resolution; the operator sees the
partner's role as Unknown in the Admin UI / driver diagnostics, which is the
correct surface for "we can't reach the standby chassis right now."
Role-tag detection matrix
| Firmware / fronts | Address | Decode |
|---|---|---|
| v20 / v24 / v32+ ControlLogix HSBY | WallClockTime.SyncStatus (DINT) |
0 = Standby, 1 = Synchronized / Active, 2 = Disqualified, anything else = Unknown |
| PLC-5 / SLC500 status-byte fallback | S:34 Module Status word |
bit 0 = "this chassis is Active". Bit set → Active; clear → Standby |
| Custom user role tag | any DINT-typed CIP path | Same matrix as WallClockTime.SyncStatus (0 / 1 / 2). Out-of-range values → Unknown. |
AbCipHsbyRoleProber.MapValueToRole is the value-to-role mapper; unit tests
in tests/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipHsbyTests.cs pin every
row of the matrix.
What gets reported
The driver surfaces three diagnostics counters per HSBY-enabled device
(visible via driver-diagnostics RPC + the Admin UI):
| Counter | Value |
|---|---|
AbCip.HsbyActive |
1 if primary is Active, 2 if partner is Active, 0 if neither (or HSBY off) |
AbCip.HsbyPrimaryRole |
(int)HsbyRole — 0 = Unknown, 1 = Active, 2 = Standby, 3 = Disqualified |
AbCip.HsbyPartnerRole |
Same encoding as HsbyPrimaryRole, observed on the partner chassis |
When more than one HSBY pair is configured on the same driver instance the
flat keys are scoped per primary host: AbCip.HsbyActive[ab://10.0.0.5/1,0],
etc.
The DeviceState.ActiveAddress field (internal; surfaced via
HsbyActive diagnostics) is the address PR 5.2 will route through
ResolveHost.
Active-resolution rules
| Primary role | Partner role | ActiveAddress resolution |
|---|---|---|
| Active | Standby / Disqualified / Unknown | primary |
| Standby / Disqualified / Unknown | Active | partner |
| Active | Active (split-brain) | primary wins, warning logged |
| Standby + Standby | Standby + Standby | null (PR 5.2 will surface as BadCommunicationError) |
| Unknown + Unknown | Unknown + Unknown | null |
Split-brain (both chassis claim Active simultaneously) is a real
production failure mode — typically a redundancy-module misconfiguration or
a partial network split. The driver picks primary deterministically + emits
a warning through AbCipDriverOptions.OnWarning so operators see it in the
log.
CLI flags
The otopcua-abcip-cli tool exposes the HSBY plumbing through two surfaces
(see Driver.AbCip.Cli.md for the full CLI guide):
--partner <gateway>— global flag on every command. SetsPartnerHostAddress+ auto-enablesHsby.Enabled = trueso the role probe runs alongside any read / write / subscribe.hsby-status— dedicated command that prints which chassis is currently Active. Reads the role tag on both gateways for a few ticks + prints the(primary, partner, active)tuple.
# Print which chassis is Active right now
otopcua-abcip-cli hsby-status -g ab://10.0.0.5/1,0 --partner ab://10.0.0.6/1,0
# Subscribe through the active chassis (PR 5.2 follow-up — today the
# subscribe stays pointed at the primary; the role probe runs alongside).
otopcua-abcip-cli subscribe -g ab://10.0.0.5/1,0 --partner ab://10.0.0.6/1,0 \
-t Motor01_Speed --type Real -i 500
Test coverage
- Unit (
tests/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipHsbyTests.cs):- Pure
MapValueToRolematrix (WallClockTime.SyncStatus + S:34 bit mask + Unknown values). - End-to-end driver loop: primary Active / partner Standby resolves to
primary; both Active resolves to primary with a warning; both
Standby clears
ActiveAddress; primary read failure routes to partner. - Diagnostics surface (
AbCip.HsbyActive/HsbyPrimaryRole/HsbyPartnerRole). - DTO JSON round-trip (
PartnerHostAddress+Hsby.{Enabled, RoleTagAddress, ProbeIntervalMs}survive deserialise → driver →DeviceState). Hsby.Enabled = false→ no role probing.
- Pure
- Integration (
tests/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/AbCipHsbyRoleProberTests.cs):- Skipped by default (
Assert.Skip) —ab_servercannot emulate a ControlLogix HSBY pair (noWallClockTime.SyncStatus, no second chassis concept). The Dockerpairedprofile (PR 5.1) brings up twoab_serverinstances + a stubhsby-muxsidecar so the topology is documented, but PR 5.2 follow-up needs a patchedab_serverimage that actually serves the role tag before the integration test can assert anything against the wire. - Trait
Category=Hsbysodotnet test --filter Category=Hsbyfinds this test once it's promoted.
- Skipped by default (
Follow-ups (PR 5.2 + beyond)
- PR 5.2 — wire
ActiveAddressintoResolveHostso reads/writes route to the live chassis automatically. Today's PR only gathers the role. - Patched
ab_serverimage — add a writableWallClockTime.SyncStatustag (or a separate Python shim) so the Dockerpairedprofile can exercise the wire-level role probe. hsby-muxREST endpoint —POST /flip {"active": "primary"}writes1to the chosen chassis +0to the other so integration tests can drive switch-overs deterministically.- GuardLogix HSBY — same role-tag plumbing applies; verify against a real 1756-L8xS pair when one is on-site.
See also
docs/Driver.AbCip.Cli.md—--partnerflag +hsby-statuscommand referencedocs/drivers/AbServer-Test-Fixture.md§"What it does NOT cover" — HSBY entrydocs/Redundancy.md— server-level (OPC UA-stack) redundancy; HSBY is the driver-level companion