View Source Finitomata behaviour (Finitomata v0.5.0)

bird-view

Bird View

Finitomata provides a boilerplate for FSM implementation, allowing to concentrate on the business logic rather than on the process management and transitions/events consistency tweaking.

It reads a description of the FSM from a string in PlantUML, Mermaid, or even custom format. Basically, it looks more or less like this

PlantUML

[*] --> s1 : to_s1
s1 --> s2 : to_s2
s1 --> s3 : to_s3
s2 --> [*] : ok
s3 --> [*] : ok

Mermaid

s1 --> |to_s2| s2
s1 --> |to_s3| s3

Note

mermaid does not allow to explicitly specify transitions (and hence event names) from the starting state and to the end state(s), these states names are implicitly set to :* and events to :__start__ and :__end__ respectively.

Finitomata validates the FSM is consistent, namely it has a single initial state, one or more final states, and no orphan states. If everything is OK, it generates a GenServer that could be used both alone, and with provided supervision tree. This GenServer requires to implement three callbacks

  • on_transition/4 — mandatory
  • on_failure/3 — optional
  • on_enter/2 — optional
  • on_exit/2 — optional
  • on_terminate/1 — optional

All the callbacks do have a default implementation, that would perfectly handle transitions having a single to state and not requiring any additional business logic attached.

Upon start, it moves to the next to initial state and sits there awaiting for the transition request. Then it would call an on_transition/4 callback and move to the next state, or remain in the current one, according to the response.

Upon reachiung a final state, it would terminate itself. The process keeps all the history of states it went through, and might have a payload in its state.

example

Example

Let’s define the FSM instance

defmodule MyFSM do
  @fsm """
  [*] --> s1 : to_s1
  s1 --> s2 : to_s2
  s1 --> s3 : to_s3
  s2 --> [*] : ok
  s3 --> [*] : ok
  """

  use Finitomata, @fsm

  def on_transition(:s1, :to_s2, event_payload, state_payload),
    do: {:ok, :s2, state_payload}
end

Now we can play with it a bit.

children = [Finitomata.child_spec()]
Supervisor.start_link(children, strategy: :one_for_one)

Finitomata.start_fsm MyFSM, "My first FSM", %{foo: :bar}
Finitomata.transition "My first FSM", {:to_s2, nil}
Finitomata.state "My first FSM"                    
#⇒ %Finitomata.State{current: :s2, history: [:s1], payload: %{foo: :bar}}

Finitomata.allowed? "My first FSM", :* # state
#⇒ true
Finitomata.responds? "My first FSM", :to_s2 # event
#⇒ false

Finitomata.transition "My first FSM", {:ok, nil} # to final state
#⇒ [info]  [◉ ⇄] [state: %Finitomata.State{current: :s2, history: [:s1], payload: %{foo: :bar}}]

Finitomata.alive? "My first FSM"
#⇒ false

Typically, one would implement all the on_transition/4 handlers, pattern matching on the state/event.

Link to this section Summary

Types

The payload that can be passed to each call to transition/3

Error types of FSM validation

Callbacks

This callback will be called on entering the state.

This callback will be called on exiting the state.

This callback will be called if the transition failed to complete to allow the consumer to take an action upon failure.

This callback will be called on transition to the final state to allow the consumer to perform some cleanup, or like.

This callback will be called from each transition processor.

Functions

Returns true if the supervision tree is alive, false otherwise.

Returns true if the FSM specified is alive, false otherwise.

Returns true if the transition to the state state is possible, false otherwise.

Returns true if the transition by the event event is possible, false otherwise.

Starts the FSM instance.

The state of the FSM.

Initiates the transition.

Link to this section Types

@type event_payload() :: any()

The payload that can be passed to each call to transition/3

@type validation_error() ::
  :initial_state | :final_state | :orphan_from_state | :orphan_to_state

Error types of FSM validation

Link to this section Callbacks

Link to this callback

on_enter(state, t)

View Source (optional)
@callback on_enter(Finitomata.Transition.state(), Finitomata.State.t()) :: :ok

This callback will be called on entering the state.

Link to this callback

on_exit(state, t)

View Source (optional)
@callback on_exit(Finitomata.Transition.state(), Finitomata.State.t()) :: :ok

This callback will be called on exiting the state.

Link to this callback

on_failure(event, event_payload, t)

View Source (optional)
@callback on_failure(Finitomata.Transition.event(), event_payload(), Finitomata.State.t()) ::
  :ok

This callback will be called if the transition failed to complete to allow the consumer to take an action upon failure.

Link to this callback

on_terminate(t)

View Source (optional)
@callback on_terminate(Finitomata.State.t()) :: :ok

This callback will be called on transition to the final state to allow the consumer to perform some cleanup, or like.

Link to this callback

on_transition( state, event, event_payload, payload )

View Source

This callback will be called from each transition processor.

Link to this section Functions

@spec alive?() :: boolean()

Returns true if the supervision tree is alive, false otherwise.

@spec alive?(GenServer.name()) :: boolean()

Returns true if the FSM specified is alive, false otherwise.

Returns true if the transition to the state state is possible, false otherwise.

Link to this function

responds?(target, event)

View Source

Returns true if the transition by the event event is possible, false otherwise.

Link to this function

start_fsm(impl, name, payload)

View Source
@spec start_fsm(module(), any(), any()) :: DynamicSupervisor.on_start_child()

Starts the FSM instance.

The arguments are

  • the implementation of FSM (the module, having use Finitomata)
  • the name of the FSM (might be any term, but it must be unique)
  • the payload to be carried in the FSM state during the lifecycle

The FSM is started supervised.

@spec state(GenServer.name()) :: Finitomata.State.t()

The state of the FSM.

Initiates the transition.

The arguments are

  • the name of the FSM
  • {event, event_payload} tuple; the payload will be passed to the respective on_transition/4 call