2020-07-28 07:52:05 -07:00
|
|
|
package fsm
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2020-07-29 14:49:41 -07:00
|
|
|
"fmt"
|
|
|
|
"strings"
|
2020-07-28 07:52:05 -07:00
|
|
|
"sync"
|
|
|
|
)
|
|
|
|
|
|
|
|
//
|
|
|
|
// fsmInstance, err := fsm.New(scope)
|
|
|
|
// if err != nil {
|
|
|
|
// log.Println(err)
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// fsmInstance.Do(event, args)
|
|
|
|
//
|
|
|
|
|
|
|
|
// Temporary global finish state for deprecating operations
|
|
|
|
const (
|
2020-07-31 04:25:52 -07:00
|
|
|
StateGlobalIdle = State("__idle")
|
|
|
|
StateGlobalDone = State("__done")
|
2020-08-11 09:46:18 -07:00
|
|
|
)
|
2020-08-09 05:02:50 -07:00
|
|
|
|
2020-08-11 09:46:18 -07:00
|
|
|
const (
|
2020-08-09 05:02:50 -07:00
|
|
|
EventRunDefault EventRunMode = iota
|
|
|
|
EventRunBefore
|
|
|
|
EventRunAfter
|
2020-07-28 07:52:05 -07:00
|
|
|
)
|
|
|
|
|
2020-07-31 04:25:52 -07:00
|
|
|
type State string
|
|
|
|
|
|
|
|
func (s *State) String() string {
|
|
|
|
return string(*s)
|
|
|
|
}
|
|
|
|
|
|
|
|
type Event string
|
|
|
|
|
|
|
|
func (e *Event) String() string {
|
|
|
|
return string(*e)
|
|
|
|
}
|
|
|
|
|
2020-08-13 08:22:06 -07:00
|
|
|
func (e Event) IsEmpty() bool {
|
2020-08-08 14:44:52 -07:00
|
|
|
return e.String() == ""
|
|
|
|
}
|
|
|
|
|
2020-08-09 05:02:50 -07:00
|
|
|
type EventRunMode uint8
|
|
|
|
|
2020-07-31 04:25:52 -07:00
|
|
|
// Response returns result for processing with clientMocks events
|
|
|
|
type Response struct {
|
2020-07-28 07:52:05 -07:00
|
|
|
// Returns machine execution result state
|
2020-07-31 04:25:52 -07:00
|
|
|
State State
|
2020-07-28 07:52:05 -07:00
|
|
|
// Must be cast, according to mapper event_name->response_type
|
|
|
|
Data interface{}
|
|
|
|
}
|
|
|
|
|
|
|
|
type FSM struct {
|
|
|
|
name string
|
2020-07-31 04:25:52 -07:00
|
|
|
initialState State
|
|
|
|
currentState State
|
2020-07-28 07:52:05 -07:00
|
|
|
|
|
|
|
// May be mapping must require pair source + event?
|
|
|
|
transitions map[trKey]*trEvent
|
|
|
|
|
2020-08-13 08:22:06 -07:00
|
|
|
autoTransitions map[trAutoKeyEvent]*trEvent
|
2020-08-09 05:02:50 -07:00
|
|
|
|
2020-07-28 07:52:05 -07:00
|
|
|
callbacks Callbacks
|
|
|
|
|
2020-07-31 04:25:52 -07:00
|
|
|
initialEvent Event
|
2020-07-28 07:52:05 -07:00
|
|
|
|
|
|
|
// Finish states, for switch machine or fin,
|
|
|
|
// These states cannot be linked as SrcState in this machine
|
2020-07-31 04:25:52 -07:00
|
|
|
finStates map[State]bool
|
2020-07-28 07:52:05 -07:00
|
|
|
|
|
|
|
// stateMu guards access to the currentState state.
|
|
|
|
stateMu sync.RWMutex
|
|
|
|
}
|
|
|
|
|
|
|
|
// Transition key source + dst
|
|
|
|
type trKey struct {
|
2020-07-31 04:25:52 -07:00
|
|
|
source State
|
|
|
|
event Event
|
2020-07-28 07:52:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Transition lightweight event description
|
|
|
|
type trEvent struct {
|
2020-08-09 05:02:50 -07:00
|
|
|
event Event
|
|
|
|
dstState State
|
|
|
|
isInternal bool
|
|
|
|
isAuto bool
|
|
|
|
runMode EventRunMode
|
2020-07-28 07:52:05 -07:00
|
|
|
}
|
|
|
|
|
2020-08-13 08:22:06 -07:00
|
|
|
type trAutoKeyEvent struct {
|
|
|
|
state State
|
|
|
|
runMode EventRunMode
|
|
|
|
}
|
|
|
|
|
2020-07-31 04:25:52 -07:00
|
|
|
type EventDesc struct {
|
|
|
|
Name Event
|
2020-07-28 07:52:05 -07:00
|
|
|
|
2020-07-31 04:25:52 -07:00
|
|
|
SrcState []State
|
2020-07-28 07:52:05 -07:00
|
|
|
|
|
|
|
// Dst state changes after callback
|
2020-07-31 04:25:52 -07:00
|
|
|
DstState State
|
2020-07-28 07:52:05 -07:00
|
|
|
|
|
|
|
// Internal events, cannot be emitted from external call
|
|
|
|
IsInternal bool
|
2020-08-06 17:20:13 -07:00
|
|
|
|
2020-08-09 05:02:50 -07:00
|
|
|
// Event must run without manual call
|
|
|
|
IsAuto bool
|
|
|
|
|
|
|
|
AutoRunMode EventRunMode
|
2020-07-28 07:52:05 -07:00
|
|
|
}
|
|
|
|
|
2020-08-08 14:44:52 -07:00
|
|
|
type Callback func(event Event, args ...interface{}) (Event, interface{}, error)
|
2020-07-28 07:52:05 -07:00
|
|
|
|
2020-07-31 04:25:52 -07:00
|
|
|
type Callbacks map[Event]Callback
|
2020-07-28 07:52:05 -07:00
|
|
|
|
2020-07-31 04:25:52 -07:00
|
|
|
func MustNewFSM(machineName string, initialState State, events []EventDesc, callbacks Callbacks) *FSM {
|
2020-07-29 14:49:41 -07:00
|
|
|
machineName = strings.TrimSpace(machineName)
|
2020-07-31 04:25:52 -07:00
|
|
|
initialState = State(strings.TrimSpace(initialState.String()))
|
2020-07-28 07:52:05 -07:00
|
|
|
|
2020-07-29 14:49:41 -07:00
|
|
|
if machineName == "" {
|
|
|
|
panic("machine name cannot be empty")
|
2020-07-28 07:52:05 -07:00
|
|
|
}
|
|
|
|
|
2020-07-29 14:49:41 -07:00
|
|
|
if initialState == "" {
|
|
|
|
panic("initial state state cannot be empty")
|
2020-07-28 07:52:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// to remove
|
|
|
|
if len(events) == 0 {
|
|
|
|
panic("cannot init fsm with empty events")
|
|
|
|
}
|
|
|
|
|
|
|
|
f := &FSM{
|
2020-08-09 05:02:50 -07:00
|
|
|
name: machineName,
|
|
|
|
currentState: initialState,
|
|
|
|
initialState: initialState,
|
|
|
|
transitions: make(map[trKey]*trEvent),
|
2020-08-13 08:22:06 -07:00
|
|
|
autoTransitions: make(map[trAutoKeyEvent]*trEvent),
|
2020-08-09 05:02:50 -07:00
|
|
|
finStates: make(map[State]bool),
|
|
|
|
callbacks: make(map[Event]Callback),
|
2020-07-28 07:52:05 -07:00
|
|
|
}
|
|
|
|
|
2020-07-31 04:25:52 -07:00
|
|
|
allEvents := make(map[Event]bool)
|
2020-07-28 07:52:05 -07:00
|
|
|
|
|
|
|
// Required for find finStates
|
2020-07-31 04:25:52 -07:00
|
|
|
allSources := make(map[State]bool)
|
|
|
|
allStates := make(map[State]bool)
|
2020-07-28 07:52:05 -07:00
|
|
|
|
|
|
|
// Validate events
|
|
|
|
for _, event := range events {
|
2020-07-31 04:25:52 -07:00
|
|
|
event.Name = Event(strings.TrimSpace(event.Name.String()))
|
|
|
|
event.DstState = State(strings.TrimSpace(event.DstState.String()))
|
2020-07-28 07:52:05 -07:00
|
|
|
|
|
|
|
if event.Name == "" {
|
|
|
|
panic("cannot init empty event")
|
|
|
|
}
|
|
|
|
|
|
|
|
if event.DstState == "" {
|
|
|
|
panic("event dest cannot be empty, use StateGlobalDone for finish or external state")
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := allEvents[event.Name]; ok {
|
2020-07-29 14:49:41 -07:00
|
|
|
panic(fmt.Sprintf("duplicate event \"%s\"", event.Name))
|
2020-07-28 07:52:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
allEvents[event.Name] = true
|
|
|
|
allStates[event.DstState] = true
|
|
|
|
|
2020-07-29 14:49:41 -07:00
|
|
|
trimmedSourcesCounter := 0
|
|
|
|
|
2020-07-28 07:52:05 -07:00
|
|
|
for _, sourceState := range event.SrcState {
|
2020-07-31 04:25:52 -07:00
|
|
|
sourceState := State(strings.TrimSpace(sourceState.String()))
|
2020-07-29 14:49:41 -07:00
|
|
|
|
|
|
|
if sourceState == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-07-28 07:52:05 -07:00
|
|
|
tKey := trKey{
|
|
|
|
sourceState,
|
|
|
|
event.Name,
|
|
|
|
}
|
|
|
|
|
|
|
|
if sourceState == StateGlobalDone {
|
|
|
|
panic("StateGlobalDone cannot set as source state")
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := f.transitions[tKey]; ok {
|
|
|
|
panic("duplicate dst for pair `source + event`")
|
|
|
|
}
|
|
|
|
|
2020-08-11 09:46:18 -07:00
|
|
|
if event.IsAuto && event.AutoRunMode == EventRunDefault {
|
|
|
|
event.AutoRunMode = EventRunAfter
|
|
|
|
}
|
|
|
|
|
2020-08-09 05:02:50 -07:00
|
|
|
trEvent := &trEvent{
|
2020-08-06 17:20:13 -07:00
|
|
|
tKey.event,
|
|
|
|
event.DstState,
|
|
|
|
event.IsInternal,
|
2020-08-09 05:02:50 -07:00
|
|
|
event.IsAuto,
|
|
|
|
event.AutoRunMode,
|
2020-08-06 17:20:13 -07:00
|
|
|
}
|
2020-07-28 07:52:05 -07:00
|
|
|
|
2020-08-09 05:02:50 -07:00
|
|
|
f.transitions[tKey] = trEvent
|
|
|
|
|
2020-07-28 07:52:05 -07:00
|
|
|
// For using provider, event must use with IsGlobal = true
|
2020-07-29 14:49:41 -07:00
|
|
|
if sourceState == initialState {
|
2020-08-09 05:02:50 -07:00
|
|
|
if f.initialEvent == "" {
|
|
|
|
f.initialEvent = event.Name
|
2020-07-28 07:52:05 -07:00
|
|
|
}
|
2020-08-09 05:02:50 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if event.IsAuto {
|
|
|
|
if event.AutoRunMode != EventRunBefore && event.AutoRunMode != EventRunAfter {
|
|
|
|
panic("{AutoRunMode} not set for auto event")
|
|
|
|
}
|
|
|
|
|
2020-08-13 08:22:06 -07:00
|
|
|
trAutoKey := trAutoKeyEvent{sourceState, event.AutoRunMode}
|
|
|
|
if _, ok := f.autoTransitions[trAutoKey]; ok {
|
2020-08-09 05:02:50 -07:00
|
|
|
panic(fmt.Sprintf(
|
|
|
|
"auto event \"%s\" already exists for state \"%s\"",
|
|
|
|
event.Name,
|
|
|
|
sourceState,
|
|
|
|
))
|
|
|
|
}
|
2020-08-13 08:22:06 -07:00
|
|
|
f.autoTransitions[trAutoKey] = trEvent
|
2020-07-28 07:52:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
allSources[sourceState] = true
|
2020-07-29 14:49:41 -07:00
|
|
|
trimmedSourcesCounter++
|
|
|
|
}
|
|
|
|
|
|
|
|
if trimmedSourcesCounter == 0 {
|
|
|
|
panic("event must have minimum one source available state")
|
2020-07-28 07:52:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(allStates) < 2 {
|
|
|
|
panic("machine must contain at least two states")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validate callbacks
|
|
|
|
for event, callback := range callbacks {
|
|
|
|
if event == "" {
|
2020-07-29 14:49:41 -07:00
|
|
|
panic("callback machineName cannot be empty")
|
2020-07-28 07:52:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := allEvents[event]; !ok {
|
2020-08-13 08:22:06 -07:00
|
|
|
panic(fmt.Sprintf("callback has unused event \"%s\"", event))
|
2020-07-28 07:52:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
f.callbacks[event] = callback
|
|
|
|
}
|
|
|
|
|
|
|
|
for state := range allStates {
|
|
|
|
if state == StateGlobalIdle {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// Exit states cannot be a source in this machine
|
|
|
|
if _, exists := allSources[state]; !exists || state == StateGlobalDone {
|
|
|
|
f.finStates[state] = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(f.finStates) == 0 {
|
|
|
|
panic("cannot initialize machine without final states")
|
|
|
|
}
|
|
|
|
|
|
|
|
return f
|
|
|
|
}
|
|
|
|
|
2020-08-21 06:56:03 -07:00
|
|
|
// WithState returns FSM copy with custom setup state
|
2020-08-21 07:29:52 -07:00
|
|
|
func (f *FSM) MustCopyWithState(state State) *FSM {
|
|
|
|
var exists bool
|
|
|
|
|
2020-08-21 06:56:03 -07:00
|
|
|
f.stateMu.RLock()
|
|
|
|
defer f.stateMu.RUnlock()
|
|
|
|
|
|
|
|
if state != "" {
|
2020-08-21 07:29:52 -07:00
|
|
|
for _, s := range f.StatesList() {
|
|
|
|
if s == state {
|
|
|
|
exists = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !exists {
|
|
|
|
panic(fmt.Sprintf("cannot set state, not exists \"%s\" for \"%s\"", state, f.name))
|
|
|
|
}
|
2020-08-21 06:56:03 -07:00
|
|
|
f.currentState = state
|
|
|
|
}
|
2020-08-21 07:29:52 -07:00
|
|
|
return f
|
2020-08-21 06:56:03 -07:00
|
|
|
}
|
|
|
|
|
2020-08-06 17:20:13 -07:00
|
|
|
func (f *FSM) DoInternal(event Event, args ...interface{}) (resp *Response, err error) {
|
|
|
|
trEvent, ok := f.transitions[trKey{f.currentState, event}]
|
|
|
|
if !ok {
|
2020-09-29 07:31:31 -07:00
|
|
|
return nil, fmt.Errorf("cannot execute internal event \"%s\" for state \"%s\"",
|
|
|
|
event, f.currentState)
|
2020-08-06 17:20:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return f.do(trEvent, args...)
|
|
|
|
}
|
2020-07-28 07:52:05 -07:00
|
|
|
|
2020-08-06 17:20:13 -07:00
|
|
|
func (f *FSM) Do(event Event, args ...interface{}) (resp *Response, err error) {
|
2020-07-28 07:52:05 -07:00
|
|
|
trEvent, ok := f.transitions[trKey{f.currentState, event}]
|
|
|
|
if !ok {
|
2020-09-29 07:31:31 -07:00
|
|
|
return nil, fmt.Errorf("cannot execute event \"%s\" for state \"%s\"", event, f.currentState)
|
2020-07-28 07:52:05 -07:00
|
|
|
}
|
|
|
|
if trEvent.isInternal {
|
|
|
|
return nil, errors.New("event is internal")
|
|
|
|
}
|
|
|
|
|
2020-08-06 17:20:13 -07:00
|
|
|
return f.do(trEvent, args...)
|
|
|
|
}
|
2020-08-13 08:22:06 -07:00
|
|
|
|
|
|
|
// Check and execute auto event
|
|
|
|
func (f *FSM) processAutoEvent(mode EventRunMode, args ...interface{}) (exists bool, outEvent Event, response interface{}, err error) {
|
|
|
|
autoEvent, exists := f.autoTransitions[trAutoKeyEvent{f.State(), mode}]
|
|
|
|
if !exists {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if f.isCallbackExists(autoEvent.event) {
|
|
|
|
outEvent, response, err = f.execCallback(autoEvent.event, args...)
|
|
|
|
// Do not try change state on error
|
|
|
|
if err != nil {
|
|
|
|
return exists, "", response, err
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if outEvent.IsEmpty() || autoEvent.event == outEvent {
|
|
|
|
err = f.SetState(autoEvent.event)
|
|
|
|
} else {
|
|
|
|
err = f.SetState(outEvent)
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-08-06 17:20:13 -07:00
|
|
|
func (f *FSM) do(trEvent *trEvent, args ...interface{}) (resp *Response, err error) {
|
2020-08-08 14:44:52 -07:00
|
|
|
var outEvent Event
|
2020-08-06 17:20:13 -07:00
|
|
|
// f.eventMu.Lock()
|
|
|
|
// defer f.eventMu.Unlock()
|
|
|
|
|
2020-08-13 08:22:06 -07:00
|
|
|
resp = &Response{}
|
|
|
|
|
2020-08-09 05:02:50 -07:00
|
|
|
// Process auto event
|
2020-08-13 08:22:06 -07:00
|
|
|
isAutoEventExecuted, outEvent, data, err := f.processAutoEvent(EventRunBefore, args...)
|
|
|
|
|
|
|
|
if isAutoEventExecuted {
|
|
|
|
resp.State = f.State()
|
|
|
|
if data != nil {
|
|
|
|
resp.Data = data
|
2020-08-06 17:20:13 -07:00
|
|
|
}
|
|
|
|
|
2020-08-13 08:22:06 -07:00
|
|
|
if err != nil {
|
|
|
|
return resp, err
|
|
|
|
}
|
2020-07-28 07:52:05 -07:00
|
|
|
}
|
|
|
|
|
2020-08-13 08:22:06 -07:00
|
|
|
if f.isCallbackExists(trEvent.event) {
|
|
|
|
outEvent, resp.Data, err = f.execCallback(trEvent.event, args...)
|
2020-07-28 07:52:05 -07:00
|
|
|
// Do not try change state on error
|
|
|
|
if err != nil {
|
|
|
|
return resp, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-09 05:02:50 -07:00
|
|
|
// Set state when callback executed
|
|
|
|
if outEvent.IsEmpty() || trEvent.event == outEvent {
|
|
|
|
err = f.SetState(trEvent.event)
|
2020-08-13 08:22:06 -07:00
|
|
|
if err != nil {
|
|
|
|
return resp, err
|
|
|
|
}
|
2020-08-09 05:02:50 -07:00
|
|
|
} else {
|
|
|
|
err = f.SetState(outEvent)
|
2020-08-13 08:22:06 -07:00
|
|
|
if err != nil {
|
|
|
|
return resp, err
|
|
|
|
}
|
2020-08-09 05:02:50 -07:00
|
|
|
}
|
2020-08-08 14:44:52 -07:00
|
|
|
|
2020-08-13 08:22:06 -07:00
|
|
|
resp.State = f.State()
|
|
|
|
|
2020-08-09 05:02:50 -07:00
|
|
|
// Process auto event
|
2020-09-29 08:05:40 -07:00
|
|
|
isAutoEventExecuted, _, data, err = f.processAutoEvent(EventRunAfter, args...)
|
2020-08-13 08:22:06 -07:00
|
|
|
|
|
|
|
if isAutoEventExecuted {
|
|
|
|
resp.State = f.State()
|
|
|
|
if data != nil {
|
|
|
|
resp.Data = data
|
2020-08-09 05:02:50 -07:00
|
|
|
}
|
2020-08-13 08:22:06 -07:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return resp, err
|
2020-08-09 05:02:50 -07:00
|
|
|
}
|
2020-08-05 06:41:19 -07:00
|
|
|
}
|
2020-08-06 17:20:13 -07:00
|
|
|
|
2020-08-13 16:35:52 -07:00
|
|
|
resp.State = f.State()
|
|
|
|
|
2020-07-28 07:52:05 -07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// State returns the currentState state of the FSM.
|
2020-07-31 04:25:52 -07:00
|
|
|
func (f *FSM) State() State {
|
2020-07-28 07:52:05 -07:00
|
|
|
f.stateMu.RLock()
|
|
|
|
defer f.stateMu.RUnlock()
|
|
|
|
return f.currentState
|
|
|
|
}
|
|
|
|
|
2020-08-06 17:20:13 -07:00
|
|
|
// SetState allows the user to move to the given state from currentState state.
|
2020-07-28 07:52:05 -07:00
|
|
|
// The call does not trigger any callbacks, if defined.
|
2020-08-06 17:20:13 -07:00
|
|
|
func (f *FSM) SetState(event Event) error {
|
2020-07-28 07:52:05 -07:00
|
|
|
f.stateMu.Lock()
|
|
|
|
defer f.stateMu.Unlock()
|
|
|
|
|
|
|
|
trEvent, ok := f.transitions[trKey{f.currentState, event}]
|
|
|
|
if !ok {
|
2020-09-29 08:05:40 -07:00
|
|
|
return fmt.Errorf("cannot set state by event \"%s\" for state \"%s\"", event, f.currentState)
|
2020-07-28 07:52:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
f.currentState = trEvent.dstState
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *FSM) Name() string {
|
|
|
|
return f.name
|
|
|
|
}
|
|
|
|
|
2020-07-31 04:25:52 -07:00
|
|
|
func (f *FSM) InitialState() State {
|
2020-07-28 07:52:05 -07:00
|
|
|
return f.initialState
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check entry event for available emitting as global entry event
|
2020-07-31 04:25:52 -07:00
|
|
|
func (f *FSM) GlobalInitialEvent() (event Event) {
|
2020-07-28 07:52:05 -07:00
|
|
|
if initialEvent, exists := f.transitions[trKey{StateGlobalIdle, f.initialEvent}]; exists {
|
|
|
|
if !initialEvent.isInternal {
|
|
|
|
event = f.initialEvent
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-07-31 04:25:52 -07:00
|
|
|
func (f *FSM) EntryEvent() (event Event) {
|
2020-07-28 07:52:05 -07:00
|
|
|
if entryEvent, exists := f.transitions[trKey{f.initialState, f.initialEvent}]; exists {
|
|
|
|
if !entryEvent.isInternal {
|
|
|
|
event = f.initialEvent
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-07-31 04:25:52 -07:00
|
|
|
func (f *FSM) EventsList() (events []Event) {
|
|
|
|
var eventsMap = map[Event]bool{}
|
2020-07-28 07:52:05 -07:00
|
|
|
if len(f.transitions) > 0 {
|
|
|
|
for trKey, trEvent := range f.transitions {
|
|
|
|
if !trEvent.isInternal {
|
2020-07-29 14:49:41 -07:00
|
|
|
eventsMap[trKey.event] = true
|
|
|
|
if _, exists := eventsMap[trKey.event]; !exists {
|
|
|
|
|
|
|
|
events = append(events, trKey.event)
|
|
|
|
}
|
2020-07-28 07:52:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-07-29 14:49:41 -07:00
|
|
|
|
|
|
|
if len(eventsMap) > 0 {
|
|
|
|
for event := range eventsMap {
|
|
|
|
events = append(events, event)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-28 07:52:05 -07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-08-21 07:29:52 -07:00
|
|
|
func (f *FSM) StatesList() (states []State) {
|
2020-07-31 04:25:52 -07:00
|
|
|
var allStates = map[State]bool{}
|
2020-07-28 07:52:05 -07:00
|
|
|
if len(f.transitions) > 0 {
|
2020-09-29 08:05:40 -07:00
|
|
|
for trKey := range f.transitions {
|
2020-07-28 07:52:05 -07:00
|
|
|
allStates[trKey.source] = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(allStates) > 0 {
|
|
|
|
for state := range allStates {
|
|
|
|
states = append(states, state)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-08-13 08:22:06 -07:00
|
|
|
func (f *FSM) isCallbackExists(event Event) bool {
|
|
|
|
_, exists := f.callbacks[event]
|
|
|
|
return exists
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *FSM) execCallback(event Event, args ...interface{}) (Event, interface{}, error) {
|
2020-09-29 08:05:40 -07:00
|
|
|
callback := f.callbacks[event]
|
2020-08-13 08:22:06 -07:00
|
|
|
return callback(event, args...)
|
|
|
|
}
|
|
|
|
|
2020-07-31 04:25:52 -07:00
|
|
|
func (f *FSM) IsFinState(state State) bool {
|
2020-07-28 07:52:05 -07:00
|
|
|
_, exists := f.finStates[state]
|
|
|
|
return exists
|
|
|
|
}
|