Sunday, 5 July 2015

FSM in Akka

A FSM can be described as a set of relations of the form:
State(S) x Event(E) -> Actions (A), State(S’)
These relations are interpreted as meaning:
If we are in state S and the event E occurs, we should perform the actions A and make a transition to the state S’.


The basic strategy is to declare the actor, mixing in the FSM trait and specifying the possible states and data values as type parameters. Within the body of the actor a DSL is used for declaring the state machine:
  • startWith defines the initial state and initial data
  • then there is one when(<state>) { ... } declaration per state to be handled (could potentially be
    multiple ones, the passed PartialFunction will be concatenated using orElse)
  • finally starting it up using initialize, which performs the transition into the initial state and sets up
    timers (if required).

The FSM trait inherits directly from Actor, when you extend FSM you must be aware that an actor is actually created. 

class Buncher extends FSM[State, Data] {

  // fsm body ...

  initialize()
}


The FSM trait takes two type parameters:
1. the supertype of all state names, usually a sealed trait with case objects extending it,

2. the type of the state data which are tracked by the FSM module itself.

The state data together with the state name describe the internal state of the state machine
A state is defined by one or more invocations of the methodwhen(<name>[, stateTimeout = <timeout>])(stateFunction).

The given name must be an object which is type-compatible with the first type parameter given to the FSM trait. This object is used as a hash key, so you must ensure that it properly implements equals and hashCode; in particular it must not be mutable. The easiest fit for these requirements are case objects.

If the stateTimeout parameter is given, then all transitions into this state, including staying, receive this time- out by default. Initiating the transition with an explicit timeout may be used to override this default, see Initiating Transitions for more information. The state timeout of any state may be changed during action processing with setStateTimeout(state, duration).Thisenablesruntimeconfiguratione.g.viaexternalmessage.

The stateFunction argument is a PartialFunction[Event, State], which is conveniently given using the partial function literal syntax as demonstrated below:

when(Idle) {
  case Event(SetTarget(ref), Uninitialized) =>

    stay using Todo(ref, Vector.empty)
}

when(Active, stateTimeout = 1 second) {
  case Event(Flush | StateTimeout, t: Todo) =>

    goto(Idle) using t.copy(queue = Vector.empty)
}


TheEvent(msg: Any, data: D)caseclassisparameterizedwiththedatatypeheldbytheFSMforcon- venient pattern matching.



Defining the Initial State
Each FSM needs a starting point, which is declared using
    startWith(state, data[, timeout])

The optionally given timeout argument overrides any specification given for the desired initial state. If you want to cancel a default timeout, use Duration.Inf.


Unhandled Events
If a state doesn’t handle a received event a warning is logged. If you want to do something else in this case you can specify that with whenUnhandled(stateFunction)



You do not need to worry about the exact order with respect to setting the internal state variable, as everything within the FSM actor is running single-threaded anyway.

No comments:

Post a Comment