System Architecture¶
This document explains the architecture of the Jubilee Powder system at a conceptual level.
Overview¶
The Jubilee Powder system uses a layered architecture where each layer provides a different level of abstraction and control.
Architecture Diagram¶
graph TB
subgraph "User Interface Layer"
A[User Scripts]
B[GUI View]
end
subgraph "Presentation Layer"
C[JubileeViewModel]
end
subgraph "Coordination Layer"
D[JubileeManager]
end
subgraph "Validation Layer"
E[MotionPlatformStateMachine]
F[MovementExecutor]
end
subgraph "Component Layer"
G[Manipulator]
H[PistonDispenser]
I[Scale]
end
subgraph "Hardware Layer"
J[Jubilee Machine]
K[Scale Hardware]
L[Deck/Labware]
end
subgraph "Configuration"
M[ConfigLoader]
N[JSON Config Files]
end
A --> D
B --> C
C --> D
D --> E
D --> G
D --> I
E --> F
F --> J
G --> E
H --> E
I --> K
E --> L
M --> N
D --> M
E --> M
Layer Details¶
1. User Interface Layer¶
Purpose: Entry point for automation tasks and user interaction
Components:
- User Scripts: Python scripts written by users to automate specific tasks
- GUI View: Touchscreen interface for interactive control and monitoring
Responsibilities:
- Display system state and progress
- Accept user input (well selection, weights, configuration)
- Provide visual feedback
- Define high-level workflows
2. Presentation Layer (GUI Only)¶
Purpose: Coordinate GUI with hardware operations
Components:
- JubileeViewModel: MVVM-inspired coordinator between GUI and JubileeManager
Responsibilities:
- Manage hardware configuration before connection
- Drive JubileeManager to execute operations
- Execute multi-well dispensing jobs systematically
- Provide callbacks to update GUI on progress
- Handle errors with user-friendly messages
Key Design:
- GUI (View) → ViewModel (Coordinator) → JubileeManager (Model)
- ViewModel drives the hardware, not just responds to it
- All hardware state stored in JubileeManager
- Callbacks provide real-time updates to GUI
3. Coordination Layer¶
Purpose: Coordinate complex multi-component operations
Components:
- JubileeManager: Central orchestrator for all operations
Responsibilities:
- Connect to and manage all hardware components
- Provide high-level API for common operations
- Coordinate multi-step operations (e.g., dispense_to_well)
- Store all hardware state (dispensers, pistons, positions)
- Handle errors and provide meaningful feedback
Key Design Decisions:
- Single point of access for most operations
- Owns the state machine (cannot be bypassed)
- Provides both convenience methods and component access
- All hardware state lives here
4. Validation Layer¶
Purpose: Ensure all operations are safe and valid
Components:
- MotionPlatformStateMachine: Validates and executes movements
- MovementExecutor: Executes validated movements on hardware
Responsibilities:
- Track current system state (position, tool, payload)
- Validate requested movements against current state
- Enforce movement constraints and safe zones
- Provide detailed error messages for invalid requests
- Execute validated movements
State Machine States:
The state machine tracks:
- Position: Current named position (e.g., "global_ready", "scale_ready")
- Active Tool: Which tool is currently picked up (or None)
- Payload: What the manipulator is holding (empty, mold, mold_with_piston)
Validation Rules:
- Tool must be picked up to move to certain positions
- Payload state affects which movements are allowed
- Some operations require specific starting positions
5. Component Layer¶
Purpose: Represent individual hardware components
Components:
- Manipulator: Gripper tool with vertical axis
- PistonDispenser: Container for pistons with tracking
- Scale: Weight measurement device
Responsibilities:
- Encapsulate component-specific logic
- Provide component-specific operations
- Maintain component state
- Interact with validation layer for movements
6. Hardware Layer¶
Purpose: Physical hardware interface
Components:
- Jubilee Machine: CNC motion platform (via science-jubilee library)
- Scale Hardware: Precision balance (via serial connection)
- Deck/Labware: Physical deck layout and labware definitions
Responsibilities:
- Execute physical movements
- Report sensor readings
- Handle low-level communication protocols
7. Configuration¶
Purpose: Centralize system configuration
Components:
- ConfigLoader: Loads and provides access to configuration
- JSON Config Files: Define positions, deck layouts, system parameters
Configuration Files:
motion_platform_positions.json: State machine positions and transitionssystem_config.json: System-level settingsmold_labware.json: Deck layout and labware definitionsweight_well_deck.json: Well-specific configurations
Data Flow¶
Example: GUI-Driven Multi-Well Job¶
This example traces how a multi-well dispensing job flows through the GUI system:
sequenceDiagram
participant U as User
participant GUI as GUI View
participant VM as ViewModel
participant JM as JubileeManager
participant SM as StateMachine
participant H as Hardware
U->>GUI: Select molds 0, 1
U->>GUI: Set weights 50g, 45g
U->>GUI: Click "Start Job"
GUI->>VM: start_job([0:50g, 1:45g])
VM->>VM: Validate pistons available
VM->>VM: Start background thread
Note over VM: Thread for mold 0
VM->>JM: dispense_to_well("0", 50.0)
JM->>SM: Move, pick, fill, piston
SM->>H: Execute operations
H-->>VM: Complete
VM->>GUI: on_job_progress(0, 2, "0")
GUI->>U: "Processing 0 (1/2)"
Note over VM: Thread for mold 1
VM->>JM: dispense_to_well("1", 45.0)
JM->>SM: Move, pick, fill, piston
SM->>H: Execute operations
H-->>VM: Complete
VM->>GUI: on_job_progress(1, 2, "1")
GUI->>U: "Processing 1 (2/2)"
VM->>GUI: on_job_completed()
GUI->>U: "Job Complete!" dialog
Example: Direct Script Operation¶
For comparison, a direct script operation (without GUI):
sequenceDiagram
participant U as User Script
participant JM as JubileeManager
participant SM as StateMachine
participant M as Manipulator
participant S as Scale
participant H as Hardware
U->>JM: dispense_to_well("0", 50.0)
JM->>SM: validated_move_to_mold_slot("0")
SM->>SM: Check current state
SM->>SM: Validate movement
SM->>H: Execute movement
H-->>SM: Movement complete
SM-->>JM: Success
JM->>M: pick_mold("0")
M->>SM: Update payload state
M->>H: Execute gripper
JM->>SM: validated_move_to_scale()
SM->>H: Execute movement
JM->>M: place_mold_on_scale()
M->>H: Execute gripper
M->>SM: Update payload state
JM->>SM: validated_fill_powder(50.0)
SM->>S: Read weight
SM->>H: Control trickler
SM->>S: Read weight
Note over SM,S: Repeat until target reached
JM->>M: pick_mold_from_scale()
JM->>SM: validated_move_to_dispenser(0)
JM->>SM: validated_retrieve_piston(0)
JM->>SM: validated_move_to_mold_slot("0")
JM->>M: place_mold("0")
JM-->>U: True (success)
Key Design Principles¶
Safety Through Validation¶
All movements must pass through the state machine validator. This prevents:
- Moving to unsafe positions
- Collisions between tools and labware
- Invalid state transitions
- Operating on wrong component
Progressive Disclosure¶
The architecture supports multiple levels of complexity:
- Simple: Use JubileeManager methods (most users)
- Advanced: Access state machine directly (power users)
- Expert: Access components and machine directly (developers)
State Tracking¶
The system maintains comprehensive state:
- Physical position of motion platform
- Active tool and payload
- Component states (dispenser piston counts, etc.)
- Configuration data
Fail-Safe Defaults¶
When operations fail:
- Clear error messages explain what went wrong
- System state remains consistent
- No silent failures
- Failed operations return False/ValidationResult
Configuration-Driven¶
Physical parameters are in configuration files, not code:
- Easy to adapt to different setups
- No recompilation needed
- Version control for configurations
- Validation of configuration data
Component Interactions¶
JubileeManager ↔ StateMachine¶
- JubileeManager owns the StateMachine
- All movements go through StateMachine validation
- JubileeManager coordinates multi-step operations
- StateMachine enforces single-step safety
Manipulator ↔ StateMachine¶
- Manipulator updates payload state via StateMachine
- State machine validates movements based on payload
- Manipulator uses StateMachine for movements
Configuration ↔ All Components¶
- All components read their configuration via ConfigLoader
- Positions, speeds, and parameters come from JSON
- Configuration is loaded once at startup
Extending the System¶
Adding New Operations¶
To add a new high-level operation:
- Add method to
JubileeManager - Break down into state machine operations
- Call
validated_*methods on state machine - Handle errors and return success/failure
Adding New Hardware¶
To add a new hardware component:
- Create new component class (like
PistonDispenser) - Add component to state machine context
- Add validation logic if needed
- Add configuration entries
- Add convenience methods to JubileeManager
Adding New Positions¶
To add a new named position:
- Add entry to
motion_platform_positions.json - Define allowed transitions
- Define constraints (tool required, payload restrictions)
- Add convenience method to JubileeManager if needed
Performance Considerations¶
Movement Optimization¶
- State machine can batch movements when safe
- Configuration sets appropriate feed rates
- Direct paths when validated as safe
Error Handling¶
- Validation happens before movement (fail fast)
- Clear error messages reduce debugging time
- State preserved on failure for recovery
Security Considerations¶
Access Control¶
- State machine prevents bypass of validation
- JubileeManager owns state machine exclusively
- Configuration files control physical limits
Safety Zones¶
- Configuration defines safe operating areas
- State machine enforces these boundaries
- Emergency stop accessible via hardware
Next Steps¶
- Review the Glossary for terminology
- Explore JubileeManager API
- Understand State Machine Details