8.5 KiB
otopcua-focas-cli — Fanuc FOCAS test client
Ad-hoc probe / read / write / subscribe tool for Fanuc CNCs via the FOCAS/2
protocol. Uses the same FocasDriver the OtOpcUa server does — PMC R/G/F
file registers, axis bits, parameters, and macro variables — all through
FocasAddressParser syntax.
Sixth of the driver test-client CLIs, added alongside the Tier-C isolation work tracked in task #220.
Architecture note
FOCAS is a Tier-C driver: Fwlib32.dll is a proprietary 32-bit Fanuc library
with a documented habit of crashing its hosting process on network errors.
The target runtime deployment splits the driver into an in-process
FocasProxyDriver (.NET 10 x64) and an out-of-process Driver.FOCAS.Host
(.NET 4.8 x86 Windows service) that owns the DLL — see
v2/implementation/focas-isolation-plan.md
and
v2/implementation/phase-6-1-resilience-and-observability.md
for topology + supervisor / respawn / back-pressure design.
The CLI skips the proxy and loads FocasDriver directly (via
FwlibFocasClientFactory, which P/Invokes Fwlib32.dll in the CLI's own
process). There is no public simulator for FOCAS; a meaningful probe
requires a real CNC + a licensed Fwlib32.dll on PATH (or next to the
executable). On a dev box without the DLL, every wire call surfaces as
BadCommunicationError — still useful as a "CLI wire-up is correct" signal.
Build + run
dotnet build src/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Cli
dotnet run --project src/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Cli -- --help
Or publish a self-contained binary:
dotnet publish src/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Cli -c Release -o publish/focas-cli
publish/focas-cli/otopcua-focas-cli.exe --help
Common flags
Every command accepts:
| Flag | Default | Purpose |
|---|---|---|
-h / --cnc-host |
required | CNC IP address or hostname |
-p / --cnc-port |
8193 |
FOCAS TCP port (FOCAS-over-EIP default) |
-s / --series |
Unknown |
CNC series — Unknown / Zero_i_D / Zero_i_F / Zero_i_MF / Zero_i_TF / Sixteen_i / Thirty_i / ThirtyOne_i / ThirtyTwo_i / PowerMotion_i |
--timeout-ms |
2000 |
Per-operation timeout |
--verbose |
off | Serilog debug output |
Addressing
FocasAddressParser syntax — the same format the server + FocasTagDefinition
use. Common shapes:
| Address | Meaning |
|---|---|
R100 |
PMC R-file word register 100 |
X0.0 |
PMC X-file bit 0 of byte 0 |
G50.3 |
PMC G-file bit 3 of byte 50 |
F1.4 |
PMC F-file bit 4 of byte 1 |
PARAM:1815/0 |
Parameter 1815, axis 0 |
MACRO:500 |
Macro variable 500 |
Data types
Bit, Byte, Int16, Int32, Float32, Float64, String. Default is
Int16 (matches PMC R-file word width).
Commands
probe — is the CNC reachable?
Opens a FOCAS session, reads one sample address, prints driver health.
# Default: read R100 as Int16
otopcua-focas-cli probe -h 192.168.1.50
# Explicit series + address
otopcua-focas-cli probe -h 192.168.1.50 -s ThirtyOne_i --address R200 --type Int16
read — single address
# PMC R-file word
otopcua-focas-cli read -h 192.168.1.50 -a R100 -t Int16
# PMC X-bit
otopcua-focas-cli read -h 192.168.1.50 -a X0.0 -t Bit
# Parameter (axis 0)
otopcua-focas-cli read -h 192.168.1.50 -a PARAM:1815/0 -t Int32
# Macro variable
otopcua-focas-cli read -h 192.168.1.50 -a MACRO:500 -t Float64
write — single value
Values parse per --type with invariant culture. Booleans accept
true / false / 1 / 0 / yes / no / on / off.
otopcua-focas-cli write -h 192.168.1.50 -a R100 -t Int16 -v 42
otopcua-focas-cli write -h 192.168.1.50 -a G50.3 -t Bit -v on
# MACRO: write — recipe / setpoint surface (server-side WriteOperate ACL)
otopcua-focas-cli write -h 192.168.1.50 -a MACRO:500 -t Int32 -v 42
# PARAM: write — commissioning surface (server-side WriteConfigure ACL,
# CNC must be in MDI mode + parameter-write switch enabled, else EW_PASSWD
# surfaces as BadUserAccessDenied)
otopcua-focas-cli write -h 192.168.1.50 -a PARAM:1815 -t Int32 -v 100
WARNING —
write -a G50.3 -t Bit -v onis a read-modify-write. The wire callpmc_wrpmcrngis byte-addressed; the driver reads the parent byte atG50first, sets bit 3, and writes the byte back. Other bits inG50that the ladder is concurrently updating may be clobbered by the byte we read a millisecond ago. Coordinate via a ladder-side handshake when this matters. PMC writes also bypass the ladder's normal MDI-mode protection — a misdirected bit can move motion or latch a feedhold the moment it lands. Verify e-stop is live and the machine is in JOG mode before issuing the first PMC write of a session. Seedocs/drivers/FOCAS.md"PMC bit-write read-modify-write semantics" for the full RMW flow.
PMC G/R writes land on a running machine — be careful which file you hit. Parameter writes may require the CNC to be in MDI mode with the parameter-write switch enabled.
Server-enforced ACL — issue #269, plan PR F4-b
When the same write flows through the OtOpcUa server (rather than the CLI's direct-to-CNC path), the server-layer ACL gates by tag kind:
PARAM:writes requireWriteConfiguregroup membership — heavier ACL because a misdirected parameter write can put the CNC in a bad state.MACRO:writes requireWriteOperate— matches the standard HMI recipe / setpoint surface.- PMC R/G/F writes require
WriteOperate.
The classification is declared by the FOCAS driver per tag and enforced by
DriverNodeManager; the driver itself never inspects user identity. See
docs/security.md for the full LDAP-group → permission
mapping, docs/v2/acl-design.md for the design, and
docs/v2/focas-deployment.md "Write safety" for
the operator pre-check runbook (MDI mode, parameter-write switch).
Writes are non-idempotent by default — a timeout after the CNC already applied the write will NOT auto-retry (plan decisions #44 + #45).
Server-side Writes enforcement (issue #268 F4-a + #269 F4-b + #270 F4-c)
The OtOpcUa server gates every FOCAS write behind multiple independent
opt-ins: FocasDriverOptions.Writes.Enabled (driver-level master switch),
Writes.AllowParameter (PARAM kill switch — F4-b), Writes.AllowMacro
(MACRO kill switch — F4-b), Writes.AllowPmc (PMC kill switch — F4-c),
and FocasTagDefinition.Writable (per-tag). All default false; any one
off short-circuits the server-side WriteAsync to BadNotWritable before
the wire client is touched. See docs/drivers/FOCAS.md
"Writes (opt-in, off by default)" subsection +
docs/v2/decisions.md for the decision record.
The CLI bypasses the server-side flag. otopcua-focas-cli write is a
per-invocation operator tool — it sets Writes.Enabled = true locally for
the lifetime of one process and creates the synthesised tag with
Writable = true. This is intentional: the CLI is the operator's
direct-to-CNC fallback, not a long-lived process bound to the central
config DB. Configuring the server still requires both opt-ins to be set
explicitly in the DriverInstance JSON.
subscribe — watch an address until Ctrl+C
FOCAS has no push model; the shared PollGroupEngine handles the tick
loop.
otopcua-focas-cli subscribe -h 192.168.1.50 -a R100 -t Int16 -i 500
Output format
Identical to the other driver CLIs via SnapshotFormatter:
probe/reademit a multi-line block:Tag / Value / Status / Source Time / Server Time.probeprefixes it withCNC,Series,Health, andLast errorlines.writeemits one line:Write <address>: 0x... (Good | BadCommunicationError | …).subscribeemits one line per change:[HH:mm:ss.fff] <address> = <value> (<status>).
Typical workflows
"Is the CNC alive?" → probe.
"Does my parameter write land?" → write + read back against the
same address. Check the parameter-write switch + MDI mode if the write
fails.
"Why did this macro flip?" → subscribe to the macro, let the
operator reproduce the cycle, watch the HH:mm:ss.fff timeline.
"Is the Fwlib32 DLL wired up?" → probe against any host. A
DllNotFoundException surfacing as BadCommunicationError with a
matching Last error line means the driver is loading but the DLL is
missing; anything else means a transport-layer problem.