Tools
uatools ships nine runnable tools, all installed into $PREFIX/bin/.
Section titles carry a (Kind/Language) tag — CLI vs. GUI vs.
Server, C++ vs. Python — so you can scan the chapter for the right
tool at a glance.
UaClient (CLI/C++)
Command-line OPC UA client built on ualib (the C++ library
described in the previous chapter). UaClient is a thin wrapper that
exposes the full ICommAdapter surface — the seven operations
described below — as scriptable shell commands. It is the natural way
to verify a server works, run a one-shot read/write from a shell
script, or watch live telemetry without writing C++ code.
UaClient --uri opc.tcp://127.0.0.1:4840 status
UaClient -u 127.0.0.1:4840 status # -u is an alias for --uri
UaClient --uri 4840 status # bare port -> 127.0.0.1
Like UaShell, UaClient accepts a bare port number or a host:port
pair for -u / --uri and rewrites it to the canonical opc.tcp://...
form. The bare-port form resolves to 127.0.0.1 (the literal IP,
not the name “localhost”) so the shortcut works even where
/etc/hosts resolves “localhost” to an IPv6 address before IPv4.
Subcommands. The CLI lays out the operations one-per-subcommand:
|
Connect, print server info (endpoint, protocol, adapter name + version, contract version), disconnect. |
|
Read one or more nodes and print each value. |
|
Write one node. Two-arg form ( |
|
List the children of a node. |
|
Invoke an OPC UA Method. Arguments are given as
|
|
Listen for data-change notifications on one or more
nodes for |
|
|
|
Register a |
Options. -l/--log-level LEVEL controls both ualib’s FND*
output and the open62541 SDK’s internal logger together
(trace|debug|info|warning|error|off; default warning).
--uri URI is required (except for --help). --user U --pass P
selects username/password authentication.
Exit codes. 0 on success, 1 on usage error, 2 on
connection failure, 3 on operation failure.
Full usage is available via UaClient --help — each subcommand has
its own block with two to four concrete examples (NodeId formats,
write-with-explicit-type variants, browse -r --values, method
call with typed args, etc.).
UaExplorer (GUI/Python)
PyQt6-based OPC UA browser + inspector. Connect to any OPC UA server, browse its address space, read/write attributes, call methods, subscribe to live updates, and run user-supplied scripts against the selected nodes.
UaExplorer
The server is selected from within the GUI (the connection bar / server
list and the connect action), not on the command line. UaExplorer’s
only command-line options are -l/--log-level, --console-log and
--home-dir (see UaExplorer --help); passing an unknown option
(for example --uri) is rejected with a usage error.
UaExplorer: address-space tree, node attribute panel, and the live Subscriptions panel.
Key features:
Address-space tree view with attribute panel for the selected node.
Read/write of scalar and array values.
Method invocation with argument editor.
Subscriptions panel with live updates, optional Min/Max/Mean/Count statistics columns, and an undock button (
U) that floats the panel into its own window so a long subscription list has room to breathe (or can move to a second monitor).“Plot” action that launches UaPlot with the selected variable (uses the
eso.uatools.tools.uaplotclient library underneath).Scriptable: user Python scripts under
$INTROOT/resource/scripts/uaexplorer/can manipulate the selected node using built-ins (Read,Write,Execute,Wait,WaitUntil,Log,Store,Iso,Abort).Per-server friendly names (a URI can be labelled, e.g.
MyLocalTstSrv); the name is remembered across sessions and shown next to the URI in the connection bar and in the URI drop-down.Full traversal of structured Variables — OPC UA Variables that carry child nodes (e.g. PLC DataBlocks such as
CCS_CMD_DB/DATAon TwinCAT/Siemens servers). Their children are browsed like folders, so deep DataBlock structures are fully navigable.Themes (Light/Dark/Atacama), session save/restore.
Handling large namespaces (Lazy Node Loading)
Large servers (PLCs can expose 80000+ nodes) are expensive to browse in full. UaExplorer therefore supports Lazy Node Loading, configured per server URI on the connection bar:
Lazy checkbox + limit — when Lazy is enabled (default), the namespace is browsed only up to the configured node limit (default
10000). If the whole namespace fits under the limit it is loaded completely; otherwise loading stops at the limit and the remaining subtrees load on demand. A not-yet-loaded node shows a(not loaded — expand to load)placeholder child. Uncheck Lazy (or set the limit to0) to always load the whole namespace. The setting is saved per URI. Turning Lazy off while connected offers to load the full namespace immediately (a full Rebrowse), since the loaded tree is otherwise still only the previous partial set.Load-on-scroll — not-loaded nodes load automatically as they scroll into view, so the tree fills itself in while you browse without clicking each one. Loading is debounced and only the visible nodes are fetched, a few at a time, so it stays responsive and does not flood the server. Expanding a node also loads its children explicitly.
Expand button — the toolbar Expand expands only the nodes that are already loaded; it does not pull in the rest of a partially-loaded namespace (which on a large PLC would be slow). To load more, scroll to the nodes you want, or raise / disable the Lazy limit.
Load-status badge — a badge on the connection bar shows whether the namespace is loaded
COMPLETE(green) or onlyPARTIAL(orange, with the loaded node count) so it is always clear whether more nodes may exist below the currently loaded tree. When the active View scopes the browse (see below) a complete load showsCOMPLETE (N) [View]— the full set of nodes the View allows, not the whole namespace.Partial-result warnings — features that work across the whole namespace operate only on the loaded nodes while the namespace is
PARTIAL. A Quick Filter search then shows a status-bar notice that results may be incomplete, and the Export Namespace dialog shows a warning (both Quick and Full dump export only the loaded nodes — Full dump adds their values but does not fetch missing ones). Disable Lazy loading and Rebrowse first for complete results.Quick Filter node limit — the inline Filter box normally filters the tree live as you type. On a namespace larger than the Quick Filter node limit (a global setting under Preferences → Node Browser, default
10000) live filtering is disabled — you type a filter and press Return to run the search explicitly, which avoids freezing the GUI while it rebuilds a very large tree. The filter text is orange while a search is pending and turns green once it has run; a small spinner next to the Filter label shows while the search is in progress.
View-aware browse
A View (the drop-down next to the Node Browser) filters which nodes are shown. For large servers UaExplorer can also use the active View to browse fewer nodes from the server, not just hide them after the fact:
Browse pruning — when a View only excludes subtrees (e.g. the built-in TwinCAT view hides
/Server,/Types…) or includes a fixed subtree (e.g. an include of/system/subsys001), those decisions are applied during the browse itself, so the excluded / out-of-scope subtrees are never fetched. This can turn a 25-second full browse into a fraction of that. Views that use arbitrary wildcard patterns can’t be pruned safely and fall back to a full browse with render-time filtering (the result is identical, just not faster).Include wins over exclude — a node that is explicitly included (or lives under an included subtree) is shown even if it also matches an exclude, so you can “exclude broadly, then re-include one subtree” to cherry-pick.
Auto-Rebrowse on View change — switching to a View that changes what the browse loads reloads automatically; switching between Views that only change the display re-filters instantly without a server round-trip.
Per-URI memory — the last View used for a server is remembered and restored the next time you connect to that URI.
Cancelling and estimating a long browse
Cancel — while a namespace is loading, the Connect button becomes an orange Cancel. Cancelling keeps whatever was loaded so far (the tree becomes
PARTIAL) — useful when a large PLC is taking too long.Progress and ETA — the status bar shows the node count and elapsed time during a browse. Once a server’s full namespace has been loaded once, the time it took is remembered per URI and used to show an estimated time remaining on the next load. The estimate covers browsing and building the tree; the tree build is a brief synchronous step shown as
Rendering tree.... (A namespace loaded from the persistent cache below is near-instant, so its load time is never learned as the browse estimate.)
Persistent namespace cache
Each full browse is saved to an on-disk SQLite cache
(~/.uatools/UaExplorer/nscache.db). Reconnecting to that server then
populates the tree instantly from disk — no browse — and revalidates it
with a full rebrowse in the background, swapping the refreshed namespace in
when it completes. So after the first connect to a large server, later
connects are effectively instant, and a stale cache self-heals within a
minute of connecting.
It is a global setting, on by default, toggled under Preferences → Node Browser → “Cache namespaces on disk”. Unchecked, every connect browses the server live.
Only full, unscoped browses are cached — a Lazy-partial or View-scoped browse never overwrites the canonical cache.
Rebrowse forces a fresh live browse and refreshes the on-disk cache.
Which to use — lazy loading vs Views vs the cache
The three mechanisms cooperate; for most users the defaults (lazy on, cache on) are all that is needed:
Do nothing. Your first connect to a big server loads up to the Lazy limit fast and defers the rest (they load as you expand); that browse is cached, so every later connect is instant and complete.
Use a View when you only ever care about part of a server — it browses far fewer nodes and keeps the tree focused. The tool for “I never want to see the rest of this namespace.”
Tune the Lazy limit only if the first connect is still too slow (lower it) or you want everything up front (turn Lazy off). Once the cache holds a full copy, the Lazy limit barely matters — the full namespace is loaded from disk and Lazy/Views act as display filters, not load-time savers.
Turn the cache off only if on-disk state is unwanted; you then pay the full browse on every connect, so Lazy loading and Views matter much more.
The Subscriptions panel undocked into its own window — useful when monitoring many nodes at once.
UaPlot (GUI/Python)
Live-plotting GUI for OPC UA variables (PyQt6 + pyqtgraph).
UaPlot --uri opc.tcp://localhost:4840
UaPlot showing several live variables. Multiple plots auto-arrange into a fit-to-window mosaic (e.g. four plots tile 2x2).
Can run standalone or be driven programmatically from another Python
process via the eso.uatools.tools.uaplot client library:
from eso.uatools.tools.uaplot import UaPlotClient
client = UaPlotClient()
client.start(uri="opc.tcp://localhost:4840")
client.create_plot("Temperature Monitor")
client.plot_variable(node_id="ns=4;s=MAIN.temp",
display_name="Sensor 1")
Communication between the client library and the UaPlot process is
JSON-over-QLocalSocket. See the UaPlot README in
tools/uaplot/ for the full API.
UaShell (CLI/Python)
A bash-like interactive shell and scripting client for OPC UA.
The design goal is a familiar “unix shell” feel for navigating and
manipulating a server’s address space — you cd into the
namespace, ls nodes, read/write values, call methods,
and pipe/redirect output just like a real shell.
Interactive mode:
UaShell --uri opc.tcp://127.0.0.1:4840
UaShell -u 127.0.0.1:4840 # host:port shorthand
UaShell -u 4840 # bare port -> opc.tcp://127.0.0.1:4840
UaShell # prompts for an endpoint
The bare-port and host:port shorthands save typing during local
development. UaShell --help lists every flag.
A UaShell session: navigating the namespace, finding nodes with a pipeline, and subscribing to several variables (note the live subscription count and named-server prompt).
Navigation & inspection (filesystem mental model):
cd/pwd/ls/ll/tree— move around and list the address space as if it were directories.read/write/call— read a value, write a value, or invoke a method (Method()syntax also works directly).find— recursively search for nodes by name/type.info— show full attribute detail for a node.
Bash-isms:
Pipes to external programs:
find | grep -i temp | sort.xargs pipe target:
find -type v -name '*Temp*' | xargs subscribefeeds the pipe tokens as positional args to an internal UaShell command. Saves retyping node paths afindalready enumerated. Empty input is a silent no-op. Seehelp xargs.Redirection:
tree Server > server.txt,... >> append.txt.Globbing (segment-wise, like bash):
ls Objects/*,subscribe system/*/fcs/sensor*/Temperature(subscribe/plot globs expand to variables only).History recall:
!42re-runs command 42, and works inside;-chains (!42;!43).Command chaining with
;.Tab completion is context-aware: at the prompt it offers commands and methods in the current directory; mid-command it offers paths, subcommands, flags or specific value sets depending on what the command expects. For example,
subscribe rm <Tab>completes from the live subscription set (not the whole namespace),plot rm <Tab>from the cached plot refs,find -<Tab>from the-name/-iname/-type/-maxdepthflag set, andset color <Tab>fromon/off. Completion candidates are colour-coded by semantic role (folders bold blue, methods bold green, variables green, flags yellow, commands bold cyan, subcommands bold magenta), matching the same palette used elsewhere in UaShell.External commands via pipe —
help <name>recognises common external pipe targets (grep,sort,awk,head,wc,xargsetc.) and points the user atman <name>rather than printing “no help available”.
Live data:
subscribe <node>— open a separate UaSubscription viewer window (PyQt6) that shows live values; the prompt carries a[N]active-subscription count.subscribe list/rm/closemanage them. Closing the viewer window unsubscribes everything.plot <name> <node>— open UaPlot and plot variables;plot list/plot rm <ref>/plot rm <ref> <node>manage plots and individual curves.
Server naming:
set name <alias>labels the current server URI (shown in the prompt as[alias], persisted across sessions);connect <alias>then reconnects by name.
Large namespaces — lazy loading, Views, and the persistent cache:
On connect, UaShell browses and caches the server’s namespace for instant
navigation. On a large server (e.g. a PLC with tens of thousands of nodes)
a full browse is costly (tens of seconds), so three cooperating mechanisms
keep it responsive: lazy loading (browse fewer nodes now, load the rest
on demand), Views (permanently scope to the subtrees you care about),
and the persistent cache (reuse the last full browse from disk on
reconnect). Use settings to see the current state (colour, name, lazy
state, active view, loaded node count, the remembered full-load time, and
whether the cache is on).
Which to use — the short version:
Do nothing. The defaults are tuned for this: lazy loading is on (limit 10000) and the persistent cache is on. Your first connect to a big server loads the first 10000 nodes fast and defers the rest (they load as you navigate); that browse is saved to disk, so every later connect is effectively instant and complete. For most users this is all they need.
Reach for a View when you only ever care about part of a server (e.g. one instrument’s subtree). A scoped view browses far fewer nodes, which helps the first connect and keeps
findfocused. Views are the tool for “I never want to see the rest of this namespace.”Tune the lazy limit only if the first connect is still too slow (lower it) or you want everything up front (
set lazy off). Once the persistent cache has a full copy, the lazy limit barely matters — the cache is loaded whole from disk and lazy/views act as display filters, not load-time savers.Turn the cache off (
set nscache off) only if on-disk state is unwanted (shared/locked-down home, privacy). You then pay the full browse on every connect, so lazy loading and Views matter much more.
The three are configured with set (lazy and view are per URI; nscache is
global) and detailed below.
Lazy loading browses breadth-first only up to a node limit, deferring the rest. A new URI defaults to lazy on with a limit of 10000.
set lazy on|offandset lazy limit <N>(0= no cap = off).A capped browse is reported as
PARTIAL; the prompt shows the loaded count and a+when more is deferred, e.g. the prompt shows the loaded count in angle brackets like[srv]<10000+>:/path>.Deferred subtrees load automatically when you
cd/ls/findinto them.scan <path>loads a subtree explicitly, andscan(no argument) loads everything still deferred.rebrowsere-browses from scratch under the current settings.While browsing, an ETA is shown once a full load of that server has been timed once.
Views scope the browse to chosen subtrees, so a scoped view loads far fewer nodes. Views are format-compatible with UaExplorer and shared: a view defined in the GUI is usable here and vice versa. On a name clash, UaShell’s own view (under
~/.uatools/UaShell/views/) wins over UaExplorer’s (under~/.uatools/UaExplorer/views/).view list/view show [<name>]— inspect available views.view create <name>thenview include <pattern>/view exclude <pattern>— author a view (include wins over exclude, subtree-aware).view rm <name>deletes one of your own views.set view <name>applies a view to the current URI (set view -clears it);rebrowseto re-scope. The active view shows in the prompt in braces, e.g.[srv]{plc-core}:/path>.A leading-slash path pattern (
/Objects/system, optionally/*or/**) scopes to that subtree and is efficiently pruned at browse time; a bare word is a substring match and mid-path globs still filter but cannot prune (the whole namespace is walked).
Persistent cache (
set nscache on|off, global, on by default). Each full browse is saved to an on-disk SQLite cache (~/.uatools/UaShell/nscache.db). Reconnecting loads the namespace instantly from disk — no browse — then revalidates it with a full rebrowse in the background, swapping the refreshed namespace in when it completes (a brief(namespace refreshed: N nodes)note appears only if it changed). So a stale cache self-heals within a minute of connecting, and you rarely wait for a browse after the first one. Only full, unscoped browses are persisted — a lazy-partial or view-scoped browse never overwrites the canonical cache.rebrowseforces a fresh live browse and refreshes the on-disk cache.
Export:
exportwrites the namespace to TXT, CSV, JSON, YAML, or OPC UA NodeSet2 XML (export -x) — the XML form loads in UaModeler, Prosys, andasyncua’simport_xml.
Scripts — two kinds, run with UaShell -s run <name> (batch) or
the interactive run/script commands:
.uas— a saved sequence of UaShell commands (command playback)..py— a Python script definingdef main(sh):with the same synchronous built-ins as UaExplorer (Read,Write,Execute,Wait,WaitUntil,Log,Store,Iso,Verbose,Abort), so the scripting experience is identical in both tools. A new.pystub is generated with a header listing all built-ins. An example lives intools/uashell/examples/.
# example .py script (synchronous built-ins, no await)
def main(sh):
Verbose(INFO)
t = Read("system/subsys001/fcs/sensor1/Temperature")
Write("system/subsys001/fcs/motor1/Setpoint", 20.0)
Execute("system/Init")
WaitUntil("system/subsys001/fcs/motor1/State",
lambda v: v == "READY", timeout=10)
Store(Iso(), t) # tab-separated row -> .dat
Log("cycle complete")
Manage saved scripts with the script subcommands
(list / cat / edit / rm / mv / run / save).
Scripts live under ~/.uatools/UaShell/scripts/.
UaShell can also be used as a Python library:
from UaShell import UaShell
shell = UaShell("opc.tcp://localhost:4840")
await shell.connect()
await shell.cmd_read(["ns=2;s=TestDouble"])
The separate subscription viewer is shown below.
The UaSubscription viewer, launched by UaShell’s subscribe
command, displaying live values for the subscribed nodes.
UaSubscription (GUI/Python)
Live subscription viewer (PyQt6): a window holding one row per subscribed
node — Node ID, Name, Value, Type, Quality, Timestamp — that updates each
row in place as notifications arrive. It is aligned with UaPlot: same panel
menu, the same Server: <uri> / Name: <name> [/ Session: <session>]
header, and the same recording controls.
It runs in two modes:
Driven by UaShell (push mode). UaShell’s subscribe command spawns
a UaSubscription viewer for the subscribed nodes (one viewer per UaShell
session, reused for repeated subscribe calls). Here UaShell owns the
OPC UA connection and pushes value updates to the viewer over local IPC;
the viewer’s URI and friendly name are shown in the header for reference.
In this mode the connection belongs to UaShell, so Add/Remove and
Load Session are disabled.
Standalone. Launched on its own, the viewer connects to the server itself and is fully autonomous:
UaSubscription --uri opc.tcp://localhost:4840
(There are no positional node arguments — subscriptions are added from the GUI.) In standalone mode you can:
Add subscriptions via a live address-space browse with a quick
a+bAND filter (e.g.sens+temp), multi-select; Remove them from a dialog, the right-click menu, or the Delete key.Save / Load sessions (URI, theme, recording settings, subscriptions) — the session JSON shares the UaExplorer / UaPlot shape.
Record the table to CSV (same RFC-4180 + comment-header format as UaPlot), with rate / mode / limit / file controls.
Statistics. Optional Min / Max / Mean / Count columns
(hidden by default; enabled in Preferences) compute over a sliding window
of recent numeric samples per row, mirroring UaExplorer’s Subscriptions
panel. Non-numeric rows show -; Count is the total samples received.
Preferences offer the theme, the table font size, the Recording group, and the Statistics group. Three themes (Light / Dark / Atacama) match the rest of the tools.
UaTstServer (Server/Python)
An asyncua-based OPC UA test server. Convenient when you need a
running OPC UA endpoint for development or integration testing without
installing a “real” server. The Python tools and the C++ UaClient
(plus all integration tests) are exercised against it.
UaTstServer -i 127.0.0.1 -p 4840
The server advertises the namespace urn:ifw:uatools:pysrv and builds a
realistic, control-system-shaped address space rather than a flat bag
of variables — so it is useful for testing browsing, subscriptions,
plotting and method calls against something close to a real instrument.
Address space. A system root holds a configurable number of
subsystems (subsys001, subsys002, …), each containing device
groups such as fcs (function control) and cameras. Typical
devices and their methods:
lamp —
On(intensity),Offmotor —
MoveAbs(position, velocity),MoveVel(velocity),Stop; livePosition/Target/Velocityvariablessensor — telemetry variables (
Temperature,Flow,Pressure) whose values drift over time, so subscriptions and plots show moving datacamera —
StartAcq,StopAcq
Every device exposes a small state machine: a State variable
plus the common methods Init, Enable, Disable, Reset.
This makes it easy to script realistic sequences (e.g.
Reset → Init → Enable → MoveAbs(...)) — see the
motor_demo.py example under tools/uashell/examples/.
Namespace size is selectable, so you can test both interactive use and heavy-load browsing:
|
Subsystems |
Approx nodes |
|---|---|---|
|
5 |
~4 000 |
|
10 |
~15 000 |
|
10 |
~100 000 |
|
10 |
~200 000 |
Authentication is optional. With -a/--authentication the server
requires a username/password (the built-in test credentials are
user / user_pswd); without it the endpoint is anonymous.
Full command line:
UaTstServer -i/--ip <addr> -p/--port <port> [-a/--authentication]
[-s/--size s|m|l|h] [-l/--log-level LEVEL|logger:LEVEL]
[--list-loggers]
UaItestServer (Server/C++)
A C++ test server (open62541-based, the same stack the protocol
library uses). Unlike UaTstServer, whose address space is fixed in
code, UaItestServer builds its namespace from a YAML description
— so you define exactly the nodes, types and behaviour you want. It is
used by the C++ integration tests to spin up a predictable server on a
free port, and is equally handy for downstream-project tests:
UaItestServer -c opcua_server.yaml -e opc.tcp://127.0.0.1:4840
Creating a namespace
The config file has four top-level sections: server (endpoint +
namespace URI), folders (organisational objects), nodes
(variables) and methods. A minimal example:
server:
endpoint: "opc.tcp://0.0.0.0:4840"
namespaceUri: "http://example.com/integration-test/"
folders:
- name: "TestScalars"
nodeId: "TestScalars"
parent: "Objects" # parent folder or "Objects" (the root)
nodes:
- name: "TestDouble"
nodeId: "TestDouble"
datatype: "Double" # Boolean/Int32/Float/Double/String
initialValue: 23.5
writable: true
folder: "TestScalars" # which folder the node lives in
Add an array by setting isArray: true and initialValues
(a list) instead of initialValue:
nodes:
- name: "TestDoubles"
nodeId: "TestDoubles"
datatype: "Double"
isArray: true
initialValues: [21.5, 22.0, 23.3]
writable: true
folder: "TestArrays"
Give a node time-varying values with a simulation block —
useful for exercising subscriptions and plots. Two modes are
supported, sine and ramp:
nodes:
- name: "TempSine"
nodeId: "TempSine"
datatype: "Double"
initialValue: 20.0
folder: "TestScalars"
simulation:
type: "sine"
intervalMs: 1000 # update period
periodMs: 10000 # full sine cycle
amplitude: 5.0
offset: 20.0
- name: "LevelRamp"
nodeId: "LevelRamp"
datatype: "Int32"
initialValue: 0
folder: "TestScalars"
simulation:
type: "ramp"
intervalMs: 500
step: 1.0
min: 0.0
max: 100.0
Define methods with input/output arguments and a fixed
simulation that returns canned output values:
methods:
- name: "AddInt32"
nodeId: "AddInt32"
inputArguments:
- name: "A"
datatype: "Int32"
description: "First operand"
- name: "B"
datatype: "Int32"
description: "Second operand"
outputArguments:
- name: "Sum"
datatype: "Int32"
description: "Result A + B"
simulation:
type: "fixed"
outputs: ["8"] # canned return value(s)
The parser (and the authoritative list of supported keys and
datatypes) lives in itest/lib/src/server.cpp; itest/server/
is a thin CLI wrapper around the library so the same address-space
machinery is reused by the C++ integration-test fixture. A small
ready-to-use YAML fixture lives at
test/uatools/resource/uaitestserver.cfg.yaml (folders + scalar
variables + a method) and is exercised by the robot suite
test/uatools/src/uaitestserver.robot.
UaStressTest (CLI/C++)
YAML-driven load and stress driver for ualib. The same binary
targets a synthetic server (UaTstServer, UaItestServer,
Prosys) or a real PLC (TwinCAT, Siemens, B&R, …); the YAML
config describes which nodes are readable / writable /
subscribable, and which named scenarios should be available. A
scenario is picked at the CLI and run on N worker threads for a
chosen duration, after which a structured report is printed.
UaStressTest --config <yaml> --scenario <name> [options]
-c, --config <path> YAML config (required)
-s, --scenario <name> scenario to run (or ``all``)
-t, --threads <N> worker threads (default: 1)
-d, --duration <sec> run duration in seconds (default: 30)
-r, --report <path> also write report to this file
-l, --log-level <lvl> TRACE|DEBUG|INFO|WARNING|ERROR|OFF
(default: WARNING)
Scenario kinds (iteration 1). Five kinds, all driven through ualib::Client:
|
Each thread reads the entire |
|
Each thread writes random values into the
|
|
Each thread subscribes to the |
|
Each thread walks the configured method cycle
(e.g. |
|
Each thread cycles Connect / Disconnect on the
shared |
All worker threads share one ualib::Client instance, which
is the recommended ualib usage pattern and is what UaStressTest
puts under pressure. A future iteration will add a multi-Client
mode (one Client per thread).
A worked example config lands at
$PREFIX/resource/config/eso/uatools/uastresstest/ and a
TwinCAT-targeted starter at example/twincat_motor.yaml.
UaWatch (CLI/C++)
Per-session subscription event-log tool. Companion to
UaStressTest: where UaStressTest measures aggregate
throughput and latency under load, UaWatch produces a
per-event log so the operator can correlate physical actions
(reboot PLC, kill server, drop network) with what each client
session sees on the wire.
Each watcher in the YAML config runs in its own thread and owns
its own ualib::Client instance — so three watchers means three
independent sessions to the same server. This is the right shape
for the question “do multiple sessions react the same way when the
server goes away?” — the answer is sometimes “yes” and sometimes
“no”, and the timeline of state transitions per watcher reveals
which.
UaWatch --config <yaml> [options]
-c, --config <path> YAML config (required)
-d, --duration <sec> stop after N seconds (0 = until Ctrl+C)
-L, --logfile <path> also write the log to this file
-l, --log-level <lvl> ualib SDK log threshold (default: WARNING)
--no-color disable ANSI colour escapes
Output is one line per event, with an ISO-8601 timestamp and the watcher tag, e.g.:
2026-06-02T15:32:10.012345Z [motor1] state Disconnected -> Connecting reason=Ok
2026-06-02T15:32:10.318112Z [motor1] notify ns=2;s=...Position = 42.7 (srcTs=15:32:10Z)
The default config at
$PREFIX/resource/config/eso/uatools/uawatch/uatstserver_default.yaml
matches UaTstServer’s standard device tree (motor / lamp / sensor
nodes) so the tool can be run against the test server straight
out of the box.