View Source Finitomata.Transition (Finitomata v0.29.0)

The internal representation of Transition.

It includes from and to states, and the event, all represented as atoms.

Summary

Types

The event in FSM

The kind of event

The state of FSM

t()

The transition is represented by from and to states and the event.

Functions

Returns the list of all the transitions, matching the options.

Returns the list of all the transitions, matching the from state and the event.

Returns true if the transition fromto is allowed, false otherwise.

Returns keyword list of {Finitomata.Transition.state(), [Finitomata.Transition.event()]} for transitions which do not have a determined to state.

Returns the continuation from the state given which inevitably lead to other state(s).

Returns keyword list of {Finitomata.Transition.state(), Finitomata.Transition.event()} tuples for determined transition from the current state.

Returns {:ok, {event(), state()}} tuple if there is a determined transition from the current state, :error otherwise.

Returns the state after starting one, so-called entry state.

Returns the kind of event.

Returns the not ordered list of events, including or excluding the internal starting and ending transitions :__start__ and __end__ according to the second argument.

Returns the states before ending one, so-called exit states.

Returns all the states which inevitably lead to the ending one.

Tries to guess the next state based on current state and event

Returns all the hard transitions which inevitably lead to the next state (events ending with an exclamation sign,) which makes the FSM to go to the next state with :continue callback.

Returns all the loops aka internal paths where starting and ending states are the same one.

Returns all the paths from starting to ending state.

Returns true if the state from hsa an outgoing transition with event, false otherwise.

Returns the not ordered list of states, including or excluding the starting and ending states :* according to the second argument.

Returns the minimal number of steps required to get from from state to to state, including hard transitions

Returns the minimal number of steps required to get from from state to to state, omitting hard transitions

Returns the straight paths from starting to ending state.

Types

event()

@type event() :: atom()

The event in FSM

event_kind()

@type event_kind() :: :soft | :hard | :normal

The kind of event

state()

@type state() :: atom()

The state of FSM

t()

@type t() :: %Finitomata.Transition{
  from: state(),
  to: state() | [state()],
  event: event()
}

The transition is represented by from and to states and the event.

Functions

allowed(transitions, options \\ [])

@spec allowed([t()],
  from: state(),
  to: state(),
  with: event(),
  as: :states | :transitions
) :: [
  {state(), state(), event()}
]

Returns the list of all the transitions, matching the options.

Used internally for the validations.

iex> {:ok, transitions} =
...>   Finitomata.Mermaid.parse(
...>     "idle --> |to_s1| s1\n" <>
...>     "s1 --> |to_s2| s2\n" <>
...>     "s1 --> |to_s3| s3\n" <>
...>     "s2 --> |to_s3| s3")
...> Finitomata.Transition.allowed(transitions, to: [:idle, :*])
[{:*, :idle, :__start__}, {:s3, :*, :__end__}]
iex> Finitomata.Transition.allowed(transitions, from: :s1)
[{:s1, :s2, :to_s2}, {:s1, :s3, :to_s3}]
iex> Finitomata.Transition.allowed(transitions, from: :s1, to: :s3)
[{:s1, :s3, :to_s3}]
iex> Finitomata.Transition.allowed(transitions, from: :s1, with: :to_s3)
[{:s1, :s3, :to_s3}]
iex> Finitomata.Transition.allowed(transitions, from: :s2, with: :to_s2)
[]

allowed(transitions, from, event)

@spec allowed([t()], state(), event()) :: [state()]

Returns the list of all the transitions, matching the from state and the event.

Used internally for the validations.

iex> {:ok, transitions} =
...>   Finitomata.PlantUML.parse("[*] --> s1 : foo\ns1 --> s2 : ok\ns2 --> [*] : ko")
...> Finitomata.Transition.allowed(transitions, :s1, :foo)
[:s2]
...> Finitomata.Transition.allowed(transitions, :s1, :*)
[]

allowed?(transitions, from, to)

@spec allowed?([t()], state(), state()) :: boolean()

Returns true if the transition fromto is allowed, false otherwise.

iex> {:ok, transitions} =
...>   Finitomata.PlantUML.parse("[*] --> s1 : foo\ns1 --> s2 : ok\ns2 --> [*] : ko")
...> Finitomata.Transition.allowed?(transitions, :s1, :s2)
true
...> Finitomata.Transition.allowed?(transitions, :s1, :*)
false

ambiguous(transitions)

@spec ambiguous([t()]) :: [{state(), {event(), state()}}]

Returns keyword list of {Finitomata.Transition.state(), [Finitomata.Transition.event()]} for transitions which do not have a determined to state.

Used internally for the validations.

iex> {:ok, transitions} =
...>   Finitomata.Mermaid.parse(
...>     "idle --> |to_s1| s1\n" <>
...>     "s1 --> |to_s2| s2\n" <>
...>     "s1 --> |to_s3| s3\n" <>
...>     "s2 --> |to_s1| s3\n" <>
...>     "s2 --> |ambiguous| s3\n" <>
...>     "s2 --> |ambiguous| s4\n" <>
...>     "s3 --> |determined| s3\n" <>
...>     "s3 --> |determined| s4\n")
...> Finitomata.Transition.ambiguous(transitions)
[s3: {:determined, [:s3, :s4]}, s2: {:ambiguous, [:s3, :s4]}]

continuation(states \\ :states, from, transitions)

@spec continuation(:states | :transitions, state(), [t()]) ::
  nil | [Finitomata.Transition.Path.t()] | [t()]

Returns the continuation from the state given which inevitably lead to other state(s).

All the transitions from this state are hard (ending with !,) which makes the FSM to go through all these states in :continue callbacks.

iex> {:ok, transitions} =
...>   Finitomata.PlantUML.parse("[*] --> entry : start\nentry --> exit : go!\nexit --> done : finish\ndone --> [*] : terminate")
...> Finitomata.Transition.continuation(:entry, Finitomata.Transition.hard(:transitions, transitions))
[%Finitomata.Transition.Path{from: :entry, to: :exit, path: [go!: :exit]}]

count(what \\ :states, transitions)

@spec count(:states | :transitions, [t()]) :: non_neg_integer()

determined(transitions)

@spec determined([t()]) :: [{state(), {event(), state()}}]

Returns keyword list of {Finitomata.Transition.state(), Finitomata.Transition.event()} tuples for determined transition from the current state.

The transition is determined, if it is the only transition allowed from the state.

Used internally for the validations.

iex> {:ok, transitions} =
...>   Finitomata.Mermaid.parse(
...>     "idle --> |to_s1| s1\n" <>
...>     "s1 --> |to_s2| s2\n" <>
...>     "s1 --> |to_s3| s3\n" <>
...>     "s2 --> |to_s1| s3\n" <>
...>     "s2 --> |ambiguous| s3\n" <>
...>     "s2 --> |ambiguous| s4\n" <>
...>     "s3 --> |determined| s3\n" <>
...>     "s3 --> |determined| s4\n")
...> Finitomata.Transition.determined(transitions)
[s4: :__end__, s3: :determined, idle: :to_s1]

determined(transitions, state)

@spec determined([t()], state()) :: {:ok, {event(), state()}} | :error

Returns {:ok, {event(), state()}} tuple if there is a determined transition from the current state, :error otherwise.

The transition is determined, if it is the only transition allowed from the state.

Used internally for the validations.

iex> {:ok, transitions} =
...>   Finitomata.Mermaid.parse(
...>     "idle --> |to_s1| s1\n" <>
...>     "s1 --> |to_s2| s2\n" <>
...>     "s1 --> |to_s3| s3\n" <>
...>     "s2 --> |to_s3| s3")
...> Finitomata.Transition.determined(transitions, :s1)
:error
iex> Finitomata.Transition.determined(transitions, :s2)
{:ok, {:to_s3, :s3}}
iex> Finitomata.Transition.determined(transitions, :s3)
{:ok, {:__end__, :*}}

entry(what \\ :state, transitions)

@spec entry(:state | :transition, [t()]) :: state()

Returns the state after starting one, so-called entry state.

iex> {:ok, transitions} =
...>   Finitomata.PlantUML.parse("[*] --> entry : foo\nentry --> exit : go\nexit --> [*] : terminate")
...> Finitomata.Transition.entry(transitions)
:entry

event_kind(event)

@spec event_kind(event() | t()) :: event_kind()

Returns the kind of event.

If event ends up with an exclamation sign, it’s :hard, meaning the respective transition would be initiated automatically when the from state of such a transition is reached.

If event ends up with a question mark, it’s :soft, meaning no error would have been reported in a case transition fails.

Otherwise the event is :normal.

iex> {:ok, [_, hard, _]} =
...>   Finitomata.PlantUML.parse("[*] --> entry : foo\nentry --> exit : go!\nexit --> [*] : terminate")
...> Finitomata.Transition.event_kind(hard)
:hard

events(transitions, purge_internal \\ false)

@spec events([t()], boolean()) :: [state()]

Returns the not ordered list of events, including or excluding the internal starting and ending transitions :__start__ and __end__ according to the second argument.

iex> {:ok, transitions} =
...>   Finitomata.Mermaid.parse("s1 --> |ok| s2\ns1 --> |ko| s3")
...> Finitomata.Transition.events(transitions, true)
[:ok, :ko]
...> Finitomata.Transition.events(transitions)
[:__start__, :ok, :ko, :__end__]

exit(what \\ :states, transitions)

@spec exit(:states | :transitions, [t()]) :: [state()]

Returns the states before ending one, so-called exit states.

iex> {:ok, transitions} =
...>   Finitomata.Mermaid.parse(
...>     "entry --> |process| processing\nprocessing --> |ok| success\nprocessing --> |ko| error"
...>   )
...> Finitomata.Transition.exit(transitions)
[:error, :success]

exiting(what \\ :states, transitions)

@spec exiting(:states | :transitions, [t()]) ::
  Enumerable.t([t()]) | [Finitomata.Transition.Path.t()]

Returns all the states which inevitably lead to the ending one.

All the transitions from these states to the ending one are hard (ending with !,) which makes the FSM to go through all these states in :continue callbacks.

iex> {:ok, transitions} =
...>   Finitomata.PlantUML.parse("[*] --> entry : start\nentry --> exit : go!\nexit --> [*] : terminate")
...> Finitomata.Transition.exiting(transitions)
[%Finitomata.Transition.Path{from: :entry, to: :*, path: [go!: :exit, terminate: :*]}]

guess_next_state(transitions, current, event, payload)

@spec guess_next_state([t()], state(), event(), Finitomata.State.payload()) ::
  Finitomata.transition_resolution()

Tries to guess the next state based on current state and event

iex> {:ok, transitions} = Finitomata.Mermaid.parse("s1 --> |ok| s2\ns1 --> |ko| s3")
...> Finitomata.Transition.guess_next_state(transitions, :s1, :ok, %{})
{:ok, :s2, %{}}

iex> {:ok, transitions} = Finitomata.Mermaid.parse("s1 --> |ok| s2\ns1 --> |ok| s3")
...> Finitomata.Transition.guess_next_state(transitions, :s1, :ok, %{})
{:error, {:ambiguous_transition, {:s1, :ok}, [:s2, :s3]}}

hard(states \\ :states, transitions)

@spec hard(:states | :transitions, [t()]) ::
  Enumerable.t(t()) | [Finitomata.Transition.Path.t()]

Returns all the hard transitions which inevitably lead to the next state (events ending with an exclamation sign,) which makes the FSM to go to the next state with :continue callback.

iex> {:ok, transitions} =
...>   Finitomata.PlantUML.parse("[*] --> entry : start\nentry --> exit : go!\nexit --> [*] : terminate")
...> Finitomata.Transition.hard(transitions)
[entry: :go!]
...> Finitomata.Transition.hard(:transitions, transitions)
[%Finitomata.Transition{from: :entry, to: :exit, event: :go!}]

loops(what \\ :states, transitions)

@spec loops(:states | :transitions, [t()]) ::
  Enumerable.t(t()) | [Finitomata.Transition.Path.t()]

Returns all the loops aka internal paths where starting and ending states are the same one.

iex> {:ok, transitions} =
...>   Finitomata.PlantUML.parse("[*] --> s1 : foo\ns1 --> s2 : ok\ns2 --> s1 : ok\ns2 --> [*] : ko")
...> Finitomata.Transition.loops(transitions)
[%Finitomata.Transition.Path{from: :s1, to: :s1, path: [ok: :s2, ok: :s1]},
 %Finitomata.Transition.Path{from: :s2, to: :s2, path: [ok: :s1, ok: :s2]}]

paths(what \\ :states, transitions, from \\ :*, to \\ :*)

@spec paths(:states | :transitions, [t()], state(), state()) ::
  Enumerable.t(t()) | [Finitomata.Transition.Path.t()]

Returns all the paths from starting to ending state.

iex> {:ok, transitions} =
...>   Finitomata.PlantUML.parse("[*] --> s1 : foo\ns1 --> s2 : ok\ns1 --> s3 : ok\ns2 --> [*] : ko\ns3 --> [*] : ko")
...> Finitomata.Transition.paths(transitions)
[%Finitomata.Transition.Path{from: :*, to: :*, path: [foo: :s1, ok: :s2, ko: :*]},
 %Finitomata.Transition.Path{from: :*, to: :*, path: [foo: :s1, ok: :s3, ko: :*]}]

responds?(transitions, from, event)

@spec responds?([t()], state(), event()) :: boolean()

Returns true if the state from hsa an outgoing transition with event, false otherwise.

iex> {:ok, transitions} =
...>   Finitomata.PlantUML.parse("[*] --> s1 : foo\ns1 --> s2 : ok\ns2 --> [*] : ko")
...> Finitomata.Transition.responds?(transitions, :s1, :ok)
true
...> Finitomata.Transition.responds?(transitions, :s1, :ko)
false

shortest_paths(what \\ :states, transitions, from \\ :*, to \\ :*, handled_only? \\ false)

@spec shortest_paths(:states | :transitions, [t()], state(), state(), boolean()) ::
  Enumerable.t(t()) | [Finitomata.Transition.Path.t()]

Returns the shortest path from starting to ending state.

iex> {:ok, transitions} =
...>   Finitomata.PlantUML.parse("[*] --> s1 : foo\ns1 --> s2 : ok\ns1 --> s3 : ok\ns2 --> [*] : ko\ns3 --> s4 : step\ns4 --> [*] : ko")
...> Finitomata.Transition.shortest_paths(transitions)
[%Finitomata.Transition.Path{from: :*, to: :*, path: [foo: :s1, ok: :s2, ko: :*]}]

states(transitions, purge_internal \\ false)

@spec states([t()], boolean()) :: [state()]

Returns the not ordered list of states, including or excluding the starting and ending states :* according to the second argument.

iex> {:ok, transitions} =
...>   Finitomata.PlantUML.parse("[*] --> s1 : foo\ns1 --> s2 : ok\ns2 --> [*] : ko")
...> Finitomata.Transition.states(transitions, true)
[:s1, :s2]
...> Finitomata.Transition.states(transitions)
[:*, :s1, :s2]

steps(transitions, from \\ :*, to \\ :*)

@spec steps([t()], state(), state()) :: non_neg_integer()

Returns the minimal number of steps required to get from from state to to state, including hard transitions

steps_handled(transitions, from \\ :*, to \\ :*)

@spec steps_handled([t()], state(), state()) :: non_neg_integer()

Returns the minimal number of steps required to get from from state to to state, omitting hard transitions

straight_paths(what \\ :states, transitions, from \\ :*, to \\ :*)

@spec straight_paths(:states | :transitions, [t()], state(), state()) ::
  Enumerable.t(t()) | Finitomata.Transition.Path.t()

Returns the straight paths from starting to ending state.

iex> {:ok, transitions} =
...>   Finitomata.PlantUML.parse("[*] --> s1 : foo\ns1 --> s2 : ok\ns1 --> s3 : ok\ns2 --> s2 : loop\ns2 --> [*] : ko\ns3 --> s4 : step\ns4 --> [*] : ko")
...> Finitomata.Transition.straight_paths(transitions)
[%Finitomata.Transition.Path{from: :*, to: :*, path: [foo: :s1, ok: :s2, ko: :*]},
 %Finitomata.Transition.Path{from: :*, to: :*, path: [foo: :s1, ok: :s3, step: :s4, ko: :*]}]