Verify your sequences against a realistic virtual plant before deploying to physical PLC hardware.
A plant model computes sensor inputs (inputs) from PLC coil outputs (outputs) and elapsed scan time (dt).
Create plant/line.mjs in your project folder:
export const INITIAL_INPUTS = {
part_present: false,
part_is_metal: false,
clamp_is_retracted: true,
clamp_is_extended: false,
pusher_is_retracted: true,
pusher_is_extended: false,
};
let clampPos = 0;
let pusherPos = 0;
export function step(outputs, dt, inputs) {
const speed = dt / 0.25;
clampPos = outputs.clamp_extend ? Math.min(1, clampPos + speed) : Math.max(0, clampPos - speed);
pusherPos = outputs.pusher_extend ? Math.min(1, pusherPos + speed) : Math.max(0, pusherPos - speed);
return {
clamp_is_retracted: clampPos <= 0.05,
clamp_is_extended: clampPos >= 0.95,
pusher_is_retracted: pusherPos <= 0.05,
pusher_is_extended: pusherPos >= 0.95,
};
}
For standard cylinders and conveyors that do not require custom scripting, you can also use a declarative
plant.yamlfile in your project root.
automationview.emulation.plantConfigPath) to plant/line.mjs (or plant.yaml).clamp_extend, the plant model transitions clamp_is_retracted to FALSE and clamp_is_extended to TRUE after 250 ms..yaml Test ScenariosCreate repeatable test files in a tests/ folder to validate normal production cycles, recipe changes, and watchdog alarms.
tests/normal_cycle.yaml)name: "Normal Sorting Cycle"
project: ".."
cycle_ms: 10
plant: "../plant/line.mjs"
max_time: 60s
steps:
- set: { start_button: true, part_present: true, part_is_metal: true }
- wait: 100ms
- set: { start_button: false }
- wait_until: "sorted_metal == 1"
timeout: 15s
- assert: "sorted_total == 1"
message: "Total sorted count must increment after sorting a metal part"
- expect_active: ["Coordination.WaitPart"]
tests/jam_fault.yaml)Use force and release to simulate a jammed cylinder reed switch and verify that the sequence enters its fault step:
name: "Cylinder Jam Watchdog Test"
project: ".."
cycle_ms: 10
plant: "../plant/line.mjs"
max_time: 30s
steps:
- force: { clamp_is_extended: false }
- pulse: { start_button: 100ms }
- set: { part_present: true }
- wait_until: "clamp_error == true"
timeout: 5s
- assert: "fault_active == true"
message: "Machine fault flag must activate when clamp watchdog expires"
- release: [clamp_is_extended]
| Action | Syntax | Purpose |
|---|---|---|
| Set Inputs | set: { var: value } |
Write values to one or more input variables |
| Pulse Input | pulse: { var: 100ms } |
Hold a boolean input TRUE for a duration, then reset to FALSE |
| Force Variable | force: { var: value } |
Override plant and sequence writes with a fixed value |
| Release Force | release: [var] |
Remove forced override from listed variables |
| Fixed Wait | wait: 500ms |
Advance simulation clock by a fixed time |
| Conditional Wait | wait_until: "expr" |
Advance simulation until boolean expression is TRUE (fails if timeout elapses) |
| Assertion | assert: "expr" |
Verify boolean expression immediately; fail test with message if FALSE |
| Active Step Check | expect_active: ["Seq.Step"] |
Verify that specific steps are active at the current scan |