opcua-cli · v4.3.x
Docs · Recipes

Inventory with dump and grep

Extract a tag list from a server: dump:nodeset to XML, then grep / xmlstarlet / Python to slice it. Useful for vendor onboarding and address-space audits.

For one-off audits — "what does this server actually expose?" — dump:nodeset + standard text tools beat custom code every time. The XML is grep-able, xmlstarlet-friendly, and parseable by any language.

The minimum

bash bash — dump and inspect
opcua-cli dump:nodeset opc.tcp://plc.local:4840 \
    --output=PLC.NodeSet2.xml \
    --namespace=2

# How many nodes?
grep -c '<UAVariable\|<UAObject\|<UAMethod' PLC.NodeSet2.xml

# How many Variable nodes?
grep -c '<UAVariable' PLC.NodeSet2.xml

Five-second answers to "how big is this server" without running multiple browses or building a tree in code.

Extracting a tag list

Variables are the things that hold values. Extract their names and NodeIds:

bash bash — variable inventory with xmlstarlet
xmlstarlet sel -N u="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd" \
    -t -m '//u:UAVariable' \
    -v '@NodeId' -o $'\t' -v '@BrowseName' -n \
    PLC.NodeSet2.xml
text output
ns=2;i=1001    PLC/Speed
ns=2;i=1002    PLC/Mode
ns=2;i=1003    PLC/Health
ns=2;i=2001    Pump/FlowRate
ns=2;i=2002    Pump/Pressure
...

Pipe to sort, wc, column to your taste. The output is tab-separated, suitable for spreadsheet import.

Filtering by DataType

A common audit question: "which tags hold Double values?" Variable nodes have a DataType attribute referencing a DataType NodeId. For Double, that's i=11:

bash bash — filter by DataType
xmlstarlet sel -N u="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd" \
    -t -m '//u:UAVariable[@DataType="i=11"]' \
    -v '@NodeId' -o $'\t' -v '@BrowseName' -n \
    PLC.NodeSet2.xml

The catalogue of well-known DataTypes (in namespace 0) is in the OPC UA spec — i=1 Boolean, i=6 Int32, i=11 Double, i=12 String, … See opcua-client — BuiltinType.

Counting per namespace

bash bash — per-namespace counts
xmlstarlet sel -N u="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd" \
    -t -m '//u:UAVariable' -v '@NodeId' -n \
    PLC.NodeSet2.xml \
  | sed -E 's/^(ns=[0-9]+).*/\1/; t; s/^/ns=0/' \
  | sort | uniq -c
text output
  150 ns=0
 2854 ns=2
   42 ns=3

Tells you which namespace dominates. If ns=2 has 2 854 tags and you only care about a subset, narrow your next dump.

Building a "tag explorer" CSV

For non-technical operators:

bash bash — CSV export
xmlstarlet sel -N u="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd" \
    -t -m '//u:UAVariable' \
    -v '@NodeId' -o ',' \
    -v '@BrowseName' -o ',' \
    -v '@DataType' -o ',' \
    -v 'u:DisplayName' -n \
    PLC.NodeSet2.xml \
    > tags.csv

# Add a header
sed -i '1i NodeId,BrowseName,DataType,DisplayName' tags.csv

Import to Excel / Google Sheets, give the operator a searchable inventory of the address space.

Comparing two snapshots — what changed?

Periodic dumps catch firmware-update surprises:

bash bash — diff snapshots
# Yesterday's snapshot
opcua-cli dump:nodeset opc.tcp://plc.local:4840 \
    --output=PLC-$(date -u +%Y%m%d).NodeSet2.xml --namespace=2

# Today's
opcua-cli dump:nodeset opc.tcp://plc.local:4840 \
    --output=PLC-$(date -u +%Y%m%d).NodeSet2.xml --namespace=2

# Diff the BrowseName lists (NodeIds change less often)
diff <(xmlstarlet sel -t -m '//*[local-name()="UAVariable"]' -v '@BrowseName' -n PLC-20260514.NodeSet2.xml | sort -u) \
     <(xmlstarlet sel -t -m '//*[local-name()="UAVariable"]' -v '@BrowseName' -n PLC-20260515.NodeSet2.xml | sort -u)

New tags, removed tags, renamed tags — visible in the diff. Bake into CI for a "schema drift detector".

Finding methods

Methods are nodes whose class is UAMethod. To list them:

bash bash — method inventory
xmlstarlet sel -N u="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd" \
    -t -m '//u:UAMethod' \
    -v '@NodeId' -o $'\t' -v '@BrowseName' -n \
    PLC.NodeSet2.xml

The CLI itself does not currently expose a call subcommand — to invoke a method against the live server, drop down to opcua-client directly ($client->callMethod(...)). The inventory step above is still useful as a discovery aid: you get the catalogue of method NodeIds and their browse names, then script the actual invocation in PHP.

Extracting enum definitions

Enums are nodes whose class is UADataType with a definition containing enumeration members:

bash bash — enum extraction
xmlstarlet sel -N u="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd" \
    -t -m '//u:UADataType[u:Definition/@Name]' \
    -v '@NodeId' -o $'\t' -v '@BrowseName' -n \
    PLC.NodeSet2.xml

For each enum, fetch the cases:

bash bash — enum cases
xmlstarlet sel -N u="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd" \
    -t -m "//u:UADataType[@NodeId='ns=2;i=3001']/u:Definition/u:Field" \
    -v '@Name' -o '=' -v '@Value' -n \
    PLC.NodeSet2.xml

For automatically-generated typed enums, the better path is generate:nodeset — but xmlstarlet is faster when you need a list once.

When to switch to code

The CLI + xmlstarlet pattern is great for one-off audits. When you need:

  • Repeatable, scheduled audits — script it as a CI job that fails on schema drift.
  • Cross-server comparisons — load multiple dumps, compare programmatically. Easier in Python or PHP than in shell.
  • Filtering by complex criteria — anything involving multiple attributes or reference following exhausts shell quickly.

For all of those, generate typed PHP via opcua-cli generate:nodeset and use the constants in a real application. The shell pattern is for the "first look" phase.

Performance

A 2 000-node namespace dumps in ~30 seconds (network-bound by batched reads). A 50 000-node namespace can take minutes. Cache the dump file — it doesn't change between firmware updates.

xmlstarlet is comfortable with files up to ~100 MB. For bigger dumps, switch to a streaming parser (Python's xml.etree's iterparse, or PHP's XMLReader).