Files
lmxopcua/docs/Driver.TwinCAT.Cli.md
Joseph Doherty 4dc685a365 Task #251 — S7 + TwinCAT test-client CLIs (driver CLI suite complete)
Final two of the five driver test clients. Pattern carried forward from
#249 (Modbus) + #250 (AB CIP, AB Legacy) — each CLI inherits Driver.Cli.Common
for DriverCommandBase + SnapshotFormatter and adds a protocol-specific
CommandBase + 4 commands (probe / read / write / subscribe).

New projects:
  - src/ZB.MOM.WW.OtOpcUa.Driver.S7.Cli/ — otopcua-s7-cli.
    S7CommandBase carries host/port/cpu/rack/slot/timeout. Handles all S7
    atomic types (Bool, Byte, Int16..UInt64, Float32/64, String, DateTime).
    DateTime parses via RoundtripKind so "2026-04-21T12:34:56Z" works.
  - src/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Cli/ — otopcua-twincat-cli.
    TwinCATCommandBase carries ams-net-id + ams-port + --poll-only toggle
    (flips UseNativeNotifications=false). Covers the full IEC 61131-3
    atomic set: Bool, SInt/USInt, Int/UInt, DInt/UDInt, LInt/ULInt, Real,
    LReal, String, WString, Time/Date/DateTime/TimeOfDay. Structure writes
    refused as out-of-scope (same as AB CIP). IEC time/date variants marshal
    as UDINT on the wire per IEC spec. Subscribe banner announces "ADS
    notification" vs "polling" so the mechanism is obvious in bug reports.

Tests (49 new, 122 cumulative driver-CLI):
  - S7: 22 tests. Every S7DataType has a happy-path + bounds case. DateTime
    round-trips an ISO-8601 string. Tag-name synthesis round-trips every
    S7 address form (DB / M / I / Q, bit/word/dword, strings).
  - TwinCAT: 27 tests. Full IEC type matrix including WString UTF-8 pass-
    through + the four IEC time/date variants landing on UDINT. Structure
    rejection case. Tag-name synthesis for Program scope, GVL scope, nested
    UDT members, and array elements.

Docs:
  - docs/Driver.S7.Cli.md — address grammar cheat sheet + the PUT/GET-must-
    be-enabled gotcha every S7-1200/1500 operator hits.
  - docs/Driver.TwinCAT.Cli.md — AMS router prerequisite (XAR / standalone
    Router NuGet / remote AMS route) + per-command examples.

Wiring:
  - ZB.MOM.WW.OtOpcUa.slnx grew 4 entries (2 src + 2 tests).

Full-solution build clean. Both --help outputs verified end-to-end.

Driver CLI suite complete: 5 CLIs (otopcua-{modbus,abcip,ablegacy,s7,twincat}-cli)
sharing a common base + formatter. 122 CLI tests cumulative. Every driver family
shipped in v2 now has a shell-level ad-hoc validation tool.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 08:44:53 -04:00

3.4 KiB

otopcua-twincat-cli — Beckhoff TwinCAT test client

Ad-hoc probe / read / write / subscribe tool for Beckhoff TwinCAT 2 / TwinCAT 3 runtimes via ADS. Uses the same TwinCATDriver the OtOpcUa server does (Beckhoff.TwinCAT.Ads package). Native ADS notifications by default; --poll-only falls back to the shared PollGroupEngine.

Fifth (final) of the driver test-client CLIs.

Build + run

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

Prerequisite: AMS router

The Beckhoff.TwinCAT.Ads library needs a reachable AMS router to open ADS sessions. Pick one:

  1. Local TwinCAT XAR — install the free TwinCAT 3 XAR Engineering install on the machine running the CLI; it ships the router.
  2. Beckhoff.TwinCAT.Ads.TcpRouter — standalone NuGet router. Run in a sidecar process when no XAR is installed.
  3. Remote AMS route — any Windows box with TwinCAT installed, with an AMS route authorised to the CLI host.

The CLI compiles + runs without a router, but every wire call fails with a transport error until one is reachable.

Common flags

Flag Default Purpose
-n / --ams-net-id required AMS Net ID (e.g. 192.168.1.40.1.1)
-p / --ams-port 851 AMS port (TwinCAT 3 PLC = 851, TwinCAT 2 = 801)
--timeout-ms 5000 Per-operation timeout
--poll-only off Disable native ADS notifications, use PollGroupEngine instead
--verbose off Serilog debug output

Data types

TwinCAT exposes the IEC 61131-3 atomic set: Bool, SInt, USInt, Int, UInt, DInt, UDInt, LInt, ULInt, Real, LReal, String, WString, Time, Date, DateTime, TimeOfDay. The four IEC time/date variants marshal as UDINT on the wire — CLI takes a numeric raw value and lets the caller interpret semantics.

Commands

probe

# Local TwinCAT 3, probe a canonical global
otopcua-twincat-cli probe -n 127.0.0.1.1.1 -s "TwinCAT_SystemInfoVarList._AppInfo.OnlineChangeCnt"

# Remote, probe a project variable
otopcua-twincat-cli probe -n 192.168.1.40.1.1 -s MAIN.bRunning --type Bool

read

# Bool symbol
otopcua-twincat-cli read -n 192.168.1.40.1.1 -s MAIN.bStart -t Bool

# Counter
otopcua-twincat-cli read -n 192.168.1.40.1.1 -s GVL.Counter -t DInt

# Nested UDT member
otopcua-twincat-cli read -n 192.168.1.40.1.1 -s Motor1.Status.Running -t Bool

# Array element
otopcua-twincat-cli read -n 192.168.1.40.1.1 -s "Recipe[3]" -t Real

# WString
otopcua-twincat-cli read -n 192.168.1.40.1.1 -s GVL.sMessage -t WString

write

otopcua-twincat-cli write -n 192.168.1.40.1.1 -s MAIN.bStart -t Bool -v true
otopcua-twincat-cli write -n 192.168.1.40.1.1 -s GVL.Counter -t DInt -v 42
otopcua-twincat-cli write -n 192.168.1.40.1.1 -s GVL.sMessage -t WString -v "running"

Structure writes refused — drop to driver config JSON for those.

subscribe

# Native ADS notifications (default) — PLC pushes on its own cycle
otopcua-twincat-cli subscribe -n 192.168.1.40.1.1 -s GVL.Counter -t DInt -i 500

# Fall back to polling for runtimes where native notifications are constrained
otopcua-twincat-cli subscribe -n 192.168.1.40.1.1 -s GVL.Counter -t DInt -i 500 --poll-only

The subscribe banner announces which mechanism is in play — "ADS notification" or "polling" — so it's obvious in screen-recorded bug reports.