Skip to content

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

  1. Specific transitions always take precedence over wildcard transitions
  2. Among specific transitions, lower priority numbers win
  3. Among wildcard transitions, lower priority numbers win
  4. 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.