guides
Intended Documentation
PLC Migration Patterns
Common safety-PLC interlocks (Siemens F-CPU, GuardLogix, TwinCAT Safety, Pilz) translated into Intended policy clauses — boolean, compound, consensus, and continuous-time predicates.
PLC Migration Patterns#
Audience: controls engineers familiar with safety-PLC programming (Beckhoff TwinCAT 3 Safety, Siemens F-CPU / SCL, Rockwell GuardLogix / AOIs, Pilz PNOZmulti) who are layering Intended on top of an existing safety stack.
Intended does not replace your safety PLC. It adds intent-aware authority gating above it. Your e-stop chain, light curtains, and safety-rated I/O stay exactly where they are. What changes is how AI-generated or operator-issued commands get classified, authorized, and audited before they reach motion.
Layering, not replacing#
The safety PLC remains the authority on physical safety hazards. Intended is the authority on whether the command should be issued in the first place — and records every decision.
How a clause is evaluated#
A tenant policy is a list of clauses, evaluated in order against the physicalState snapshot submitted with each issuance. The first clause that both applies and whose predicate matches decides the outcome:
| Clause field | Meaning |
|---|---|
id | Stable clause identifier; echoed into the denial as clauseId. |
effect | deny, escalate, or allow (an explicit allow short-circuits later denies). |
applies_to.oil_categories | Optional — restrict to OI codes or families (OI-1500 matches OI-15xx). |
applies_to.verbs | Optional — restrict to specific structured-goal verbs. |
predicate | The condition tested against the snapshot (types below). |
safe_default | Fallback action carried in the denial. |
safety_citations | OI-29xx citations recorded on the decision. |
Predicate types the evaluator supports: boolean, number, continuous_time, consensus (N-of-M), compound (all / any), attestation, audit_gap, and classifier_confidence.
The default policy
Until a tenant uploads its own policy (POST /v1/physical/policy), the runtime applies a built-in default that denies on three conditions: safety/emergency_stop == true (cites OI-2901/OI-2902), safety/light_curtain_breached == true (cites OI-2902), and safety/human_in_cell == true (cites OI-2902/OI-2904). The patterns below extend that baseline.
Pattern translations#
Pattern 1 — Latched e-stop interlock#
Safety-PLC (Siemens F-SCL):
Intended clause:
What the layer adds: the safety PLC stops the motors; Intended additionally denies issuance of new motion tokens while e-stop is latched, with the attempted structured goal, actor identity, and OI category recorded. That record answers "did the agent keep trying to move during the latched e-stop?" — a question the PLC alone cannot.
Pattern 2 — Light-curtain mute / re-arm#
Deny on the breach predicate, and escalate restart commands after a recent breach:
The second clause forces any "resume" after a recent breach through human approval, independent of the PLC mute state — catching a partially-corrupted agent that issues resume-motion before the cell is visually confirmed clear.
Pattern 3 — Speed/separation monitoring (cobot, ISO/TS 15066)#
This denies a high-speed goal while a human is near, even if the PLC's speed override would clamp the actual TCP speed — giving you an audit record of the agent attempting unsafe-zone motion.
Pattern 4 — Two-hand control / dual confirmation#
N-of-M consensus (ECE-P4). The predicate trips when at least n of the sub-predicates match:
The PLC enforces the millisecond timing of the press; Intended records that two distinct operator inputs were the authorization source, with timestamps.
Pattern 5 — Continuous-time predicates ("if X for ≥250 ms")#
ECE-P2 — true only when the predicate has held the target value continuously for at least min_duration_ms:
Use this when the decision should depend on input stability, not the instantaneous value — protecting against flicker in higher-level fusion (e.g. a vision stack briefly disagreeing with the safety scanner). The evaluator needs the predicate's recent history to compute the run-length; supply it via the snapshot's timestamps.
Pattern 6 — Time-bounded audit gap (AUD-P5)#
A robot that keeps moving when it cannot reach the audit chain has lost its forensic guarantee. An audit_gap clause denies once the last confirmed audit write is older than max_gap_ms. When this clause is the decider, the issuer returns 423 (not 422) — same envelope, different status — so your controller can distinguish "policy says no" from "we've lost the audit trail."
What you should NOT translate to Intended#
- Hard real-time interlocks. Anything needing ms-level guaranteed response stays in the safety PLC. The (roadmap) edge verifier targets ≤50ms; sub-ms response belongs in dedicated silicon.
- Direct I/O control. Intended issues authority; it does not drive outputs. The PLC continues to drive contactors, valves, brakes.
- Safety-rated signals with no bus representation. If you cannot surface it as a
PhysicalStateValueover a named protocol, Intended cannot reason about it.
Lifecycle parity#
Treat the policy file with the same rigor as your PLC program:
| PLC discipline | Intended equivalent |
|---|---|
| Source control on the PLC project | Git on the policy JSON |
| Change-board approval for ladder edits | PR review on policy edits |
| Validation before commissioning | Replay against captured traces in shadow mode |
| Backup / rollback | Git revert + re-upload (POST /v1/physical/policy, versioned) |
| Operator change log | The audit chain records every decision |
Migration sequencing#
Inventory
List every operator command, AI-issued command, and autonomous planner output. Map each to an OI category.
Predicates
List every safety-bus signal used by an interlock. Map each to a PhysicalStateProvider predicate name.
Bridge
Build the predicate provider against your bus and submit it in the snapshot. This is where the integration work lives — typically 1–2 weeks for a single cell.
Policy
Translate your interlock catalog into clauses. Start with effect: deny for the obvious safety patterns; add escalate where human approval is required. Upload with activate: false first.
Shadow
Run with the controller logging denials but still actuating from the PLC. Watch false-deny rates.
Cut over
Activate enforcement (activate: true). The PLC interlocks stay fully active — the Intended layer is defense-in-depth.
See also#
- Authority API reference — the clause shapes and
POST /v1/physical/policy. - Safety-case writing — arguing the layered architecture in formal terms.
- C++ industrial controls — the mediator/bridge pattern in TwinCAT / Studio 5000 / TIA Portal.