# 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](https://docs.synnaxlabs.com/reference/hardware/opc-ua/connect-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:

| Form                                       | Example                                  |
| ------------------------------------------ | ---------------------------------------- |
| `nsu=<namespace URI>;<type>=<identifier>`  | `nsu=urn:example:plant;s=Tank1.Pressure` |
| `ns=<namespace index>;<type>=<identifier>` | `ns=2;s=Tank1.Pressure`                  |
| `<type>=<identifier>`                      | `i=2258`, a node in namespace 0          |

| Type | Identifier       | Example                                       |
| ---- | ---------------- | --------------------------------------------- |
| `i`  | A number         | `ns=2;i=1001`                                 |
| `s`  | A string         | `ns=2;s=Tank1.Pressure`                       |
| `g`  | A GUID           | `ns=2;g=09087e75-8e5e-499b-954f-f2a9603db28a` |
| `b`  | Bytes, in base64 | `ns=2;b=M/RbKBsRVkePCePcx24oRA==`             |

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

[Enterprise](https://docs.synnaxlabs.com/reference/get-started/editions "Needs Synnax Enterprise")

**Console**

1. In the devices toolbar, right-click the server and select **Create read task**.
2. Set **Sample rate** and **Stream rate**.
3. In the **Browser**, expand the folders of the server.
4. Drag the variables to read into **Channels**. You can select more than one.
5. 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](https://docs.synnaxlabs.com/reference/hardware/opc-ua/read-task#use-server-timestamps).

[](https://synnax.nyc3.cdn.digitaloceanspaces.com/docs/hardware/opc-ua/read-task/a1ebf8a46370-dark.mp4#t=0.001)

**Python**

Python does not create channels. Create an index channel and a channel for each node first.

```python
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**

TypeScript does not create channels. Create an index channel and a channel for each node first.

```typescript
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.

| Column      | Default   | Notes                                                                                                                       |
| ----------- | --------- | --------------------------------------------------------------------------------------------------------------------------- |
| `name`      | Empty     | The channel name. Empty uses the node name.                                                                                 |
| `node_name` | Empty     | The node name. Empty uses the end of the node ID.                                                                           |
| `node_id`   |           | See [node IDs](https://docs.synnaxlabs.com/reference/hardware/opc-ua/read-task#node-ids).                                   |
| `field`     | Empty     | See [structure fields](https://docs.synnaxlabs.com/reference/hardware/opc-ua/read-task#read-structure-fields).              |
| `data_type` | `float32` | A Synnax [data type](https://docs.synnaxlabs.com/reference/hardware/opc-ua/read-task#data-types).                           |
| `enabled`   | `true`    | `true` or `false`.                                                                                                          |
| `index`     | `false`   | `true` uses the node as the [index](https://docs.synnaxlabs.com/reference/hardware/opc-ua/read-task#use-server-timestamps). |

## Read arrays

Some servers buffer fast samples in an array node. To read the arrays:

1. Set **Sampling mode** to **Array**.
2. 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:

1. In the **Browser**, expand the structure node. The browser shows the fields of its type, and the fields of each nested structure.
2. 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:

1. Add a node with a DateTime value, such as the source time of the PLC, to the task.
2. 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

| Console label | Python field           | Default                 | Notes                                                                                                                                          |
| ------------- | ---------------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| OPC UA server | `device`               |                         | The key of the server.                                                                                                                         |
| Sampling mode | `sampling`             | Scalar                  | `sy.opcua.ArraySampling(size=5)` reads arrays. See [read arrays](https://docs.synnaxlabs.com/reference/hardware/opc-ua/read-task#read-arrays). |
| Sample rate   | `sample_rate`          | 50 Hz (10 Hz in Python) | Reads per second. At most 10,000.                                                                                                              |
| Stream rate   | `stream_rate`          | 25 Hz (5 Hz in Python)  | Frames per second sent to the Core. At most the sample rate. Not in array mode.                                                                |
| Array size    | `sampling.size`        | 1                       | Values in each array node.                                                                                                                     |
| NaN fill      | `nan_fill_enabled`     | Off                     | See [bad values](https://docs.synnaxlabs.com/reference/hardware/opc-ua/read-task#bad-values).                                                  |
| Data saving   | `data_saving_disabled` | On                      | See [data saving](https://docs.synnaxlabs.com/reference/hardware/tasks#data-saving).                                                           |
| Auto start    | `auto_start`           | Off                     | Starts the task each time the Driver starts.                                                                                                   |

TypeScript uses the same fields in camel case, such as `sampleRate`. See [timing](https://docs.synnaxlabs.com/reference/hardware/tasks#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.

| Setting                 | Default   | Notes                                                                                                                                           |
| ----------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| Channel name `name`     | Empty     | The name of the channel that the Console creates.                                                                                               |
| `channel`               |           | The key of the channel that records the values.                                                                                                 |
| Node ID `node_id`       |           | See [node IDs](https://docs.synnaxlabs.com/reference/hardware/opc-ua/read-task#node-ids).                                                       |
| `field`                 | Empty     | The field of a structure node. See [structure fields](https://docs.synnaxlabs.com/reference/hardware/opc-ua/read-task#read-structure-fields).   |
| Node name `node_name`   | Empty     | The browse name, or the folder path. Names the channel when the name is empty.                                                                  |
| `data_type`             | `float32` | The data type of the channel that the Console creates.                                                                                          |
| Use as index `is_index` | Off       | Shows only for a DateTime node. See [server timestamps](https://docs.synnaxlabs.com/reference/hardware/opc-ua/read-task#use-server-timestamps). |
| Enabled `disabled`      | Off       | A disabled channel is not read.                                                                                                                 |

### Data types

The Console creates each channel with the data type of its node:

| OPC UA type | Synnax data type |
| ----------- | ---------------- |
| Boolean     | `boolean`        |
| SByte       | `int8`           |
| Byte        | `uint8`          |
| Int16       | `int16`          |
| UInt16      | `uint16`         |
| Int32       | `int32`          |
| UInt32      | `uint32`         |
| Int64       | `int64`          |
| UInt64      | `uint64`         |
| Float       | `float32`        |
| Double      | `float64`        |
| String      | `string`         |
| DateTime    | `timestamp`      |
| Guid        | `uuid`           |
| ByteString  | `bytes`          |
| Enumeration | `int32`          |

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](https://docs.synnaxlabs.com/reference/hardware/opc-ua/read-task#bad-values).

## Troubleshooting

* [Invalid NodeId format](https://docs.synnaxlabs.com/reference/hardware/opc-ua/read-task#invalid-nodeid-format)

  * 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](https://docs.synnaxlabs.com/reference/hardware/opc-ua/read-task#node-ids).

* [Received bad values](https://docs.synnaxlabs.com/reference/hardware/opc-ua/read-task#received-bad-values)

  * 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, `BadNodeIdUnknown` means that the server has no node with that ID. See [bad values](https://docs.synnaxlabs.com/reference/hardware/opc-ua/read-task#bad-values).

  * Fix

    For `BadNodeIdUnknown` or a missing namespace, drag the node from the **Browser** again. For a conversion error, check the data type of the node.

* [Received uncertain values](https://docs.synnaxlabs.com/reference/hardware/opc-ua/read-task#received-uncertain-values)

  * 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.

* [Failed to read data](https://docs.synnaxlabs.com/reference/hardware/opc-ua/read-task#failed-to-read-data)

  * Cause

    The task lost the connection to the server. It tries again until the server answers. See [outages](https://docs.synnaxlabs.com/reference/hardware/opc-ua/read-task#outages).

  * Fix

    Check the [server connection](https://docs.synnaxlabs.com/reference/hardware/opc-ua/connect-server#troubleshooting).

* [OPC UA array for ... is too small](https://docs.synnaxlabs.com/reference/hardware/opc-ua/read-task#opc-ua-array-for-is-too-small)[... is too large](https://docs.synnaxlabs.com/reference/hardware/opc-ua/read-task#opc-ua-array-for-is-too-small)

  * Cause

    An array node has a different number of values than **Array size**.

  * Fix

    Set **Array size** to the length of the array.

* [Channel ... already exists as ...](https://docs.synnaxlabs.com/reference/hardware/opc-ua/read-task#channel-already-exists-as)

  * Cause

    Another task already reads the node into that channel.

  * Fix

    Move the channels to one task.

* [Failed to browse OPC UA nodes](https://docs.synnaxlabs.com/reference/hardware/opc-ua/read-task#failed-to-browse-opc-ua-nodes)[Failed to find variables](https://docs.synnaxlabs.com/reference/hardware/opc-ua/read-task#failed-to-browse-opc-ua-nodes)

  * 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](https://docs.synnaxlabs.com/reference/hardware/opc-ua/connect-server#troubleshooting).

* [Failed to read node](https://docs.synnaxlabs.com/reference/hardware/opc-ua/read-task#failed-to-read-node)

  * 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](https://docs.synnaxlabs.com/reference/hardware/troubleshooting).
