MotionPlatformStateMachine API Reference¶
The MotionPlatformStateMachine provides validated movement control for the Jubilee powder dispensing system. It ensures all operations are safe by tracking system state and enforcing constraints.
Overview¶
The state machine:
- Tracks current position, tool, and payload state
- Validates all requested movements
- Enforces safety constraints
- Provides detailed error messages when operations are invalid
Advanced Usage
Most users should interact with JubileeManager instead of using the state machine directly. Only use the state machine when you need operations not provided by JubileeManager.
Class Reference¶
MotionPlatformStateMachine
¶
Bases: StateMachine
Finite state machine responsible for validating and sequencing platform moves.
The machine relies on python-statemachine to model the control flow. It maintains awareness of both high-level state (idle, moving, tool engaged) and the current logical position descriptor.
Attributes¶
complete_motion_with_tool
class-attribute
instance-attribute
¶
complete_motion_with_tool = to(tool_engaged)
_executor
instance-attribute
¶
Functions¶
from_config_file
classmethod
¶
initialize_deck
¶
Initialize the deck with weight wells in each slot.
| PARAMETER | DESCRIPTION |
|---|---|
deck_name
|
Name of the deck configuration
TYPE:
|
config_path
|
Path to the deck configuration files
TYPE:
|
initialize_dispensers
¶
Initialize piston dispensers.
| PARAMETER | DESCRIPTION |
|---|---|
num_piston_dispensers
|
Number of piston dispensers
TYPE:
|
num_pistons_per_dispenser
|
Number of pistons in each dispenser
TYPE:
|
get_mold_from_deck
¶
Get a mold object from the deck by mold slot ID.
| PARAMETER | DESCRIPTION |
|---|---|
well_id
|
Mold slot identifier (numerical string "0" through "17")
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Optional[object]
|
Mold object if found, None otherwise |
validated_pick_mold
¶
Validate and execute picking up a mold from a mold slot.
| PARAMETER | DESCRIPTION |
|---|---|
well_id
|
Mold slot identifier (numerical string "0" through "17")
TYPE:
|
manipulator_config
|
Configuration dict for the manipulator
TYPE:
|
validated_place_mold
¶
Validate and execute placing a mold in a mold slot.
| PARAMETER | DESCRIPTION |
|---|---|
well_id
|
Well identifier (numerical string "0" through "17")
TYPE:
|
manipulator_config
|
Configuration dict for the manipulator
TYPE:
|
validated_place_mold_on_scale
¶
Validate and execute placing mold on scale.
validated_pick_mold_from_scale
¶
Validate and execute picking mold from scale.
validated_place_top_piston
¶
Validate and execute placing top piston.
validated_tamp
¶
Validate and execute tamping action.
Tamping compresses powder in a mold held by the manipulator to reduce volume. This is typically done at the scale_ready position before inserting the top piston.
Parameter bounds are loaded from system_config.json and can be customized.
| PARAMETER | DESCRIPTION |
|---|---|
manipulator_config
|
Configuration dict for the manipulator
TYPE:
|
tamp_depth
|
Target depth for tamping movement in mm (default 40.0)
TYPE:
|
tamp_speed
|
Speed for tamping movement in mm/min (default 2000)
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
MoveValidationResult
|
MoveValidationResult with outcome |
_validate_and_execute
¶
_validate_and_execute(target_position_id=None, action_id=None, additional_requirements=None, execution_func=None, **execution_kwargs)
Generic validation and execution for movements and tool actions.
This method performs comprehensive validation for either: - Position movements (when target_position_id is provided) - Tool actions (when action_id is provided)
Validation steps for MOVEMENTS: 1. Checks state machine is not already moving 2. Validates position transition is allowed (current → target) 3. Validates machine is actually at expected current position 4. Validates z-height policy for target position 5. Validates all requirements for target position 6. If valid, executes the provided function and transitions position
Validation steps for ACTIONS: 1. Checks state machine is not already moving 2. Validates action exists in registry 3. Validates tool engagement state (if required/blocked) 4. Validates required tool ID matches 5. Validates position scope (action allowed at current position) 6. Validates action requirements and excludes 7. If valid, executes the provided function (no position change)
| PARAMETER | DESCRIPTION |
|---|---|
target_position_id
|
The target position identifier (for movements)
TYPE:
|
action_id
|
The action identifier (for tool actions)
TYPE:
|
additional_requirements
|
Extra requirements beyond position/action requirements
TYPE:
|
execution_func
|
Function to execute if validation passes
DEFAULT:
|
**execution_kwargs
|
Arguments to pass to execution function
DEFAULT:
|
| RETURNS | DESCRIPTION |
|---|---|
MoveValidationResult
|
MoveValidationResult with validation outcome |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If both or neither target_position_id and action_id are provided |
_validate_and_execute_move
¶
_validate_and_execute_move(target_position_id, additional_requirements=None, execution_func=None, **execution_kwargs)
Internal method to validate and execute position movements.
See _validate_and_execute() for full documentation.
_validate_and_execute_action
¶
_validate_and_execute_action(action_id, additional_requirements=None, execution_func=None, **execution_kwargs)
Internal method to validate and execute tool actions.
All actions must be defined in the registry (config JSON).
See _validate_and_execute_move() for full documentation.
validated_move_to_mold_slot
¶
Validate and execute movement to a specific mold slot.
| PARAMETER | DESCRIPTION |
|---|---|
well_id
|
Mold slot identifier (numerical string "0" through "17")
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
MoveValidationResult
|
MoveValidationResult with outcome |
validated_move_to_scale
¶
Validate and execute movement to the scale.
| RETURNS | DESCRIPTION |
|---|---|
MoveValidationResult
|
MoveValidationResult with outcome |
validated_move_to_dispenser
¶
Validate and execute movement to a dispenser ready position.
| PARAMETER | DESCRIPTION |
|---|---|
piston_dispenser
|
PistonDispenser object with ready_pos attribute
|
| RETURNS | DESCRIPTION |
|---|---|
MoveValidationResult
|
MoveValidationResult with outcome |
validated_fill_powder
¶
Validate and execute filling mold with powder.
| PARAMETER | DESCRIPTION |
|---|---|
target_weight
|
Target weight to fill
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
MoveValidationResult
|
MoveValidationResult with outcome |
validated_home_tamper
¶
Validate and execute tamper homing (uses home_manipulator action).
Can be performed while holding a mold without a top piston. The homing process uses the mold itself as a reference: - Start position: v=2 (tamper inserted into mold) - End position: v=-7 (tamper touching bottom of mold)
This establishes accurate positioning by using the mold bottom as a reference point.
| PARAMETER | DESCRIPTION |
|---|---|
tamper_axis
|
Axis letter for tamper (default 'V')
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
MoveValidationResult
|
MoveValidationResult with outcome |
validated_home_all
¶
Validate and execute homing for all axes (X, Y, Z, U).
This action can be conducted from any position, but requires: - No tool picked up (active_tool_id should not be "manipulator") - No mold (payload_state should be "empty")
Returns machine to global_ready position after homing.
| RETURNS | DESCRIPTION |
|---|---|
MoveValidationResult
|
MoveValidationResult with outcome |
validated_home_manipulator
¶
Validate and execute homing for the manipulator axis (V).
Requires no mold picked up (payload_state should be "empty").
| PARAMETER | DESCRIPTION |
|---|---|
manipulator_axis
|
Axis letter for manipulator (default 'V')
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
MoveValidationResult
|
MoveValidationResult with outcome |
validated_home_trickler
¶
Validate and execute homing for the trickler axis (W).
Can be homed at any time with no requirements.
| PARAMETER | DESCRIPTION |
|---|---|
trickler_axis
|
Axis letter for trickler (default 'W')
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
MoveValidationResult
|
MoveValidationResult with outcome |
validated_pickup_tool
¶
Validate and execute picking up a tool.
Valid only from global_ready position. Requires no tool already picked up and mold_transfer_safe z_height. Returns to global_ready position. Only manipulator tool is currently supported.
Note: The machine's pickup_tool() method is decorated with @requires_safe_z, which automatically raises the bed height to deck.safe_z + 20 if it is not already at that height.
| PARAMETER | DESCRIPTION |
|---|---|
tool
|
The Tool object to pick up (must be manipulator)
|
| RETURNS | DESCRIPTION |
|---|---|
MoveValidationResult
|
MoveValidationResult with outcome |
validated_park_tool
¶
Validate and execute parking the current tool.
Valid from global_ready position. Requires manipulator tool to be active. Returns to global_ready position.
Note: The machine's park_tool() method is decorated with @requires_safe_z, which automatically raises the bed height to deck.safe_z + 20 if it is not already at that height.
| RETURNS | DESCRIPTION |
|---|---|
MoveValidationResult
|
MoveValidationResult with outcome |
validated_retrieve_piston
¶
Validate and execute retrieving a piston from a dispenser. This action retrieves the piston, partially inserts it, and then returns to ready position.
Requires: - Manipulator tool to be active - Mold without top piston (payload_state: mold_without_top_piston) - Must start from the corresponding dispenser_ready position for that dispenser
| PARAMETER | DESCRIPTION |
|---|---|
piston_dispenser
|
The PistonDispenser object to retrieve from
|
manipulator_config
|
Configuration dict for the manipulator
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
MoveValidationResult
|
MoveValidationResult with outcome |
request_move
¶
Evaluate and, if permissible, initiate a move or action.
If the request references an action, the FSM validates the action without changing state. Otherwise, the FSM transitions into the moving state and records the pending move for completion tracking.
validate_move
¶
Core validation hook for requested moves.
This implementation verifies
- The target position exists in the registry.
- The transition between current and target positions is permitted.
- Z-height and contextual requirements are satisfied.
- Engaged tools remain constrained to their ready points.
complete_move
¶
Finalize a move previously initiated via request_move.
| PARAMETER | DESCRIPTION |
|---|---|
tool_still_engaged
|
Indicates whether the tool engagement status should keep the FSM in the tool_engaged state after the move.
TYPE:
|
request_tool_engagement
¶
Attempt to transition from idle to tool engaged state at the current position.
request_tool_disengagement
¶
Attempt to disengage the tool and return to idle.
update_tool_engagement
¶
Update the engagement flag for a specific tool.
update_context
¶
Convenience helper to mutate commonly updated context properties.
validate_machine_state
¶
Validate that the machine's physical coordinates match the FSM's expected position.
This is a safety check to ensure the machine is actually where the FSM thinks it is. Should be called before attempting moves or actions.
| PARAMETER | DESCRIPTION |
|---|---|
machine_x
|
Current X coordinate from machine
TYPE:
|
machine_y
|
Current Y coordinate from machine
TYPE:
|
machine_z
|
Current Z coordinate from machine
TYPE:
|
machine_v
|
Current V (manipulator) coordinate from machine
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
MoveValidationResult
|
MoveValidationResult indicating if machine state matches expected position |
on_enter_moving
¶
Hook invoked when entering the moving state.
Future implementations can trigger hardware-level commands or logging from this hook. The current framework simply asserts that a pending move exists when transitions occur.
_assert_engagement_exit_ready
¶
Ensure the machine satisfies engagement requirements before exiting.
_validate_requirements
¶
Validate context attributes against a requirements mapping.
_validate_excludes
¶
Validate that context attributes do not match excluded values.
_value_matches
staticmethod
¶
Determine whether a context value satisfies an expected requirement.
_format_options
staticmethod
¶
Render a collection of options as a comma-separated string.
State Tracking¶
Current State¶
The state machine maintains:
{
"position": "global_ready", # Current named position
"active_tool_id": 0, # Active tool (or None)
"payload_state": "empty", # What manipulator holds
}
Payload States¶
| State | Description |
|---|---|
empty |
Manipulator holds nothing |
mold |
Manipulator holds a mold without piston |
mold_with_piston |
Manipulator holds a mold containing a piston |
Position Names¶
Named positions are defined in motion_platform_positions.json:
global_ready: Safe position away from all labwarescale_ready: Position to access the scalemold_slot_*: Positions for specific wells (e.g.,mold_ready_0,mold_ready_1)dispenser_*_ready: Positions for piston dispensers
Validation Results¶
All validated methods return a ValidationResult:
@dataclass
class ValidationResult:
valid: bool # Whether operation is allowed
reason: str = "" # Explanation if not valid
Example Usage¶
result = state_machine.validated_move_to_scale()
if result.valid:
print("Movement successful")
else:
print(f"Movement failed: {result.reason}")
Common Operations¶
Homing¶
# Home all axes (X, Y, Z, U)
result = state_machine.validated_home_all()
if not result.valid:
print(f"Homing failed: {result.reason}")
# Home manipulator axis (V)
result = state_machine.validated_home_manipulator(manipulator_axis='V')
if not result.valid:
print(f"Manipulator homing failed: {result.reason}")
Requirements:
- No tool picked up (for validated_home_all)
- Payload must be empty
- Currently at a named position
Tool Management¶
# Pick up a tool
from src.Manipulator import Manipulator
manipulator = Manipulator(
index=0,
name="manipulator",
state_machine=state_machine
)
result = state_machine.validated_pickup_tool(manipulator)
if not result.valid:
print(f"Tool pickup failed: {result.reason}")
# Park the current tool
result = state_machine.validated_park_tool()
if not result.valid:
print(f"Tool parking failed: {result.reason}")
Requirements:
- Must be at global_ready position
- Payload must be empty
- No tool already picked up (for pickup)
- Manipulator tool must be active (for parking)
- Z-height must be at mold_transfer_safe
Position Movements¶
# Move to scale
result = state_machine.validated_move_to_scale()
# Move to mold slot
result = state_machine.validated_move_to_mold_slot(well_id="0")
# Move to dispenser
from src.PistonDispenser import PistonDispenser
dispenser = PistonDispenser(index=0, state_machine=state_machine)
result = state_machine.validated_move_to_dispenser(
piston_dispenser=dispenser
)
Requirements vary by destination: - Correct tool must be active - Payload state must be allowed at destination - Valid transition must exist from current position
Specialized Operations¶
# Fill powder to target weight
result = state_machine.validated_fill_powder(target_weight=50.0)
# Retrieve piston from dispenser
result = state_machine.validated_retrieve_piston(
piston_dispenser=dispenser,
manipulator_config=manipulator._get_config_dict()
)
Creating from Configuration¶
From File¶
from pathlib import Path
from science_jubilee.Machine import Machine
from src.Scale import Scale
from src.MotionPlatformStateMachine import MotionPlatformStateMachine
from jubilee_api_config.constants import FeedRate
# Connect to hardware
machine = Machine(address="192.168.1.100")
machine.connect()
scale = Scale(port="/dev/ttyUSB0")
scale.connect()
# Create state machine from config
config_path = Path("jubilee_api_config/motion_platform_positions.json")
state_machine = MotionPlatformStateMachine.from_config_file(
config_file=config_path,
machine=machine,
scale=scale,
feedrate=FeedRate.MEDIUM
)
# Initialize components
state_machine.initialize_deck()
state_machine.initialize_dispensers(
num_piston_dispensers=2,
num_pistons_per_dispenser=10
)
State Updates¶
Manual State Updates¶
In some cases, you may need to manually update the state:
# Update multiple state fields
state_machine.update_context(
active_tool_id=0,
payload_state="mold"
)
# Update position
state_machine.context.position = "scale_ready"
Manual Updates
Manual state updates bypass validation. Only use when you're certain the physical state matches what you're setting.
Validation Logic¶
Position Validation¶
The state machine validates movements based on:
- Current position: Must be at a valid starting position
- Target position: Must be defined in configuration
- Transition exists: Direct path must be allowed
- Tool requirement: Correct tool must be active
- Payload constraint: Payload must be allowed at target
Example Validation Failure¶
# Trying to move to scale with wrong payload
state_machine.context.payload_state = "invalid_state"
result = state_machine.validated_move_to_scale()
# Result:
# valid = False
# reason = "Payload state 'invalid_state' not allowed at position 'scale_ready'"
Configuration Structure¶
The state machine reads from motion_platform_positions.json:
{
"positions": {
"position_name": {
"coordinates": {"x": 100, "y": 100, "z": 50, "safe_z": 150},
"description": "Position description",
"requires_tool": "manipulator",
"allowed_payloads": ["empty", "mold"]
}
},
"transitions": {
"from_position": {
"to": ["target_position1", "target_position2"]
}
}
}
Advanced Usage¶
Custom Movement Sequences¶
For complex operations, chain validated movements:
def custom_operation(state_machine, well_id, target_weight):
"""Example custom operation using state machine."""
# Move to mold slot
result = state_machine.validated_move_to_mold_slot(well_id)
if not result.valid:
return False, f"Move to slot failed: {result.reason}"
# Update payload (after picking mold)
state_machine.update_context(payload_state="mold")
# Move to scale
result = state_machine.validated_move_to_scale()
if not result.valid:
return False, f"Move to scale failed: {result.reason}"
# Fill powder
result = state_machine.validated_fill_powder(target_weight)
if not result.valid:
return False, f"Fill failed: {result.reason}"
return True, "Success"
Accessing Internal State¶
# Get current state
position = state_machine.context.position
tool = state_machine.context.active_tool_id
payload = state_machine.context.payload_state
print(f"State: pos={position}, tool={tool}, payload={payload}")
# Access components
machine = state_machine.machine
scale = state_machine.context.scale
deck = state_machine.context.deck
dispensers = state_machine.context.piston_dispensers
Error Handling¶
Common Validation Errors¶
| Error Reason | Cause | Solution |
|---|---|---|
| "Position not found" | Target position not in config | Add position to configuration |
| "No transition defined" | Movement not allowed | Add transition or use intermediate position |
| "Wrong tool active" | Tool requirement not met | Pick up required tool first |
| "Payload not allowed" | Payload state incompatible | Change payload or destination |
| "Tool already picked up" | Tool state conflict | Park current tool first |
Debugging Validation Failures¶
result = state_machine.validated_move_to_scale()
if not result.valid:
print(f"Validation failed: {result.reason}")
print(f"Current state:")
print(f" Position: {state_machine.context.position}")
print(f" Tool: {state_machine.context.active_tool_id}")
print(f" Payload: {state_machine.context.payload_state}")
Thread Safety¶
Not Thread-Safe
The state machine is not thread-safe. Do not call methods from multiple threads simultaneously. Use appropriate locking if multi-threaded access is required.
See Also¶
- JubileeManager - High-level interface (recommended)
- MovementExecutor - Low-level movement execution
- Architecture Guide - System design overview
- Configuration Guide - Setting up positions and transitions