automation_machineThe programmatic surface used to define AutomationView sequences in Python.
from automation_machine import (
Machine,
Sequence,
SequenceType,
Step,
StepType,
Transition,
Action,
ActionQualifier,
ActionInstruction,
ActionOperand,
Variable,
)
| Symbol | Kind | Purpose |
|---|---|---|
Machine |
class | Singleton registry for instances and hardware |
Sequence |
class | Base class for every SFC sequence |
SequenceType |
enum | NORMAL, MACRO, ENCLOSING |
Step |
class | A step in a Sequence |
StepType |
enum | INITIAL, NORMAL, MACRO, ENCLOSING |
Transition |
class | A boolean-gated link between steps |
Action |
class | A qualifier+instruction pair attached to a step |
ActionQualifier |
enum | When the action runs (N, S, R, ...) |
ActionInstruction |
enum | What the action does (TON, MOVE, CTU, ...) |
ActionOperand |
class | Operand for multi-input instructions |
Variable |
class | A tag in the project variable table |
MachineThe project-level singleton. AutomationView creates one Machine per project and registers all sequence instances against it.
from automation_machine import Machine
machine = Machine()
machine.name = "PackagingLine"
machine.version = "1.0.0"
machine.description = "Cardboard packaging line"
machine.author = "Engineering Team"
| Field | Type | Notes |
|---|---|---|
name |
str |
Machine name |
version |
str |
Free-form version string |
description |
str |
Short summary |
author |
str |
Owner or team |
clients |
list[str] |
Connected client identifiers |
metadata |
dict[str, Any] |
Arbitrary project metadata |
| Method | Purpose |
|---|---|
setup() |
Lifecycle hook - declare instances and hardware here |
add_instance(class_name, instance_name, **params) |
Instantiate a sequence by class name with parameters |
add_station(label, *, protocol=None) |
Register a hardware station |
Machine.get_instance(name) |
Look up a registered sequence instance by name |
Machine.get_all_instances() |
Return the full dict of registered instances |
Machine.reset() |
Drop all registered instances - used by tests |
class Line(Machine):
def setup(self):
self.name = "Line 1"
self.add_instance("CylinderCycle", "front_cylinder")
self.add_instance("CylinderCycle", "rear_cylinder")
SequenceSubclass and override setup().
Sequence is rarely instantiated by hand - AutomationView creates instances through Machine.add_instance or via the visual editor. The constructor signature is:
Sequence(
name: Optional[str] = None,
sequence_type: SequenceType = SequenceType.NORMAL,
parent_sequence_id: Optional[str] = None,
parent_sequence: Optional["Sequence"] = None,
instance_name: Optional[str] = None,
**kwargs,
)
setup() lifecycle methodOverride setup() to declare your steps, transitions, and actions:
from automation_machine import Sequence, StepType
class Heater(Sequence):
def setup(self):
self.s0 = self.add_step(StepType.INITIAL, name="Off")
self.s1 = self.add_step(name="Heating")
self.add_transition(self.s0, self.s1, "cmd_on")
self.s1.add_action("heater_relay")
| Method | Returns | Purpose |
|---|---|---|
add_step(step_type=NORMAL, name=None, comment=None) |
Step |
Create a step |
add_transition(source, target, condition="True", comment=None) |
Transition |
Wire a transition |
add_macro_step(name, sub_class_name, entry_step=None, exit_step=None) |
Step |
Embed a macro sequence |
add_enclosing_step(name, sub_class_name) |
Step |
Embed an enclosing (parallel) sequence |
begin_action(name, ...) / end_action(name) |
- | Attach a persistent action to every subsequent step until ended |
get_sub_sequence(name) |
Sequence or None |
Look up a child sequence by name |
Sequence.get_instance(name=None) |
Sequence |
Look up a registered instance by name (defaults to class name) |
to_dict() |
dict |
Serialise the sequence |
validate() |
list[str] |
Return validation errors (empty when valid) |
| Field | Type | Notes |
|---|---|---|
id |
str |
Auto-generated unique identifier |
name |
str |
Display name |
instance_name |
str or None |
Set when instantiated through Machine |
sequence_type |
SequenceType |
NORMAL, MACRO, or ENCLOSING |
steps |
list[Step] |
All steps declared in setup() |
transitions |
list[Transition] |
All transitions declared in setup() |
sub_sequences |
list[Sequence] |
Macro and enclosing children |
params |
dict[str, Any] |
Parameters passed via add_instance |
SequenceTypeclass SequenceType(Enum):
NORMAL = "normal" # Standard top-level sequence
MACRO = "macro" # Reusable sub-sequence invoked by macro steps
ENCLOSING = "enclosing" # Parallel child of an encapsulation step
StepA node in the SFC. Returned by Sequence.add_step().
add_action()step.add_action(
name: str,
description: str = "",
qualifier: ActionQualifier = ActionQualifier.N,
instruction: ActionInstruction | str | None = None,
duration: float | None = None,
operands: list[ActionOperand] | None = None,
preset_value: int | None = None,
comment: str | None = None,
parameters: dict[str, Any] | None = None,
) -> Action
s1.add_action("motor_run", description="Conveyor motor")
s2.add_action(
"delay_timer",
instruction=ActionInstruction.TON,
duration=2500,
)
| Field | Type | Notes |
|---|---|---|
step_type |
StepType |
INITIAL, NORMAL, MACRO, ENCLOSING |
name |
str |
Display name |
id |
str |
Auto-generated identifier |
actions |
list[Action] |
Actions attached to this step |
sub_sequence |
Sequence or None |
The child sequence for macro/encapsulation steps |
entry_step_name |
str or None |
Entry into a macro sub-sequence |
exit_step_name |
str or None |
Exit from a macro sub-sequence |
StepTypeclass StepType(Enum):
INITIAL = "initial" # Starting step (exactly one per sequence)
NORMAL = "normal" # Standard step
MACRO = "macro" # Step that invokes a macro sub-sequence
ENCLOSING = "enclosing" # Step that wraps a parallel sub-sequence
s_start = self.add_step(StepType.INITIAL, name="Start")
s_run = self.add_step(name="Run") # NORMAL is default
s_pack = self.add_macro_step(name="Pack", sub_class_name="PackingMacro")
s_super = self.add_enclosing_step(name="Supervise", sub_class_name="SafetyMonitor")
TransitionA directional, boolean-gated link between steps. Returned by Sequence.add_transition().
| Field | Type | Notes |
|---|---|---|
source_steps |
list[Step] |
One step normally, several for a convergence |
target_steps |
list[Step] |
One step normally, several for a divergence |
condition |
str |
Boolean expression evaluated each scan |
id |
str |
Auto-generated identifier |
comment |
str or None |
Optional inline note |
# Single transition
self.add_transition(s0, s1, condition="start_button")
# Convergence: both s_a and s_b must finish before continuing
self.add_transition([s_a, s_b], s_done, condition="TRUE")
# Divergence: from s_choose to s_left or s_right depending on direction flag
self.add_transition(s_choose, s_left, condition="direction = 0")
self.add_transition(s_choose, s_right, condition="direction = 1")
ActionThe unit of behaviour attached to a step. Returned by Step.add_action() - rarely constructed directly.
| Field | Type | Notes |
|---|---|---|
name |
str |
Variable or function block instance |
description |
str |
Human-readable label |
qualifier |
ActionQualifier |
When the action runs |
instruction |
ActionInstruction or str or None |
What the action does |
duration |
float or None |
Milliseconds (for timers and timed qualifiers) |
operands |
list[ActionOperand] |
Inputs for multi-operand instructions |
preset_value |
int or None |
Preset for counter instructions |
comment |
str or None |
Optional inline note |
parameters |
dict[str, Any] or None |
Plugin-contributed extra parameters |
ActionQualifierControls when an action runs while its step is active. Defaults to N.
| Value | Name | Behaviour |
|---|---|---|
N |
Non-stored | Active for the entire duration of the step (default) |
S |
Set | Latched ON when the step activates - stays ON until reset |
R |
Reset | Latched OFF when the step activates - clears a prior S |
P |
Pulse | One scan cycle pulse on step activation |
L |
Time-limited | Active for the duration, then stops (even if step is still active) |
D |
Time-delayed | Activates after the duration if the step is still active |
SD |
Stored and time-delayed | S after the duration |
DS |
Delayed and stored | Delayed S, cleared on deactivation if duration not reached |
SL |
Stored and time-limited | S for the duration, then auto-reset |
Qualifiers
L,D,SD,DS, andSLall require adurationargument (milliseconds).
from automation_machine import ActionQualifier
s1.add_action("alarm_lamp", qualifier=ActionQualifier.S) # latch ON
s2.add_action("alarm_lamp", qualifier=ActionQualifier.R) # latch OFF
s3.add_action("warning_pulse", qualifier=ActionQualifier.P) # one scan
s4.add_action("indicator", qualifier=ActionQualifier.L, duration=3000) # 3 s only
See the qualifiers reference for full semantics and timing diagrams.
ActionInstructionControls what an action does. Without an instruction, an action simply drives its boolean name according to the qualifier.
| Value | Name | Use |
|---|---|---|
MOVE |
Move | Copy a value into the action's variable |
s1.add_action(
"target_speed",
instruction=ActionInstruction.MOVE,
operands=[ActionOperand("1500", is_literal=True)],
)
| Value | Name | Use |
|---|---|---|
SR |
Set-dominant flip-flop | Output stays set when both inputs are TRUE |
RS |
Reset-dominant flip-flop | Output stays reset when both inputs are TRUE |
| Value | Name | Output |
|---|---|---|
R_TRIG |
Rising-edge trigger | One scan pulse on FALSE -> TRUE |
F_TRIG |
Falling-edge trigger | One scan pulse on TRUE -> FALSE |
s1.add_action(
"start_edge",
instruction=ActionInstruction.R_TRIG,
operands=[ActionOperand("start_button")],
)
| Value | Name | Operation |
|---|---|---|
ADD |
Add | result := a + b |
SUB |
Subtract | result := a - b |
MUL |
Multiply | result := a * b |
DIV |
Divide | result := a / b |
s1.add_action(
"total_count",
instruction=ActionInstruction.ADD,
operands=[
ActionOperand("part_count"),
ActionOperand("1", is_literal=True),
],
)
| Value | Name | Boolean result |
|---|---|---|
EQ |
Equal | a = b |
NE |
Not equal | a <> b |
GT |
Greater than | a > b |
GE |
Greater than or equal | a >= b |
LT |
Less than | a < b |
LE |
Less than or equal | a <= b |
s1.add_action(
"above_setpoint",
instruction=ActionInstruction.GT,
operands=[
ActionOperand("temperature"),
ActionOperand("setpoint"),
],
)
All timer durations are in milliseconds. Outputs are exposed as .Q (boolean) and .ET (elapsed time) on the timer's variable.
| Value | Name | Behaviour |
|---|---|---|
TON |
On-delay timer | .Q becomes TRUE after duration of continuous input |
TOF |
Off-delay timer | .Q stays TRUE for duration after input goes FALSE |
TP |
Pulse timer | .Q is TRUE for exactly duration on a rising input edge |
RTO |
Retentive on-delay | Like TON but accumulates across disable/enable cycles |
s1.add_action(
"warmup_timer",
instruction=ActionInstruction.TON,
duration=5000,
)
# Reference the result in a transition:
self.add_transition(s1, s2, condition="warmup_timer.Q")
.CV (current value) and .Q (preset reached) are exposed on the counter's variable.
| Value | Name | Behaviour |
|---|---|---|
CTU |
Count up | Increments .CV on each rising edge of the input |
CTD |
Count down | Decrements .CV from preset on each rising edge |
CTUR |
Count up with reset | CTU with a reset input |
CTDR |
Count down with reset | CTD with a reset input |
s1.add_action(
"cycle_counter",
instruction=ActionInstruction.CTU,
preset_value=100,
operands=[ActionOperand("cycle_trigger")],
)
self.add_transition(s1, s2, condition="cycle_counter.Q")
See the instructions reference for in-depth per-instruction docs including timing diagrams.
ActionOperandA typed input for instructions that need more than the implicit name. Used by ADD, SUB, MUL, DIV, MOVE, comparison instructions, edge triggers, and counters.
from automation_machine import ActionOperand
ActionOperand("temperature") # variable reference
ActionOperand("1500", is_literal=True) # literal value
| Parameter | Type | Notes |
|---|---|---|
value |
str |
Variable name or literal text |
is_literal |
bool |
True for a literal value, False for a variable reference (default False) |
VariableA tag in the project variable table. Variables are usually declared in the visual editor or .machine file, but they can also be constructed programmatically.
| Field | Type | Notes |
|---|---|---|
name |
str |
Variable name (unique within scope) |
data_type |
str |
BOOL, INT, DINT, REAL, STRING, ... |
initial_value |
bool or int or float or str or None |
Power-on default |
current_value |
same as initial_value |
Live value during emulation |
description |
str |
Documentation |
address |
str or None |
Physical I/O address |
| Method | Purpose |
|---|---|
set_value(value) |
Update the current value |
get_value() |
Read the current value |
reset() |
Reset to initial_value |
to_dict() |
Serialise |
from automation_machine import Variable
cycle_count = Variable()
cycle_count.name = "cycle_count"
cycle_count.data_type = "DINT"
cycle_count.initial_value = 0
cycle_count.description = "Completed production cycles"
The data type is inferred automatically when a variable is first used in a step or transition. You only need to construct
Variableinstances directly for advanced scripting scenarios.
The module exposes a few utility helpers:
| Function | Purpose |
|---|---|
is_timed_qualifier(q) |
True if the qualifier requires a duration (L, D, SD, DS, SL) |
is_stored_qualifier(q) |
True if the qualifier latches state (S, SD, DS, SL) |
requires_operands(i) |
True if the instruction requires operands |
instruction_to_str(i) |
Normalise an instruction to its string name |
The frozen sets TIMED_QUALIFIERS, STORED_QUALIFIERS, OPERAND_INSTRUCTIONS, and BOOLEAN_INSTRUCTIONS are also exported for lookup-based code.
A small sequence that exercises most of the API:
from automation_machine import (
Sequence, StepType, ActionQualifier, ActionInstruction, ActionOperand,
)
class FillStation(Sequence):
"""Fill a tank until level setpoint, then drain on demand."""
def setup(self):
s_idle = self.add_step(StepType.INITIAL, name="Idle")
s_fill = self.add_step(name="Filling")
s_full = self.add_step(name="Full")
s_drain = self.add_step(name="Draining")
# Open inlet valve while filling
s_fill.add_action("inlet_valve")
# Compare level to setpoint - drives boolean variable `level_ok`
s_fill.add_action(
"level_ok",
instruction=ActionInstruction.GE,
operands=[ActionOperand("tank_level"), ActionOperand("setpoint")],
)
# Latch a "fault" flag if filling lasts longer than 30 s
s_fill.add_action(
"fill_fault",
qualifier=ActionQualifier.D,
duration=30000,
)
# Idle: clear the fault
s_idle.add_action("fill_fault", qualifier=ActionQualifier.R)
# Drain valve on while draining
s_drain.add_action("drain_valve")
# Wiring
self.add_transition(s_idle, s_fill, "cmd_fill AND NOT fill_fault")
self.add_transition(s_fill, s_full, "level_ok")
self.add_transition(s_full, s_drain, "cmd_drain")
self.add_transition(s_drain, s_idle, "tank_level <= 5")