Documentation
¶
Index ¶
- type FSM
- func (m *FSM) Add(state string, transitions Transitions) error
- func (m *FSM) AddOrMerge(state string, transitions Transitions)
- func (m *FSM) AddOrReplace(state string, transitions Transitions)
- func (m *FSM) Do(action string, args ...interface{}) (string, error)
- func (m *FSM) Exists(state string) bool
- func (m *FSM) State() string
- func (m *FSM) States() []string
- func (m *FSM) Subscribe(callback func(state string)) func()
- type Metadata
- type States
- type Transitions
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type FSM ¶
type FSM struct {
// contains filtered or unexported fields
}
FSM is a finite-state machine that can be instantiated using the Machine function.
func Machine ¶
Machine instatiate a new FSM with the given initial state and the given set of possible states.
Note: the given initial state must be within the given possible states.
Example ¶
package main
import (
"fmt"
"interrato.dev/fine"
)
func main() {
powerSwitch := fine.Machine("off", fine.States{
"off": {"toggle": "on"},
"on": {"toggle": "off"},
})
fmt.Println("Current FSM state:", powerSwitch.State())
}
Output: Current FSM state: off
func (*FSM) Add ¶
func (m *FSM) Add(state string, transitions Transitions) error
Add allows to add a new state with its associated transitions. If a state with the same name is already present in the FSM a non-nil error is returned.
Example ¶
package main
import (
"fmt"
"interrato.dev/fine"
)
func main() {
powerSwitch := fine.Machine("off", fine.States{
"off": {"toggle": "on"},
})
// Here I add the "on" state.
powerSwitch.Add("on", fine.Transitions{"toggle": "off"})
// Trying to add the "off" state, which is already in the FSM, will fail.
err := powerSwitch.Add("off", fine.Transitions{"smash": "broken"})
fmt.Println("Adding \"off\" failed?", err != nil)
powerSwitch.Do("toggle")
fmt.Println("Current FSM state:", powerSwitch.State())
}
Output: Adding "off" failed? true Current FSM state: on
func (*FSM) AddOrMerge ¶
func (m *FSM) AddOrMerge(state string, transitions Transitions)
AddOrMerge allows to add a new state with its associated transitions. If a state with the same name is already present in the FSM, its transitions will be merged, keeping the newer ones in case of collisions.
Example ¶
package main
import (
"fmt"
"interrato.dev/fine"
)
func main() {
powerSwitch := fine.Machine("off", fine.States{
"off": {"toggle": "on"},
})
// Here I add the "on" state. No difference with Add() here.
powerSwitch.AddOrMerge("on", fine.Transitions{"toggle": "off"})
// Here I try to add the "off" state, which is already in the FSM, and the
// new transitions are merged to the old ones.
powerSwitch.AddOrMerge("off", fine.Transitions{"smash": "broken"})
// So I can still toggle it.
powerSwitch.Do("toggle")
fmt.Println("Current FSM state:", powerSwitch.State())
// And toggle it again...
powerSwitch.Do("toggle")
fmt.Println("Current FSM state:", powerSwitch.State())
// ...and now I smash it.
powerSwitch.Do("smash")
fmt.Println("Current FSM state:", powerSwitch.State())
}
Output: Current FSM state: on Current FSM state: off Current FSM state: broken
func (*FSM) AddOrReplace ¶
func (m *FSM) AddOrReplace(state string, transitions Transitions)
AddOrReplace allows to add a new state with its associated transitions. If a state with the same name is already present in the FSM, its transitions will be completely overwritten.
Example ¶
package main
import (
"fmt"
"interrato.dev/fine"
)
func main() {
powerSwitch := fine.Machine("off", fine.States{
"off": {"toggle": "on"},
})
// Here I add the "on" state. No difference with Add() here.
powerSwitch.AddOrReplace("on", fine.Transitions{"toggle": "off"})
// Here I try to add the "off" state, but, because it's already in the FSM,
// its transitions are now replaced.
powerSwitch.AddOrReplace("off", fine.Transitions{"smash": "broken"})
// The "toggle" event for the "off" state does not exist anymore, so
// nothing changes even if I try to invoke "toggle" many times.
powerSwitch.Do("toggle")
fmt.Println("Current FSM state:", powerSwitch.State())
powerSwitch.Do("toggle")
fmt.Println("Current FSM state still", powerSwitch.State())
powerSwitch.Do("toggle")
fmt.Println("Current FSM state is", powerSwitch.State(), "again")
// So, let's break this switch.
powerSwitch.Do("smash")
fmt.Println("Current FSM state:", powerSwitch.State())
}
Output: Current FSM state: off Current FSM state still off Current FSM state is off again Current FSM state: broken
func (*FSM) Do ¶
Do executes the specified action on the FSM from the current state.
The action parameter specifies the event, that is, the action name.
It is possible to pass arguments to the action. If the action isn't a function or does not accept any parameter, the arguments will be ignored.
Note: lifecycle actions cannot be manually executed.
Example ¶
package main
import (
"fmt"
"interrato.dev/fine"
)
func main() {
powerSwitch := fine.Machine("off", fine.States{
"off": {"toggle": "on"},
"on": {"toggle": "off"},
})
fmt.Println("Current FSM state:", powerSwitch.State())
fmt.Println("Toggling...")
powerSwitch.Do("toggle")
fmt.Println("Current FSM state:", powerSwitch.State())
}
Output: Current FSM state: off Toggling... Current FSM state: on
Example (Lifecycle) ¶
package main
import (
"fmt"
"interrato.dev/fine"
)
func main() {
powerSwitch := fine.Machine("off", fine.States{
"off": {
"@exit": func(metadata fine.Metadata) {
fmt.Println("[INFO] from:", metadata.From)
fmt.Println("[INFO] to:", metadata.To)
fmt.Println("[INFO] event:", metadata.Event)
fmt.Println("[INFO] args:", metadata.Args)
},
"toggle": func(args ...interface{}) string {
message := args[0].(string)
fmt.Printf("message: %q\n", message)
return "on"
},
},
"on": {
"@enter": func() {
fmt.Println("Finally, light!")
},
"toggle": "off",
},
})
fmt.Println("Current FSM state:", powerSwitch.State())
fmt.Println("Toggling...")
powerSwitch.Do("toggle", "Shine, step into the light")
fmt.Println("Current FSM state:", powerSwitch.State())
}
Output: Current FSM state: off Toggling... message: "Shine, step into the light" [INFO] from: off [INFO] to: on [INFO] event: toggle [INFO] args: [Shine, step into the light] Finally, light! Current FSM state: on
func (*FSM) States ¶
States returns a slice with all the possible states of the FSM.
Note: the order is not guaranteed.
func (*FSM) Subscribe ¶
Subscribe allows subscribing to state changes with a callback function. The callback function will be executed every time the state changes and receives the new state as a parameter. The callback function also runs when subscribing and will receive the current state.
An unsubscribe function is returned.
Example ¶
package main
import (
"fmt"
"interrato.dev/fine"
)
func main() {
powerSwitch := fine.Machine("off", fine.States{
"off": {"toggle": "on"},
"on": {"toggle": "off"},
})
onSubscribe := true
unsubscribe := powerSwitch.Subscribe(func(state string) {
if onSubscribe {
fmt.Printf("Subscribed with state set to %q\n", state)
onSubscribe = false
} else {
fmt.Printf("State just changed to %q\n", state)
}
})
powerSwitch.Do("toggle")
powerSwitch.Do("toggle")
powerSwitch.Do("toggle")
unsubscribe()
powerSwitch.Do("toggle")
fmt.Println("Current FSM state:", powerSwitch.State())
}
Output: Subscribed with state set to "off" State just changed to "on" State just changed to "off" State just changed to "on" Current FSM state: off
type Metadata ¶
type Metadata struct {
// The previous state from which the transition started.
From string
// The new state where the transition will end.
To string
// The name of the action that caused the system state change.
Event string
// The arguments that were passed to the action.
Args []interface{}
}
Metadata holds the information about a transition that changed the system state.
type States ¶
type States map[string]Transitions
States are mappings from states to Transitions.
A state has type string.
type Transitions ¶
type Transitions map[string]interface{}
Transitions is a mapping between events (or names of actions) and actions.
An action can have one of the following types, or nil.
string
func() string
func(args ...interface{}) string
func()
func(args ...interface{})
Trying to call an action that has a different type will panic.
There are two special lifecycle functions, named "@enter" and "@exit", executed on entering and exiting a state, respectively. It is not possible to pass custom parameters to these functions. They receive an optional Metadata object and an optional pointer to the FSM itself. Thus, the possible types for lifecycle actions are the following, or nil.
func() func(this *fine.FSM) func(metadata fine.Metadata) func(this *fine.FSM, metadata fine.Metadata)
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
blinking-led
command
|
|
|
counter-switch
command
|
|
|
simple-switch
command
|
|
|
traffic-light
command
|
|
|
turnstile
command
|
