FSM Wildcard Transitions: Catch-All Mechanism¶
Overview¶
The ABS Platform FSM implementation supports wildcard transitions using the "*" symbol as the from state. This provides a catch-all mechanism for global actions that should be available from any state, reducing verbosity and improving maintainability.
How It Works¶
Wildcard Syntax¶
type MealyTransition {
from: String! # Source state (must be in States) or "*" for wildcard (any state)
input: String! # Input symbol (must be in Inputs)
to: String! # Target state (must be in States)
# ... other fields
}
Priority System¶
- Specific transitions (e.g.,
from: "RUNNING") have higher priority - Wildcard transitions (e.g.,
from: "*") have lower priority - When both specific and wildcard transitions match, the specific one is chosen
Usage Examples¶
1. Global Reset Function¶
// Reset from any state to IDLE
{
id: 'global_reset',
from: '*', // Wildcard: matches any state
input: 'RESET',
to: 'IDLE',
outputs: ['reset_signal'],
description: 'Reset from any state to idle',
priority: 100, // Lower priority than specific transitions
created_at: new Date()
}
2. Emergency Stop¶
// Emergency stop from any state to ERROR
{
id: 'emergency_stop',
from: '*', // Wildcard: matches any state
input: 'EMERGENCY_STOP',
to: 'ERROR',
outputs: ['emergency_stop', 'error_alarm'],
description: 'Emergency stop from any state',
priority: 50, // Higher priority than RESET but lower than specific transitions
created_at: new Date()
}
3. System Shutdown¶
// Graceful shutdown from any state
{
id: 'system_shutdown',
from: '*',
input: 'SHUTDOWN',
to: 'OFFLINE',
outputs: ['shutdown_signal', 'save_state'],
description: 'System shutdown from any state',
priority: 75,
created_at: new Date()
}
Implementation Details¶
Transition Matching Logic¶
function findValidTransition(fsmInstance, currentState, input) {
const matchingTransitions = allTransitions.filter(t => {
// Check for exact match: from state matches current state
if (t.from === currentState && t.input === input) {
return true;
}
// Check for wildcard match: from state is "*" (any state)
if (t.from === "*" && t.input === input) {
return true;
}
return false;
});
// Sort by priority (specific transitions first)
matchingTransitions.sort((a, b) => {
if (a.from !== "*" && b.from === "*") return -1;
if (a.from === "*" && b.from !== "*") return 1;
return (a.priority || 0) - (b.priority || 0);
});
return matchingTransitions[0] || null;
}
Priority Rules¶
- Specific transitions always take precedence over wildcard transitions
- Among specific transitions, lower priority numbers win
- Among wildcard transitions, lower priority numbers win
- Wildcard transitions are useful for global actions but should not override specific behavior
Benefits¶
1. Reduced Verbosity¶
Without wildcards (repetitive):
transitions: [
{ from: 'IDLE', input: 'RESET', to: 'IDLE', outputs: ['reset'] },
{ from: 'RUNNING', input: 'RESET', to: 'IDLE', outputs: ['reset'] },
{ from: 'PAUSED', input: 'RESET', to: 'IDLE', outputs: ['reset'] },
{ from: 'ERROR', input: 'RESET', to: 'IDLE', outputs: ['reset'] },
// ... repeat for every state
]
With wildcards (concise):
transitions: [
{ from: '*', input: 'RESET', to: 'IDLE', outputs: ['reset'], priority: 100 },
// ... specific transitions only where needed
]
2. Maintainability¶
- Global actions defined once, not repeated for each state
- Easy to add new global actions without touching every state
- Clear separation between specific and global behavior
3. Type Safety¶
- Maintains GraphQL schema validation
- Clear documentation of wildcard behavior
- Consistent with existing FSM patterns
Best Practices¶
1. Use Appropriate Priorities¶
// Specific transitions: priority 1-10
{ from: 'RUNNING', input: 'stop', to: 'IDLE', priority: 1 }
// Wildcard transitions: priority 50-100
{ from: '*', input: 'RESET', to: 'IDLE', priority: 100 }
{ from: '*', input: 'EMERGENCY_STOP', to: 'ERROR', priority: 50 }
2. Clear Naming¶
// Use descriptive names for wildcard transitions
{ id: 'global_reset', from: '*', input: 'RESET', ... }
{ id: 'emergency_stop', from: '*', input: 'EMERGENCY_STOP', ... }
3. Appropriate Use Cases¶
Good candidates for wildcards: - System-wide actions (RESET, SHUTDOWN, EMERGENCY_STOP) - Global error handling - Maintenance operations - Debug/development actions
Avoid wildcards for: - State-specific business logic - Normal workflow transitions - Actions that should behave differently per state
Example: Complete FSM with Wildcards¶
const fsmTemplate = {
states: ['IDLE', 'RUNNING', 'PAUSED', 'ERROR'],
inputs: ['start', 'stop', 'pause', 'resume', 'error', 'RESET', 'EMERGENCY_STOP'],
outputs: ['enable', 'disable', 'pause_signal', 'resume_signal', 'error_alarm', 'reset_signal', 'emergency_stop'],
initial: 'IDLE',
transitions: [
// Specific transitions (normal workflow)
{ from: 'IDLE', input: 'start', to: 'RUNNING', outputs: ['enable'], priority: 1 },
{ from: 'RUNNING', input: 'stop', to: 'IDLE', outputs: ['disable'], priority: 1 },
{ from: 'RUNNING', input: 'pause', to: 'PAUSED', outputs: ['pause_signal'], priority: 1 },
{ from: 'PAUSED', input: 'resume', to: 'RUNNING', outputs: ['resume_signal'], priority: 1 },
{ from: 'PAUSED', input: 'stop', to: 'IDLE', outputs: ['disable'], priority: 1 },
{ from: 'RUNNING', input: 'error', to: 'ERROR', outputs: ['error_alarm'], priority: 1 },
// Wildcard transitions (global actions)
{ from: '*', input: 'RESET', to: 'IDLE', outputs: ['reset_signal'], priority: 100 },
{ from: '*', input: 'EMERGENCY_STOP', to: 'ERROR', outputs: ['emergency_stop', 'error_alarm'], priority: 50 }
]
};
Testing¶
The wildcard functionality can be tested using the provided example:
# Run the wildcard example
# FSM wildcard transitions are now handled by JSON templates
# See models/battery-swap-service/bss-fsm.json for examples
This will demonstrate: - Wildcard transitions working from different states - Priority system (specific vs wildcard) - Proper logging of wildcard usage - Error handling and warnings
Conclusion¶
Wildcard transitions provide a clean, maintainable way to implement global actions in FSMs while preserving the clarity and type safety of the state machine formalism. They reduce verbosity and make it easier to add system-wide behaviors without cluttering the transition definitions.