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

Hardware in the loop

A PLC program, or any controller with an OPC UA server, is tested against a simulated plant before it meets the real one: taktwerk runs the plant model in real time and exchanges the signals the program would get from its I/O.

flowchart LR
  subgraph tw[taktwerk, real time]
    PM[plant model] <--> PI[(image)]
  end
  PI -- "sensor values<br/>written" --> PLC[PLC program under test]
  PLC -- "actuator values<br/>read" --> PI
  PI -- "recorder @io" --> R[records]
[engine]
tick_ms = 50.0
input_max_age_ms = 500.0

[engine.realtime]
policy = "fifo"
priority = 70
lock_memory = true

[models.plant]
kind = "fmi"
path = "packages/plant.fmu"

[[instance]]
id = "plant"
model = "plant"
inputs = { valve = "plc.valve", pump = "plc.pump" }

[[connector]]
id = "plc"
kind = "opcua-client"
endpoint = "opc.tcp://10.0.0.10:4840"
writes = "on"
sync_period_ms = 50

[connector.map]
"plc.valve" = "ns=4;s=Line.valve_cmd"           # read: what the program commands
"plc.pump" = "ns=4;s=Line.pump_cmd"
"plant.level" = "ns=4;s=Line.level_sim"          # written: what its sensor would read
"plant.temp" = "ns=4;s=Line.temp_sim"
"taktwerk.heartbeat" = "ns=4;s=Line.sim_heartbeat"

[[recorder]]
id = "hil"
signals = ["@io"]
  • Timing: the plant steps on absolute deadlines; [engine.realtime] with a dedicated CPU keeps the jitter low (Real time). taktwerk.overruns counts missed deadlines, the run summary prints the longest tick. An opcua-client reads every sync_period_ms and writes once per published tick.
  • Lost connection: the actuator inputs age; with input_max_age_ms the engine skips ticks and holds the plant until reads resume, instead of simulating on stale commands.
  • Test cases: a scenario file runs in real time too: set, add and ramp drive the plant model’s tunables and external inputs (a disturbance, a load change), timed to the run. Faults change what models read, not what a connector writes, so a sensor fault the PLC should see is a tunable or input of the plant model.
  • Evidence: @io records every input as the PLC wrote it and every output each tick, with the scenario in run.json. taktwerk plc-mirror generates a PLC program and the matching map when the program should see more of the engine’s signals.
  • Commissioning the test bench: check --live verifies every mapped node before the first tick (Commissioning).