Files
lmxopcua/docs/Driver.AbLegacy.Cli.md
2026-04-26 08:56:23 -04:00

12 KiB

otopcua-ablegacy-cli — AB Legacy (PCCC) test client

Ad-hoc probe / read / write / subscribe tool for SLC 500 / MicroLogix 1100 / MicroLogix 1400 / PLC-5 devices, talking to the same AbLegacyDriver the OtOpcUa server uses (libplctag PCCC back-end).

Third of four driver test-client CLIs. Shares Driver.Cli.Common with the others.

Build + run

dotnet run --project src/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Cli -- --help

Common flags

Flag Default Purpose
-g / --gateway required Canonical ab://host[:port]/cip-path
-P / --plc-type Slc500 Slc500 / MicroLogix / Plc5 / LogixPccc
--timeout-ms 5000 Per-operation timeout — see precedence note below
--retries 0 Retry count on transient BadCommunicationError (PR 9 / #252)
--demote-failure-threshold 3 PR ablegacy-12 / #255 — consecutive comm failures before the device is auto-demoted
--demote-for-ms 30000 PR ablegacy-12 / #255 — auto-demote cool-down window in ms
--no-demote off PR ablegacy-12 / #255 — disable auto-demote entirely (counters still tick)
--verbose off Serilog debug output

Family ↔ CIP-path cheat sheet:

  • SLC 5/05 / PLC-51,0
  • MicroLogix 1100 / 1400 — empty path (ab://host/) — they use direct EIP with no backplane
  • LogixPccc1,0 (Logix controller accessed via the PCCC compatibility layer; rare)
  • PLC-5 via 1756-DHRIO bridge1,<slot>,2,<station-octal> (PLC-5 only). See drivers/AbLegacy-DH-Bridging.md for the full DH+ syntax, octal-station reference (00..77 = 0..63), and manual hardware smoke procedure.

DHRIO worked example (PR ablegacy-13 / #256)

PLC-5 on DH+ node 7 (octal 07), DHRIO module in chassis slot 3, EtherNet/IP gateway 192.168.1.10:

otopcua-ablegacy-cli read `
    -g ab://192.168.1.10/1,3,2,07 `
    -P Plc5 -a N7:10 -t Int

The parser validates 1,<slot>,2,<station>: port-1 must be the backplane, slot must be 0..16, port-3 must be 2 (DH+), station must be octal 0..77 (so 80, 90, etc. are rejected). Combining a DH+ bridge path with a non-PLC-5 family at startup throws InvalidOperationException("DHRIO bridging is PLC-5-only").

Per-device timeout / retry tuning (#252, PR 9)

The CLI's --timeout-ms is the driver-wide default when launched as a one-shot test client. In production (server-side, multi-device deployment) each AbLegacyDeviceOptions row carries its own optional Timeout / Retries that override the driver-wide value.

Precedence (highest → lowest): per-device override → driver-wide default → hard-coded fallback (2000 ms / 0 retries).

Tuning cheat sheet — start here, measure, then trim:

Family Recommended Timeout Notes
SLC 5/01 (RS-232 / DH+ bridge) 5000 ms Slowest of the bunch; serial round-trip plus DH+ hop
SLC 5/02 / 5/03 (DH+) 3000 ms Bridged Ethernet → DH+ adds ~1 s
SLC 5/04 / 5/05 (Ethernet) 2000 ms Fastest of the SLC family — direct EIP/PCCC
MicroLogix 1100 / 1400 3000 ms Single-CPU, slow scan; no backplane
PLC-5 (Ethernet I/F) 2500 ms Comparable to SLC 5/05 over EIP
LogixPccc compat layer 2000 ms Logix CPU is fast; PCCC layer is the floor

A small --retries 1 (or 2 for slow chassis) is generally safe — the retry loop only fires on transient BadCommunicationError; terminal errors (BadNodeIdUnknown, BadTypeMismatch, …) surface on the first attempt.

PCCC address primer

File letters imply data type; type flag still required so the CLI knows how to parse your --value.

File Type CLI --type
N signed int16 Int
F float32 Float
B bit-packed (B3:0/3 addresses bit 3 of word 0) Bit
L long int32 (SLC 5/05+ only) Long
A analog int (semantically like N) AnalogInt
ST ASCII string (82-byte + length header) String
T timer sub-element (T4:0.ACC / .PRE / .EN / .DN) TimerElement
C counter sub-element (C5:0.ACC / .PRE / .CU / .CD / .DN) CounterElement
R control sub-element (R6:0.LEN / .POS / .EN / .DN / .ER) ControlElement

Commands

probe

# SLC 5/05 — default probe address N7:0
otopcua-ablegacy-cli probe -g ab://192.168.1.20/1,0

# MicroLogix 1100 — status file first word
otopcua-ablegacy-cli probe -g ab://192.168.1.30/ -P MicroLogix -a S:0

probe output (PR ablegacy-12 / #255) reports both Health (driver health state) and Host state. The latter is sourced from IHostConnectivityProbe and surfaces Demoted when the auto-demote threshold has tripped — a fast visual signal that the CLI is short-circuiting future reads against this device until the cool-down expires:

Gateway:      ab://192.168.1.20/1,0
PLC type:     Slc500
Health:       Degraded
Host state:   Demoted
Last error:   libplctag status -33 reading N7:0

Auto-demote knobs

# Trip after just one comm failure, hold for 60s.
otopcua-ablegacy-cli read -g ab://192.168.1.20/1,0 -a N7:0 -t Int `
    --demote-failure-threshold 1 --demote-for-ms 60000

# Opt out of auto-demote — stresses the link without short-circuiting.
otopcua-ablegacy-cli read -g ab://192.168.1.20/1,0 -a N7:0 -t Int --no-demote

The CLI is a one-shot test client — auto-demote primarily matters in the server-side multi-device deployment, where a single demoted PLC can no longer block reads against its healthy peers. Use the CLI flags to reproduce a flapping-link scenario locally before tuning the server-side appsettings.json Demote block.

read

# Integer
otopcua-ablegacy-cli read -g ab://192.168.1.20/1,0 -a N7:10 -t Int

# Float
otopcua-ablegacy-cli read -g ab://192.168.1.20/1,0 -a F8:0 -t Float

# Bit-within-word
otopcua-ablegacy-cli read -g ab://192.168.1.20/1,0 -a B3:0/3 -t Bit

# Long (SLC 5/05+)
otopcua-ablegacy-cli read -g ab://192.168.1.20/1,0 -a L19:0 -t Long

# Timer ACC
otopcua-ablegacy-cli read -g ab://192.168.1.20/1,0 -a T4:0.ACC -t TimerElement

# Diagnostic counter (PR ablegacy-10 / #253). The seven _Diagnostics/<name>
# addresses live alongside user tags — short-circuit serves them straight from
# the in-process counter store, so no PCCC frame is sent to the PLC.
otopcua-ablegacy-cli read -g ab://192.168.1.20/1,0 --address _Diagnostics/RequestCount

The diagnostic surface auto-emits per device — no config required. See docs/drivers/AbLegacy-Diagnostics.md for the full counter table + reset semantics + collision-rejection rules.

write

otopcua-ablegacy-cli write -g ab://192.168.1.20/1,0 -a N7:10 -t Int -v 42
otopcua-ablegacy-cli write -g ab://192.168.1.20/1,0 -a F8:0 -t Float -v 3.14
otopcua-ablegacy-cli write -g ab://192.168.1.20/1,0 -a B3:0/3 -t Bit -v on

Writes to timer / counter / control sub-elements land at the wire level but the PLC's runtime semantics (EN/DN edge-triggering, preset reload) are PLC-managed — use with caution.

subscribe

otopcua-ablegacy-cli subscribe -g ab://192.168.1.20/1,0 -a N7:10 -t Int -i 500

Deadband

PR 8 — per-tag absolute / percent change filter on top of the polled subscription. The driver caches the last published value per tag and suppresses OnDataChange notifications until the new sample crosses the configured threshold.

Flag Effect
--deadband-absolute <value> Suppress until `
--deadband-percent <value> Suppress until `

Booleans bypass the filter entirely (every transition publishes); strings + status changes always publish; first-seen always publishes; both flags set → either passing triggers a publish (Kepware-style logical OR).

# Float — drop sub-0.5 jitter from the noisy load-cell address.
otopcua-ablegacy-cli subscribe -g ab://192.168.1.20/1,0 -a F8:0 -t Float -i 500 `
    --deadband-absolute 0.5

# Integer — only fire on >= 5% deviation from the last reported value.
otopcua-ablegacy-cli subscribe -g ab://192.168.1.20/1,0 -a N7:10 -t Int -i 500 `
    --deadband-percent 5

Array reads

PR 7 — one PCCC frame can carry up to ~120 words. Address an array tag with either the Rockwell-native ,N suffix or the libplctag-native [N] suffix on the word number; both forms canonicalise to [N] when the driver hands the tag to libplctag, and the parser caps N at 120.

# Rockwell `,N` form — "10 consecutive words starting at N7:0"
otopcua-ablegacy-cli read -g ab://192.168.1.20/1,0 -a "N7:0,10" -t Int

# libplctag `[N]` form — same wire result
otopcua-ablegacy-cli read -g ab://192.168.1.20/1,0 -a "N7:0[10]" -t Int

# Float / Long arrays — same suffix syntax, narrower frame ceiling on Float (~60 elements)
# and Long (~60 elements) because each element is 4 bytes vs Int's 2.
otopcua-ablegacy-cli read -g ab://192.168.1.20/1,0 -a "F8:0,4" -t Float
otopcua-ablegacy-cli read -g ab://192.168.1.20/1,0 -a "L19:0,4" -t Long

# --array-length override — pin the element count from config rather than the address
# suffix. Wins over the parsed `,N` / `[N]` value when both are set; useful for keeping the
# address string compact while bumping the element count from a tags config file.
otopcua-ablegacy-cli read -g ab://192.168.1.20/1,0 -a "N7:0" --array-length 10 -t Int

Array tags reject sub-element references (T4:0,5.ACC) and bit suffixes (N7:0,10/3) at parse time — both combinations are semantically meaningless against a contiguous block.

For B-files the Rockwell convention is "one BOOL per word, not per bit": B3:0,10 returns bool[10] (one per word's non-zero state), not bool[160].

import-rslogix

ablegacy-11 / #254 — bulk-import RSLogix 500 / 5 CSV symbol exports into an appsettings.json tag fragment. Avoids hand-typing every N7:0 / F8:12 / B3:0/5 row of a several-hundred-tag PLC. Binary .RSS / .RSP project files are out of scope; export to CSV first.

# Default: emit JSON fragment to stdout
otopcua-ablegacy-cli import-rslogix `
    --file C:\plc\plc-export.csv `
    --device ab://192.168.1.20/1,0

# Write the fragment to a file + print a summary line to stdout
otopcua-ablegacy-cli import-rslogix `
    --file C:\plc\plc-export.csv `
    --device ab://192.168.1.20/1,0 `
    --output tags.json

# Filter by Scope column — only import Local:1 program-scoped tags
otopcua-ablegacy-cli import-rslogix `
    --file C:\plc\plc-export.csv `
    --device ab://192.168.1.20/1,0 `
    --scope Local:1

# Summary mode — one-line counter for CI / health checks
otopcua-ablegacy-cli import-rslogix `
    --file C:\plc\plc-export.csv `
    --device ab://192.168.1.20/1,0 `
    --emit summary
Flag Default Purpose
-f / --file required RSLogix CSV path
-d / --device required ab://host[:port]/cip-path every imported tag binds to
--emit appsettings-fragment appsettings-fragment (JSON) or summary (one-line counter)
-o / --output stdout Optional output file path
--scope none Scope filter — Global / Local:N (case-insensitive); empty Scope counts as Global
--max-rows unlimited Defensive cap on rows imported
--strict off Fail-fast on first malformed row (default permissive: skip + log)

See drivers/AbLegacy-RSLogix-Import.md for the full column reference, file-letter → AbLegacyDataType mapping, and the API surface (IRsLogixImporter, AbLegacyDriverOptions.AddRsLogixImport).

Known caveat — ab_server upstream gap

The integration-fixture ab_server Docker container accepts TCP but its PCCC dispatcher doesn't actually respond — see tests/...AbLegacy.IntegrationTests/Docker/README.md. Point --gateway at real hardware or an RSEmulate 500 box for end-to-end wire-level validation. The CLI itself is correct regardless of which endpoint you target.