Headless simulator
pdcomm-opcua-headless is the OPC UA simulator without a window. It runs the server, client and PubSub with the same engine as the GUI, for scripts, automated tests and CI. You find it in bin/ of the apps package.
Usage: pdcomm-opcua-headless [--advertised-host host] [--scenario path]Without arguments it runs in interactive mode; with --scenario it runs a scenario. --advertised-host sets server.advertised_host of the initial configuration (see Configuration). Any other argument prints the usage, and a host name it can’t use prints Advertised host must be a host name or an IP address; both exit with code 2.
Interactive mode
Section titled “Interactive mode”The program reads one JSON object per line from standard input and writes one reply per line to standard output:
{"action":"read","nodes":["ns=2;s=Simulator.Double"]}- A request is an object with an
actionand the action’s arguments. - A successful reply is
{"ok":true,"result":...}; a failed one is{"ok":false,"error":"..."}. Error texts are in English. - A line that isn’t JSON gets a parse error such as
{"error":"expected ident at line 1 column 2","ok":false}. An unknown action getsUnknown service action. - A line can be up to 1 MiB. A longer line gets
Input exceeds one MiBand the program ends. - Standard output carries replies only; diagnostics go to standard error or the configured log file.
- The simulator keeps servicing its client connection while it waits for commands.
Two requests are special:
| Request | Effect |
|---|---|
{"action":"cancel"} |
Cancels the action that is running. The cancelled action ends with {"error":"Cancelled","ok":false}, and this request returns {"ok":true,"result":{"cancel_requested":true}}. When nothing is running, cancel_requested is false. |
{"action":"shutdown"} |
Cleans up the client, PubSub and the server in turn and exits. The end of standard input (EOF) does the same. |
The exit code is 1 if any command failed and 0 otherwise.
Scenario mode
Section titled “Scenario mode”A scenario file is a JSON array of steps. The apps package ships a scenario that visits every workspace:
[ {"command": {"action": "server_start"}, "expect": {"running": true}}, {"command": {"action": "connect"}, "expect": {"connected": true, "policy": "None"}}, {"command": {"action": "browse", "node": "ns=2;s=Simulator"}, "expect": {"good": true, "more": false}}, {"command": {"action": "write", "node": "ns=2;s=Simulator.Double", "type": "Double", "value": 2.5}, "expect": {"good": true}}, {"command": {"action": "write", "node": "ns=2;s=Simulator.Counter", "type": "UInt32", "value": 1}, "expect": {"good": false, "status": "BadNotWritable (0x803B0000)"}}, {"command": {"action": "read", "nodes": ["ns=2;s=Simulator.Double", "ns=2;s=Simulator.Protected"]}}, {"command": {"action": "call", "method": "ns=2;s=Simulator.Add", "arguments": [{"type": "Double", "value": 1.5}, {"type": "Double", "value": 2}]}, "expect": {"good": true, "outputs": [{"type": "Double", "value": 3.5}]}}, {"command": {"action": "subscribe", "nodes": ["ns=2;s=Simulator.Double"], "interval_ms": 250}, "expect": {"subscription": 1}}, {"command": {"action": "publish", "count": 2}}, {"command": {"action": "event_subscribe"}, "expect": {"good": true}}, {"command": {"action": "alarm_set", "active": true}, "expect": {"good": true}}, {"command": {"action": "publish", "count": 2}}, {"command": {"action": "condition_refresh"}, "expect": {"good": true}}, {"command": {"action": "history_read", "node": "ns=2;s=Simulator.Double"}, "expect": {"good": true}}, {"command": {"action": "pubsub_start"}, "expect": {"running": true, "transport": "udp"}}, {"command": {"action": "pubsub_publish"}, "expect": {"sequence": 1}}, {"command": {"action": "pubsub_stop"}, "expect": {"running": false}}, {"command": {"action": "connect"}, "expect_error": true}, {"command": {"action": "disconnect"}, "expect": {"connected": false}}, {"command": {"action": "server_stop"}, "expect": {"running": false}}]| Field | Meaning |
|---|---|
command |
The request to run, the same as one line in interactive mode. |
expect |
Fields and values to check in the result object; only the listed fields are compared. |
expect_error |
When true, the step must fail. |
Run it:
"$OPCUA_APPS/bin/pdcomm-opcua-headless" --scenario "$OPCUA_APPS/examples/opcua/scenarios/simulator-tour.json"Each step prints one line, {"result":...,"step":N}. The beginning and the end look like this (long lines omitted):
{"result":{"audit":[],"endpoint":"opc.tcp://localhost:63408/pdcomm/simulator","error":null,"running":true,"secure":true,"sessions":0},"step":1}...{"result":{"good":false,"node":"ns=2;s=Simulator.Counter","status":"BadNotWritable (0x803B0000)"},"step":5}...{"result":{"error":"The client is already connected"},"step":18}{"result":{"connected":false},"step":19}{"result":{"running":false},"step":20}When every step meets its expectations, the exit code is 0. Otherwise the run stops at that step, writes the reason to standard error and exits with code 1:
| Standard error | Meaning |
|---|---|
Scenario step 2 field policy did not match |
A field in expect differs from the result. |
Scenario step 1 failed: The client is not connected |
A step that should succeed failed. |
Scenario step 1 succeeded unexpectedly |
A step marked expect_error succeeded. |
Scenario files can’t exceed 1 MiB or 10000 steps. Scenario mode doesn’t wait between steps, so check results that take time, such as received PubSub messages, in interactive mode.
Configuration
Section titled “Configuration”configure takes a configuration object. You can pass only some sections or fields, but every field you leave out gets its default value, not its current one. To change a few fields, get the current configuration with state, edit it and pass it back. You can change the section of a running server, client or PubSub only after stopping it; otherwise you get Stop the endpoint before changing its configuration. Check the current configuration with {"action":"state"}.
{"action":"configure","config":{"server":{"address":"127.0.0.1:4841"},"client":{"policy":"Basic256Sha256","mode":"SignAndEncrypt"}}}server
| Field | Default | Description |
|---|---|---|
address |
127.0.0.1:0 |
Listen socket address; port 0 picks a free port. |
path |
pdcomm/simulator |
Path of the endpoint URL |
advertised_host |
Empty | The host name (HostName) the server puts in the URLs of GetEndpoints, FindServers and CreateSession responses. When empty, each client gets back the host and port it used to connect. Set a name or an IP address (IPv6 without brackets) to always return that name; the status endpoint uses it too. |
application_uri |
urn:pdcomm:opcua:server |
ApplicationUri; must start with urn:. |
allow_none |
true |
Advertises the None endpoint. |
secure |
true |
Advertises signed and encrypted endpoints; it and allow_none can’t both be false. |
allow_anonymous |
true |
Allows anonymous users. |
user_name |
operator |
The user name to accept; the password goes to server_start. |
certificate, private_key |
Empty | Server certificate and private key files (PEM or DER). When empty, a self-signed certificate is created in memory; it lists localhost, the listen IP address (except 0.0.0.0) and advertised_host. |
trust |
[] |
Client certificate files to trust (up to 64) |
simulation_interval_ms |
500 |
The Simulator.Counter interval: 0, or 50 to 60000 |
max_sessions |
8 |
Maximum sessions (1 to 32) |
client
| Field | Default | Description |
|---|---|---|
url |
Empty | The opc.tcp URL to connect to; empty means this simulator’s server. |
policy, mode |
None, None |
Security policy and mode; None only when both are None. |
identity |
anonymous |
anonymous or username |
user_name |
operator |
User name; the password goes to connect. |
application_uri |
urn:pdcomm:opcua:client |
ApplicationUri |
certificate, private_key |
Empty | Client certificate and private key files; created in memory when empty. |
trust |
[] |
Server certificate files to trust. The certificate of this simulator’s own server is trusted automatically. |
timeout_ms |
5000 |
Timeout (100 to 60000 ms) |
pubsub
| Field | Default | Description |
|---|---|---|
transport |
udp |
udp or mqtt |
role |
both |
both, publisher or subscriber |
listen |
127.0.0.1:0 |
UDP subscriber listen address |
destination |
Empty | UDP publisher destination; empty means the subscriber in the same process |
broker |
127.0.0.1:1883 |
MQTT broker IP address and port |
topic |
pdcomm/opcua/simulator |
MQTT topic, without wildcards |
publisher_id, writer_group_id, dataset_writer_id |
77, 1, 17 |
PubSub identifiers; must not be 0 |
limits: notification_queue (notifications kept) and trace_entries (action records kept), each 1 to 1000, default 1000.
Typed values
Section titled “Typed values”Arguments that carry values (write, server_write, the arguments of call and the fields of pubsub_publish) take a type and a value:
{"type":"Double","value":12.5}| Type | Value format |
|---|---|
Boolean |
true or false |
SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64 |
An integer within range |
Float, Double |
A number |
String |
Text up to 64 KiB |
LocalizedText |
Text |
DateTime |
Unix milliseconds |
ByteString |
A hexadecimal string |
NodeId |
NodeId text such as ns=2;s=Name |
NodeId text accepts numeric (i=) and string (s=) identifiers. All timestamps in results are Unix milliseconds.
Actions
Section titled “Actions”| Workspace | Action | Arguments |
|---|---|---|
| connection | capabilities |
None. Returns the supported features and limits. |
| connection | state |
None. The configuration and the state of server, client and PubSub |
| connection | configure |
config |
| connection | server_start |
password (optional, up to 256 bytes). With a password, the user name token is advertised. |
| connection | server_stop, server_status |
None. server_status returns the session count and the audit record. |
| connection | discover |
url (optional) |
| connection | connect |
url (optional), password (when identity is username) |
| connection | disconnect |
None |
| connection | project_export, project_import |
project_import takes project; server, client and PubSub must all be stopped. |
| address_space | browse |
node (default i=85), direction (forward or inverse) |
| address_space | server_nodes |
None. The current values of the server’s variables |
| read_write_methods | read |
node or nodes (up to 100), attribute (1 to 27, default 13) |
| read_write_methods | write, server_write |
node, type, value |
| read_write_methods | call |
object (default ns=2;s=Simulator), method, arguments (up to 32) |
| subscriptions | subscribe |
node or nodes (up to 100), interval_ms (50 to 10000, default 250) |
| subscriptions | publish |
count (1 to 10, default 1) |
| subscriptions | notifications, notifications_clear, unsubscribe |
None |
| events | event_subscribe |
notifier (default i=2253), interval_ms |
| events | alarm_set |
active |
| events | condition |
condition (default ns=2;s=Simulator.Alarm), method (acknowledge, confirm, add_comment, enable, disable), event_id (hex), comment (up to 1024 bytes) |
| events | condition_refresh |
None |
| history | history_read |
node, start, end, max, continuation, release |
| pubsub | pubsub_start, pubsub_status, pubsub_received, pubsub_stop |
None |
| pubsub | pubsub_publish |
fields (1 to 64, optional) |
| trace | trace, trace_clear |
None |
| trace | logging_configure |
config |
| trace | logging_status, logging_flush |
None |
Passwords are passed per action and never kept in the configuration, projects, replies or logs.
Project files
Section titled “Project files”project_export returns the current configuration and the values of the server’s writable public variables. The GUI’s Project menu opens and saves the same files.
{"action":"project_export"}{"ok":true,"result":{"config":{"client":{"application_uri":"urn:pdcomm:opcua:client","certificate":"","identity":"anonymous","mode":"None","policy":"None","private_key":"","timeout_ms":5000,"trust":[],"url":"","user_name":"operator"},"limits":{"notification_queue":1000,"trace_entries":1000},"pubsub":{"broker":"127.0.0.1:1883","dataset_writer_id":17,"destination":"","listen":"127.0.0.1:0","publisher_id":77,"role":"both","topic":"pdcomm/opcua/simulator","transport":"udp","writer_group_id":1},"server":{"address":"127.0.0.1:0","advertised_host":"","allow_anonymous":true,"allow_none":true,"application_uri":"urn:pdcomm:opcua:server","certificate":"","max_sessions":8,"path":"pdcomm/simulator","private_key":"","secure":true,"simulation_interval_ms":500,"trust":[],"user_name":"operator"}},"kind":"pdcomm.opcua.project","model":[],"version":1}}Save the result object to a file and load it with {"action":"project_import","project":{...}}. kind must be pdcomm.opcua.project and version 1.
Next steps
Section titled “Next steps”- MCP server: use the same actions from AI tools.
- Tutorials: subscriptions, events, history and PubSub