Skip to content

Instantly share code, notes, and snippets.

@neilmanuell
Created September 25, 2011 08:43
Show Gist options
  • Save neilmanuell/1240398 to your computer and use it in GitHub Desktop.
Save neilmanuell/1240398 to your computer and use it in GitHub Desktop.
declaration of fluent Latchable Door FSM
// LATCH
// NB: if this Process is called in a State not declared as initiating a process,
// or as throwing an error, nothing will happen, but silently
// The CommandFlowMap will verify that each transition is valid, and all States have been declared.
commandFlowMap.configureProcess()
.onEvent( ProcessEventTypes.LATCH, ProcessEvent )
.ifCurrentState( StateNames.LOCKED )
.thenTransitionTo( StateNames.CLOSED, StateNames.OPENED, StateNames.LATCHED )
.ifCurrentState( StateNames.CLOSED )
.thenTransitionTo( StateNames.OPENED, StateNames.LATCHED )
.ifCurrentState( StateNames.OPENED )
.thenTransitionTo( StateNames.LATCHED )
.ifCurrentState( StateNames.LATCHED )
.thenThrowError();
// OPEN
commandFlowMap.configureProcess()
.onEvent( ProcessEventTypes.OPEN, ProcessEvent )
.ifCurrentState( StateNames.LOCKED )
.thenTransitionTo( StateNames.CLOSED, StateNames.OPENED )
.ifCurrentState( StateNames.CLOSED, StateNames.LATCHED )
.thenTransitionTo( StateNames.OPENED )
.ifCurrentState( StateNames.OPENED )
.thenThrowError();
// CLOSE
commandFlowMap.configureProcess()
.onEvent( ProcessEventTypes.CLOSE, ProcessEvent )
.ifCurrentState( StateNames.LOCKED, StateNames.OPENED )
.thenTransitionTo( StateNames.CLOSED )
.ifCurrentState( StateNames.LATCHED )
.thenTransitionTo( StateNames.OPENED, StateNames.CLOSED )
.ifCurrentState( StateNames.CLOSED )
.thenThrowError();
//LOCK
commandFlowMap.configureProcess()
.onEvent( ProcessEventTypes.LOCK, ProcessEvent )
.ifCurrentState( StateNames.CLOSED )
.thenTransitionTo( StateNames.LOCKED )
.ifCurrentState( StateNames.OPENED )
.thenTransitionTo( StateNames.CLOSED, StateNames.LOCKED )
.ifCurrentState( StateNames.LATCHED )
.thenTransitionTo( StateNames.OPENED, StateNames.CLOSED, StateNames.LOCKED )
.ifCurrentState( StateNames.LOCKED )
.thenThrowError();
commandFlowMap
.configureState( StateNames.LOCKED )
.setEnteringGuards( OnlyIfKeyFitsLock )
.setExitingGuards( OnlyIfKeyFitsLock )
.addTransitionTargets( StateNames.CLOSED );
commandFlowMap
.configureState( StateNames.CLOSED )
.addTransitionTargets( StateNames.LOCKED, StateName.OPEN );
commandFlowMap
.configureState( StateNames.OPENED )
.addTransitionTargets( StateNames.CLOSED, StateName.LATCHED );
commandFlowMap
.configureState( StateNames.LATCHED )
.addTransitionTargets( StateNames.OPENED );
// LOCKED
commandFlowMap
.map.asCommandClass( EngageLockCmd )
.toState( StateNames.LOCKED )
.duringEnteredPhase();
commandFlowMap
.map.asCommandClass( DisengageLockCmd )
.toState( StateNames.LOCKED )
.duringTearDownPhase();
commandFlowMap
.map.asCommandClass( HandleIncorrectKeyMacroCmd )
.toState( StateNames.LOCKED )
.duringCancelledPhase();
// CLOSED
commandFlowMap
.map.asCommandClass( PushDoorToCmd )
.withGuards( OnlyIfDoorAjar )
.toState( StateNames.CLOSED )
.duringEnteredPhase();
// OPENED
commandFlowMap
.map.asCommandClass( PullDoorAjarCmd )
.withGuards( OnlyIfDoorPulledTo )
.toState( StateNames.OPENED )
.duringEnteredPhase();
// LATCHED
commandFlowMap
.map.asCommandClass( EngageLatchCmd )
.toState( StateNames.LATCHED )
.duringEnteredPhase();
commandFlowMap
.map.asCommandClass( DisengageLatchCmd )
.toState( StateNames.LATCHED )
.duringTearDownPhase();
[Inject]
public var eventBus:IEventDispatcher;
[Inject]
public var key:IKey;
public function execute():void {
// starts LOCK process, passing a payload (the key)
// NB: any ongoing process will be cleared on acceptance of the new one.
eventBus.dispatchEvent( new ProcessEvent( ProcessEventTypes.LOCK, key ) );
}
@neilmanuell
Copy link
Author

Fluent FSM

concept

The Fluent FSM is a network of nodes (States) linked together with directional edges (transitions). This network is abstracted from the application's logic by Processes. Processes allow transitions from State to State to be concatenated, depending on the State in which they are called.

Each State may have guards applied to them to forbid entry or exit, they also have three optional phases, entering, teardown and cancelled that can be mapped to the execution of an ICommand.

  • the entered phase is dispatched when entry to the state has been approved, and the current State has changed
  • the teardown phase is dispatched when exit from that state has been approved, and before the current State has changed
  • the cancelled phase is dispatched when exit from that state, or entry to the target state has been disapproved. The current State is unchanged. The cancellation of a transition will cancel the entire Process.

the big assumption

Transitions are synchronous, therefore any ICommands executed as a result of a phase dispatch must also be synchronous, in other words executed in parallel, not in sequence. Any asynchronicity must be off loaded to other functional areas / actors. It is the States that encapsulate Asynchronous behavior, not the Transitions or Processes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment