Skip to content

opcua

tested on simulator

Tier Tier 2
Protocol OPC UA binary over TCP (opc.tcp)
Covers PLCs with an OPC UA server, LADS lab instruments, process equipment and gateways
Hazard default medium
Conformance full
Tested with asyncua's own OPC UA server running a simulated pump skid

Estimated reach. Thousands of controller and instrument models. OPC UA is the standard interface of industrial automation. Most current PLC families include an OPC UA server, and the LADS companion specification brings it to laboratory instruments. The driver reads and changes variables and calls methods, which covers any server that exposes its data that way. The count is a model estimate.

The opcua driver talks to OPC UA servers, the interface of most current PLCs, process equipment and LADS lab instruments. Variables become channels with their engineering units, and methods become actions.

Driver nameopcua
Libraryasyncua 2.1.0 (LGPL-3.0 or later, used unmodified as a library)
Extrauv tool install --compile-bytecode "openhal-hub[opcua] @ git+https://github.com/sohan-bhat/OpenHAL"
Hazard defaultmedium
Conformancefull
Safe statefrom the device file. OPC UA has no general emergency behavior
Self-describeyes, by browsing the Objects folder

What it supports

Every channel and action names its node in a map, with node ids in the OPC UA string form.

MapMeaning
{ node: "ns=2;s=Pump.Speed" }A variable, read, or set when the file's write section names the variable
{ node: "nsu=urn:example:skid;s=Pump.Speed" }The same, with the namespace URI instead of its index
{ node: "ns=2;s=Tank.Level", unit: percent, scale: 100 }unit overrides the node's EngineeringUnits, scale multiplies the OPC UA value
{ object: "ns=2;s=Pump", method: "ns=2;s=Pump.Start", inputs: [rate] }A method call. inputs lists the action's arguments in the method's order

Node ids may be numeric (i=2258), string (s=...), GUID (g=...) or opaque (b=...). nsu= ids are resolved against the server's namespace array on connect, so a file keeps working when the server renumbers its namespaces.

How the protocol is handled

  • connect() checks every node the file names. Variables must exist, variables in the write section must be writable for this user, and methods must exist with as many inputs as the map lists. Anything else stops the connect with a reason.
  • Units come from each variable's EngineeringUnits, by UNECE code (CEL, KPA, RPM, P1 and about 45 others) or its display name. A channel whose unit can't be converted from the node's unit is refused on connect, so a degF node can't be read as degC by mistake.
  • Reads check the status code, so Bad fails the read and Uncertain marks the reading suspect, and a reading outside the channel's range is also suspect.
  • A change sends a Variant of the node's own data type. Integer nodes refuse values that aren't whole or don't fit the type, and nothing is sent.
  • Method arguments go out as the method's input types, and because OPC UA arguments carry no units, the value is sent in the argument's declared unit.

Safe state

OPC UA has no general stop, so the driver has no native safe state. Give the device file a safe_state with the equipment's own stop, such as a Stop method and setpoints of 0. The safe state's steps run in order on every stop.

Discovery

openhal discover calls FindServers on opc.tcp://127.0.0.1:4840, the usual local discovery server, to list the servers registered there, and discovery never reads or changes a variable.

Setup

  1. Install the extra with uv tool install --compile-bytecode "openhal-hub[opcua] @ git+https://github.com/sohan-bhat/OpenHAL".
  2. Find the endpoint URL and the node ids with the PLC's tool or a generic client such as UaExpert.
  3. Start from openhal/drivers/opcua/examples/dosing-pump.hal.yaml, or call describe for a proposal.
  4. Run openhal file validate, then confirm the limits by hand.

Parameters

KeyDefaultMeaning
endpointrequiredopc.tcp://host:port/path
timeout4 sPer request
usernoneUser name for username login
token_envnoneName of the environment variable that holds the password for user
securitynoneasyncua security string, for example Basic256Sha256,SignAndEncrypt,client.der,client.pem
browse_depth3How many object levels describe browses

Example device file

device: Dosing pump skid
id: dosing
connect: { opcua: "opc.tcp://plc.local:4840" }
read:
  tank_level: { unit: percent, map: { node: "nsu=urn:example:skid;s=Tank.Level" } }
write:
  speed: { unit: rpm, min: 0, max: 1200, max_change_per_s: 200, map: { node: "nsu=urn:example:skid;s=Pump.Speed" } }
actions:
  stop: { map: { object: "nsu=urn:example:skid;s=Pump", method: "nsu=urn:example:skid;s=Pump.Stop" } }
safe_state:
  - { action: stop }
  - { speed: 0 }
interlocks:
  - { if: "speed > 0 rpm", require: "tank_level > 5 percent" }
hazard: medium

Known limitations

  • Subscriptions are not used, so each read is one request and the hub's polling sets the rate.
  • Arrays, structures and enumerated data types beyond String and the numeric and Boolean types are not mapped.
  • When the server has no EURange, describe proposes limits of 0 to 0 for numeric changes, so nothing moves until a person sets real limits.
  • asyncua is LGPL-3.0, installed as a separate package and not modified or copied.

Hazard defaults

The default hazard is medium, because PLCs drive pumps, heaters, valves and motors. Raise the file's hazard to high for anything that can injure, and keep the PLC's own interlocks and e-stop circuit in place, because the device file adds limits and does not replace them.

Sources