Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

A closed loop with a plant data system

Some measurements never pass through a PLC: offline analyses, lab values, a historian’s computed tags, a batch system’s context. A plant data system with a JSON API holds them and takes set points back. The rest connector closes a loop through it; an opcua-client keeps the PLC in the picture.

flowchart LR
  PDS[plant data system<br/>JSON API] -- "GET values" --> PI
  PI -- "PUT set points, watchdog<br/>gated" --> PDS
  subgraph tw[taktwerk]
    CTL[controller] <--> PI[(image)]
    SUP[supervisor: status bits] --> PI
  end
  PLC[PLC] -- "set point, interlock" --> PI
  PI -- "mirror, heartbeat, status" --> PLC

examples/plant-loop is this loop as a runnable project: the PI of raw-pi drives a pump through a REST API from the level that system measures, with the set point and an interlock from a PLC, which also mirrors the loop and lets an operator tune the gains; a small supervisor model turns the interlock and an implausible level into status bits that close both gates. The tutorial runs it from the workbench to a device, against stand-ins of both systems.

The rest connector

[[connector]]
id = "lab"
kind = "rest"
base_url = "http://10.0.0.20:8080/api/"
auth = { scheme = "bearer", token_env = "LAB_TOKEN" }
period_ms = 500
refresh_s = 10
writes = "dry-run"

[connector.gate]
open_after = 3
conditions = [{ signal = "sup.status", mask = 3 }]

[connector.map]
"lab.level" = { get = "values", pointer = "/level", min = 0, max = 5, max_unchanged_s = 60 }
"ctrl.u[0]" = { put = "setpoints/pump", pointer = "/value", min = 0, max = 1.5, clamp = true }
"taktwerk.heartbeat" = { put = "setpoints/watchdog", pointer = "/value" }
  • Reads: one GET per distinct path every period_ms; each mapping picks its value by JSON pointer. An input is stamped when the answer arrived, so with a max_age_ms it turns stale when the system stops answering, and the engine holds the loop.
  • Writes: only a changed value, or one older than refresh_s, since a plant system may log every write. name[i] sends one element of an output array to its own path.
  • Watchdog: the heartbeat written to the system lets it see the engine alive; it goes out while the gate is closed too.
  • Credentials come from the environment (Auth); on a device they go into the bundle’s env file.

Where the connector stops

The connector maps fixed paths to signals. Ids looked up at run time, paths that change during a run, dependent requests, sessions other than one login, ramps, rate limits and TLS are a companion process of the bundle: it talks to the plant system on one side and to the engine on the other, through the OPC UA server or a rest connector pointed at it (Boundary). A companion is a unit of its own, started and stopped with the engine (Bundle).