# An OPC UA Client That Trends, Records and Reports

> Most OPC UA clients show you a value. Here is how browsing, subscriptions, quality codes and source timestamps turn those tags into trends you can record.
>
> OPC UA · September 14, 2026 · by Alex Spataru · https://serial-studio.com/blog/opc-ua-client-that-trends-and-records

There is a gap in the OPC UA tooling most people have on their laptop. On one side there are browsers: point them at a server, expand the tree, watch a number tick over in a table. Excellent for answering "is this tag alive?" On the other side there is SCADA, which trends and records and archives beautifully, and which costs a seat and a project and a commissioning week.

What sits in between is the thing you usually actually want: browse the server, pick thirty tags, watch them plot against each other for an hour, and walk away with a file. Not an installation. An afternoon.

That gap is worth understanding properly, because OPC UA gives a client more to work with than a table of current values, and most of what makes trending trustworthy is in the parts people skip past.

## The address space, briefly

An OPC UA server (IEC 62541) is a tree. Under the **Objects** folder, vendors arrange folders for a plant, a line, a machine, and inside them sit **variable nodes**: the tags.

Every node has a **node id** (`ns=2;s=Plant.Line1.Filler.Level_pct`, or `ns=3;i=1017`), a display name, a data type and an access level. You never deal in registers or byte offsets. You ask for a value by node id and you get back a typed value, a **status code**, and a **source timestamp** from the device's own clock.

That last pair is the part that matters for trending and the part a simple value table throws away.

## Subscribe, do not poll

There are two ways to get values, and they are not equivalent.

**Read** asks for the current value of a list of nodes in one request. It is simple, and it means you are polling: you decide when to ask, and anything that happens between your asks is gone.

**Subscription** is the efficient path. The client creates a subscription with a publishing interval and adds monitored items. The server samples each item and publishes every change it sees. You are no longer guessing at the sample rate; the server does the sampling and tells you what happened.

A good client asks for a subscription first and falls back to reads only when it has to. There are three situations where it has to, and all three happen in the field:

- The server does not support subscriptions at all, or its quota is exhausted. A previous session that was not closed cleanly holds its subscriptions until the server times it out, which is a surprisingly common cause.
- The server refuses individual items. Item caps on an S7-1200, per-user access rules, or a node id that went stale after a PLC download. The right behaviour is to move those specific tags to the read lane and leave the rest subscribed, not to give up on the whole session.
- The subscription goes silent without any error at all. A server reloads its project or drops the subscription while leaving the session up. Nothing fails; data simply stops. A watchdog that notices nothing has arrived for several publishing periods and falls back to polling is the difference between a dashboard that keeps working and one that quietly freezes while looking fine.

If you are evaluating a client, that third case is the one to test. It is also the one nobody demos.

## Quality is not a boolean

Every OPC UA value carries a status code, and collapsing it into "connected" or "not connected" throws away the useful part.

The severity bits split three ways. **Good** is what you expect. **Bad** means the server is telling you its own reading is untrustworthy. **Uncertain** sits between them and is more common than people think: a gateway serving its last usable value, a device still in warm-up, a sensor the station has flagged but not given up on.

The sane handling is to publish Good and Uncertain, and to drop Bad. A tag whose newest value is Bad should keep its last good value on the chart and increment a counter somewhere you can see it. Drawing the failure as a number would make a fault look like data, and on a trend that you are going to hand to someone else, that is the worst possible outcome.

## Timestamps decide whether the recording is worth anything

A value's source timestamp comes from the device. The time your laptop received it does not.

For a live tile on a wall those are interchangeable. For a recording you intend to correlate against a batch record, a fault log or another system, they are not. Serial Studio stamps every frame with the earliest source timestamp it carries, mapped onto a monotonic local clock through an offset sampled when the session opened. A PLC whose clock is not synchronised is therefore followed rather than rejected, because what matters downstream is that readings advance at the device's rate. A stamp more than five seconds off that offset, or missing entirely, falls back to receive time and gets counted as unstamped, so you know it happened.

A frame stamp never goes backwards. A skewed server cannot rewind your chart.

## Browsing without hanging on a big server

A gateway fronting a plant can expose a hundred thousand nodes. A browser that tries to walk the tree eagerly will sit there for a minute and then show you everything at once, which is not help.

The workable pattern is one level per expansion: one Browse plus one batched Read for that level, and nothing below a node is fetched until you expand it. Variables need to expand too, because PLC structs and UDTs publish their members as child Variables, along with their `EngineeringUnits` and `EURange` properties. A struct that looks like a dead end usually is not; expand it and tick the members.

Those two properties are worth ticking for. `EngineeringUnits` gives a dataset its unit and `EURange` gives a widget and a plot their range, so a generated dashboard arrives already scaled instead of auto-ranging around whatever it saw in the first ten seconds.

## Security, and the handshake nobody explains

A server advertises one **endpoint** per security configuration, pairing a policy (`None`, `Basic256Sha256`, and others) with a message security mode (None, Sign, or Sign & Encrypt). Two notes save a lot of time here.

First, `Basic128Rsa15` and `Basic256` are deprecated by the OPC Foundation, which no longer considers SHA-1 and RSA-1.5 safe. Field controllers still ship them, so a client has to offer them, but it should label them and never pick one automatically.

Second, the first secure connection is *supposed* to fail. A secure channel is mutual: you have to trust the server's certificate, and the server has to trust yours. So the sequence is press Connect, get refused, compare the SHA-256 fingerprint against what the server operator told you, accept, and connect again. Then export your own client certificate into wherever that server keeps trusted clients (`pki/trusted/certs` on many of them) so the operator trusts your installation once rather than every launch.

A refusal that says which of four things went wrong saves an afternoon: the certificate is untrusted, expired, not yet valid, or was not issued for the host you dialled. Those have four different fixes and one generic error message hides all of them.

One more practical detail: servers routinely advertise their own hostname (`opc.tcp://PLC-01:4840`), which rarely resolves from an engineering laptop. Dialling the host and port you typed, while keeping the rest of the discovered endpoint description, avoids a failure mode that otherwise looks like a firewall problem and is not.

## From a tag selection to a document

Once the tags are picked, the dashboard should build itself. Browse, tick the folder, generate: one group per folder, LED widgets for booleans, plots for numerics, string tags into the data grid, units and ranges carried across from the server's own properties. Array tags fan out into one channel per element.

From there the recording path is what closes the loop. Anything on the chart can go to CSV, or to [MDF4](https://serial-studio.com/help/mdf4) when the analysis downstream is in CANape, DIAdem or `asammdf` and wants channel metadata rather than a flat table. Recorded sessions can be replayed through the same pipeline, and rendered into a PDF report with per-parameter minimum, maximum, mean and standard deviation plus one chart per dataset.

That last artifact is often the actual deliverable. The trend was how you found the answer; the report is what you send.

## Try it against a server that misbehaves

You do not need a PLC to see whether this fits your work. The `OPC UA PLC Simulator` that ships with Serial Studio runs a Python server modelling a bottling line: a filler, capper, pasteuriser and utilities, with typed tags, a six-element float array, string status tags, engineering units and ranges, and a `FaultySensor` that reports a Bad status for five seconds out of every ten.

```bash
pip install asyncua
python "examples/OPC UA PLC Simulator/opcua_plc_simulator.py"
```

Point a client at `opc.tcp://127.0.0.1:4840`, press Discover, browse, tick the `Plant` folder and generate. Then use the flags, because they reproduce exactly the situations described above: `--no-subscriptions` forces the polling fallback, `--drop-after N` stops publishing without closing the socket so you can watch the watchdog work, `--secure-only` advertises only a secured endpoint so you can walk the certificate dance, and `--user` with `--password` turns on authentication.

The driver's own integration tests run against this simulator, so the client you exercise is the client that will meet your server.

Serial Studio's OPC UA driver is part of the Pro build rather than the GPLv3 one, and the fourteen-day trial unlocks it along with every other Pro feature. There are [simulators for the other industrial protocols too](https://serial-studio.com/simulators), which means the whole evaluation can happen before anyone gives you access to real hardware.
