OPC UA read task
Read the values of OPC UA nodes at a fixed rate and record them in channels.
An OPC UA read task reads the values of nodes on a server at a fixed rate and records them in channels. For example, it can record the tank pressures and pump speeds of a PLC 50 times a second. The Driver times each read in software.
Before you start, you need a connected OPC UA server.
Node IDs
Each channel of the task reads one node. A node ID names the namespace of the node and its identifier, in one of these forms:
The browser in the Console fills node IDs in the nsu= form. A server can give a
namespace a different index when it restarts, but the URI stays the same. In a URI,
write ; as %3B and % as %25. The Driver ignores the case of the prefix and the
type, so it also accepts NS=2;S=Tank1.Pressure.
Create an OPC UA read task
Console
Python
TypeScript
- In the devices toolbar, right-click the server and select Create read task.
- Set Sample rate and Stream rate.
- In the Browser, expand the folders of the server.
- Drag the variables to read into Channels. You can select more than one.
- Click (Start).
The Console creates a channel for each node, named from the browse name of the node. To
choose a name, edit the name of the channel before you start. The Console also creates
an index channel named <server>_time_for_<task>, unless one of the channels already
has an index or you use server timestamps.
Python does not create channels. Create an index channel and a channel for each node first.
import synnax as sy
from synnax import opcua
server = client.devices.retrieve(name="Line 1 PLC")
time = client.channels.create(
name="plc_time", is_index=True, data_type=sy.DataType.TIMESTAMP
)
pressure = client.channels.create(
name="tank_1_pressure", index=time.key, data_type=sy.DataType.FLOAT64
)
task = opcua.ReadTask(
name="Line 1 PLC read",
device=server.key,
sample_rate=50,
stream_rate=25,
channels=[
opcua.ReadChannel(channel=pressure.key, node_id="NS=2;S=Tank1.Pressure")
],
)
client.tasks.configure(task)
task.start()Always pass sample_rate and stream_rate. In Python they default to 10 Hz and 5 Hz,
not to the Console defaults.
TypeScript does not create channels. Create an index channel and a channel for each node first.
import { opcua } from "@synnaxlabs/client";
const [server] = await client.devices.retrieve({
where: { name: { eq: "Line 1 PLC" } },
});
const time = await client.channels.create({
name: "plc_time",
isIndex: true,
dataType: "timestamp",
});
const pressure = await client.channels.create({
name: "tank_1_pressure",
index: time.key,
dataType: "float64",
});
const task = await client.tasks.create({
name: "Line 1 PLC read",
rack: server.rack,
type: "opc_read",
config: opcua.readConfigZ.parse({
device: server.key,
sampleRate: 50,
streamRate: 25,
channels: [{ channel: pressure.key, nodeId: "NS=2;S=Tank1.Pressure" }],
}),
});
await task.start();Find nodes
The Browser loads the children of a node when you expand it. It stops at 10,000 children, or after 7 seconds on a slow server, and the Console warns you when it stops early. To find a node:
- Type in the filter field. The browser shows only the loaded nodes whose name or node ID holds the text. Expand a folder to load its nodes.
- Type a node ID, such as
ns=2;s=Tank1.Pressure, and press Enter. The browser reads that node, even if it is not loaded.
Select a node to see its node ID, class, data type, shape, access, and minimum sampling interval, with its current value, status, and timestamps. Click (Read again) to read the value again. You can drag a node by its name in the details into Channels. A node that fails to browse shows , with the reason when you hover over it. The rest of the tree stays.
Add a folder
Drag a folder or an object into Channels to add the variables under it, at any
depth. The Console asks you to confirm the count before it adds them. It names each
channel from the folder and the browse path below it, such as Line1_Tank1_Pressure.
When the task already has the name, the Console adds a number, such as
Line1_Tank1_Pressure_2. The Driver looks under objects, but not under variables. It
stops after 1,000 variables, 10,000 objects, or 20 seconds, and the Console tells you
when it stopped early.
Import and export channels
Click (Export channels to CSV) in Channels to save the channels
as <task name>_channels.csv. Click (Import channels from CSV) to
add the channels of a CSV file. The Console adds only the nodes that the task does not
have. If one row is not valid, it adds nothing and names the row.
The file starts with a header row. Only node_id is required. Commas or tabs can
separate the cells, and the Console ignores other columns.
Read arrays
Some servers buffer fast samples in an array node. To read the arrays:
- Set Sampling mode to Array.
- Set Array size to the number of values in each array.
The Driver then reads each node Sample rate / Array size times a second, and spreads
the timestamps of the values evenly over the read period. Sample rate must be equal
to or more than Array size. Each array must have exactly Array size values, or
the Driver drops the frame and shows a warning. Stream rate does not apply in array
mode.
Read structure fields
Some nodes hold a structure, such as a motor with a speed, a state, and a position. A channel can read one field of the structure:
- In the Browser, expand the structure node. The browser shows the fields of its type, and the fields of each nested structure.
- Drag the fields into Channels.
The Console names each channel <node>.<field>, such as Motor.Speed. The channels of
one node share one read, so their values come from the same moment. The task reads the
definition of the type from the server each time it connects. If the server does not
give the definition, the task shows “Received bad values” for the fields of that type.
In Python and TypeScript, set field to the path of the field from the node, with .
between names, such as Position.Y. Put & before a . or a & that is part of a
name: a&.b is the field a.b.
Array mode cannot read structure fields. A write task cannot write to a field, so the Console does not add fields to a write task.
Use server timestamps
By default, the Driver gives the values of each read the time halfway through the read request. To use the time from the server instead:
- Add a node with a DateTime value, such as the source time of the PLC, to the task.
- Turn on Use as index for that channel.
The Driver then uses the value of that node as the timestamp of every channel in the task. Only one channel can be the index.
Bad values
The server sends a status with each value. The task records a value with a Good or an Uncertain status. For an Uncertain value, it also shows the warning “Received uncertain values” with the name of the channel.
The task does not record a Bad value. A node that does not exist and a value that the
Driver cannot convert are Bad values too. The task shows the warning “Received bad
values” with the name of the channel and the status, such as BadSensorFailure, and
keeps running. When the value becomes good again, the task records it.
By default, a Bad value skips the sample for every channel on the same index. Turn on
NaN fill to write NaN for a float channel instead, so that the other channels keep
their samples. A line plot shows NaN as a gap. A Bad value of a channel that is not a
float still skips its index.
Outages
A read task keeps running when the server or the network goes down. It shows “Failed to read data” and tries again at most 5 seconds apart. A task can also start while the server is off. Before each new connection, the task gets the connection settings of the server again, so a corrected endpoint or password takes effect without a restart.
Task options
TypeScript uses the same fields in camel case, such as sampleRate. See
timing. When a task has more nodes than the server
accepts in one request, the Driver splits each read into smaller requests.
Channel reference
The table shows the Console label of each setting, with its Python field under it.
Data types
The Console creates each channel with the data type of its node:
A channel can have a different data type than its node. The Driver converts each value to the data type of the channel, and a value that does not fit is a bad value.
Troubleshooting
- Cause
The node ID is not in a form that the Driver accepts.
- Fix
Write the node ID in one of the forms in node IDs.
- Cause
The server sent a Bad status for a node, the server has no namespace with the URI of the node, or the Driver could not convert its value. The description names each channel and the reason. For example,
BadNodeIdUnknownmeans that the server has no node with that ID. See bad values.- Fix
For
BadNodeIdUnknownor a missing namespace, drag the node from the Browser again. For a conversion error, check the data type of the node.
- Cause
The server is not sure that a value is correct, such as a last usable value of a sensor. The task records the value.
- Fix
Check the device that the server reads the node from.
- Cause
The task lost the connection to the server. It tries again until the server answers. See outages.
- Fix
Check the server connection.
- Cause
An array node has a different number of values than Array size.
- Fix
Set Array size to the length of the array.
- Cause
Another task already reads the node into that channel.
- Fix
Move the channels to one task.
- Cause
The Driver could not browse the server, or the folder that you dragged into Channels. A node that fails while the rest of the tree loads shows instead.
- Fix
Check the server connection.
- Cause
The Driver could not read the details of the node. For
BadNodeIdUnknown, the server has no node with that ID.- Fix
Check the node ID that you entered, or select the node in the Browser again.
For other problems, see Troubleshoot hardware.