Organise and reuse sequences with Python class inheritance.
By default, every sequence is a standalone class that inherits from Sequence. A custom base class lets several sequences share steps, transitions, and safety logic without copy-paste.
Sequence (provided by AutomationView)
└── ProjectBaseSequence (your project-wide base)
├── CylinderA
├── CylinderB
└── ConveyorMain
Use it when several sequences share a skeleton (identical steps, identical safety actions) but differ in I/O names, conditions, or durations.
The fastest way to start is to scaffold a project-wide base class.
Command: Ctrl+Shift+P -> AutomationView: Initialize Standards Library
This creates a standards/base_sequence.py file with a ready-to-customise ProjectBaseSequence:
from automation_machine import Sequence, StepType, ActionQualifier
class ProjectBaseSequence(Sequence):
"""Base class shared by every sequence in the project."""
def setup(self):
self.name = self.__class__.__name__
self._configure_safety()
self._define_steps()
self._define_transitions()
def _configure_safety(self):
"""Common safety actions. Override if needed."""
pass
def _define_steps(self):
"""Define steps. Call super()._define_steps() in subclasses."""
self.s0 = self.add_step(StepType.INITIAL, name="Idle")
def _define_transitions(self):
"""Define transitions. Override in subclasses."""
pass
Edit this class to hold everything common to your project: the initial step, safety actions, naming conventions.
If you need more than one base type (one for cylinders, one for conveyors, etc.), use the dedicated command.
Command: Ctrl+Shift+P -> AutomationView: New Base Class (Python)...
Three flavours are offered:
For standardising a repetitive sequence type. Subclasses override only what changes.
from automation_machine import Sequence, StepType
class BaseCylinder(Sequence):
def setup(self):
self.name = self.__class__.__name__
self._define_steps()
self._define_transitions()
def _define_steps(self):
self.s0 = self.add_step(StepType.INITIAL, name="Idle")
def _define_transitions(self):
"""Override in subclasses."""
pass
For sequences that should be configured by arguments at instantiation.
from automation_machine import Sequence, StepType
class ParametrizedSequence(Sequence):
"""
Args:
zones: Number of active zones.
sensor_prefix: Prefix for sensor variable names.
"""
def __init__(self, *args, zones: int = 1, sensor_prefix: str = "sensor", **kwargs):
self.zones = zones
self.sensor_prefix = sensor_prefix
super().__init__(*args, **kwargs)
def setup(self):
self.name = self.__class__.__name__
self.s0 = self.add_step(StepType.INITIAL, name="Idle")
for i in range(self.zones):
step = self.add_step(name=f"Zone_{i + 1}")
self.add_transition(self.s0, step, f"{self.sensor_prefix}_{i + 1}_active")
For adding cross-cutting behaviour to several sequences without forcing a single hierarchy.
from automation_machine import ActionQualifier
class SafetyMixin:
"""
Usage:
class MySequence(SafetyMixin, Sequence):
...
"""
def add_standard_reset_action(self, step, variable: str) -> None:
"""Attach a reset (R qualifier) action on the given step."""
step.add_action(variable, qualifier=ActionQualifier.R)
Once a base class is available in the project, scaffolding a child sequence is one command away.
Command: Ctrl+Shift+P -> AutomationView: New Sequence from Base Class...
AutomationView lists the base classes detected in the project. Pick one, name the new sequence, and the file is generated with the import and structure already in place:
from base_cylinder import BaseCylinder
class CylinderA(BaseCylinder):
"""CylinderA - inherits from BaseCylinder."""
def _define_transitions(self):
"""Transitions specific to CylinderA."""
self.add_transition(self.s0, self.s1, "front_sensor")
The base classes generated by AutomationView all follow the same recipe: setup() calls several small _define_* and _configure_* methods. Subclasses override only the ones they care about and use super() to keep the inherited behaviour.
def _define_steps(self):
super()._define_steps() # keep the Idle step from the parent
self.s1 = self.add_step(name="Extending")
self.s2 = self.add_step(name="Retracting")
| Hook | Typical content |
|---|---|
_configure_safety |
Emergency-stop reset, watchdog, fail-safes |
_define_steps |
Project-specific step list |
_define_transitions |
Conditions wired to project I/O |
Always store reusable steps on
self(self.s0,self.s1, ...) so subclasses can reference them in their overrides.
When a sequence inherits from a custom base class, the visual editor displays an indicator next to the sequence name:
CylinderA extends BaseCylinder
This indicator is informational only. It disappears if the sequence inherits directly from Sequence with no intermediate class.
Two similar cylinders sharing skeleton and safety logic.
standards/base_sequence.py - project-wide base:
from automation_machine import Sequence, StepType, ActionQualifier
class ProjectBaseSequence(Sequence):
def setup(self):
self.name = self.__class__.__name__
self._configure_safety()
self._define_steps()
self._define_transitions()
def _configure_safety(self):
"""Project safety hook - subclasses can override."""
pass
def _define_steps(self):
self.s0 = self.add_step(StepType.INITIAL, name="Idle")
def _define_transitions(self):
pass
lib/base_cylinder.py - shared cylinder skeleton:
from standards.base_sequence import ProjectBaseSequence
from automation_machine import ActionQualifier
class BaseCylinder(ProjectBaseSequence):
def _configure_safety(self):
# Reset both solenoids on initial scan - applies to every cylinder.
self.s0.add_action("sol_extend", qualifier=ActionQualifier.R)
self.s0.add_action("sol_retract", qualifier=ActionQualifier.R)
def _define_steps(self):
super()._define_steps()
self.s1 = self.add_step(name="Extending")
self.s2 = self.add_step(name="Retracting")
def _define_transitions(self):
"""Override in each cylinder with real I/O."""
pass
sequences/cylinder_a.py - Cylinder A wiring:
from lib.base_cylinder import BaseCylinder
class CylinderA(BaseCylinder):
def _define_transitions(self):
self.add_transition(self.s0, self.s1, "cmd_extend_a")
self.add_transition(self.s1, self.s2, "sensor_end_a")
self.add_transition(self.s2, self.s0, "sensor_retract_a")
sequences/cylinder_b.py - Cylinder B wiring:
from lib.base_cylinder import BaseCylinder
class CylinderB(BaseCylinder):
def _define_transitions(self):
self.add_transition(self.s0, self.s1, "cmd_extend_b")
self.add_transition(self.s1, self.s2, "sensor_end_b")
self.add_transition(self.s2, self.s0, "sensor_retract_b")
Both cylinders share the three steps and the solenoid reset defined in BaseCylinder. Only the I/O conditions differ - and a future change to the safety logic happens once, in the base.
AutomationView gives you three reuse mechanisms. Pick the right one:
| Mechanism | Use when |
|---|---|
| Macro step | A block of steps is invoked many times inside a single sequence as a self-contained sub-program with one entry and one exit. |
| Encapsulation step | A parent step "owns" a child sequence that runs in parallel while the parent is active. Useful for monitoring or supervisor logic. |
| Class inheritance | Several separate sequences share the same skeleton or safety actions. Differences are in I/O, naming, or specific transitions. |
Inheritance is the right tool when the duplication is across files. Macros and encapsulation are for duplication inside one file.
| Command | Use |
|---|---|
AutomationView: Initialize Standards Library |
Creates ProjectBaseSequence - the recommended starting point |
AutomationView: New Base Class (Python)... |
Creates a new base class (simple, parameterised, or mixin) |
AutomationView: New Sequence from Base Class... |
Creates a child sequence from an existing base class |