package fsm import ( "fmt" "log" "time" "git.zhangshuocauc.cn/redhat/timewheel" ) type EventName string const ( EventEntry EventName = "EventEntry" EventExit EventName = "EventExit" EventTimeOut EventName = "EventTimeOut" EventOK EventName = "EventOK" EventNoProc EventName = "EventNoProc" ) type Callback func(fsm *FSM, event EventName, arg interface{}) EventName type Callbacks map[string]Callback type FSMState struct { name string parent string initState string processor Callback } type FSM struct { name string currentState *FSMState eventNames *[]string fsmStats map[string]*FSMState timer *timewheel.TimeWheel } type Event struct { FSM *FSM Event string Src string Dst string Err error Args []interface{} canceled bool async bool cancelFunc func() } func NewFSMState(name string, parent, initState string, processor Callback) *FSMState { return &FSMState{ name: name, parent: parent, initState: initState, processor: processor, } } func NewFSM(name string, eventNames *[]string, fsmStats ...*FSMState) (*FSM, error) { if len(fsmStats) == 0 { return nil, fmt.Errorf("initial state cannot be nil") } fsm := &FSM{ name: name, currentState: fsmStats[0], eventNames: eventNames, fsmStats: make(map[string]*FSMState), timer: timewheel.New(1, time.Millisecond*100), } for index := range fsmStats { fsm.fsmStats[fsmStats[index].name] = fsmStats[index] } // Execute ENTRY event for initial state and its sub-states for tempState := fsm.currentState; tempState != nil; tempState = fsm.fsmStats[tempState.initState] { fsm.currentState = tempState if err := fsm.ExecuteEvent(EventEntry, nil); err != nil { return nil, err } } return fsm, nil } func (fsm *FSM) StartTimer(typeT timewheel.TimeMode, cycle time.Duration, arg interface{}) { fsm.timer.AddTask(fsm.currentState.name, typeT, func() { fsm.ExecuteEvent(EventTimeOut, arg) }, time.Now().Add(cycle)) } func (fsm *FSM) StopTimer() { fsm.timer.RemoveTask(fsm.currentState.name) } func (fsm *FSM) ExecuteEvent(event EventName, arg interface{}) error { if fsm == nil { return fmt.Errorf("FSM is nil") } log.Printf("%d %-23s STATE:%-23s EVENT:%s\n", time.Now().UnixMilli(), fsm.name, fsm.currentState.name, event) tmpState := fsm.currentState if tmpState == nil { return fmt.Errorf("current state is nil") } result := tmpState.processor(fsm, event, arg) for result == EventNoProc && fsm.fsmStats[tmpState.parent] != nil { tmpState = fsm.fsmStats[tmpState.parent] result = tmpState.processor(fsm, event, arg) } return nil } func (fsm *FSM) ChangeState(newState string, arg interface{}) error { if newState == "" { return fmt.Errorf("new state cannot be nil") } curState := fsm.currentState for tempState := curState; tempState != nil; tempState = fsm.fsmStats[tempState.parent] { fsm.currentState = tempState if err := fsm.ExecuteEvent(EventExit, nil); err != nil { return err } fsm.StopTimer() if newState == tempState.name { if err := fsm.ExecuteEvent(EventEntry, arg); err != nil { return err } break } if found, ok := fsm.findState(fsm.fsmStats[newState], tempState); ok { for end := len(found) - 1; end >= 0; end-- { fsm.currentState = found[end] if err := fsm.ExecuteEvent(EventEntry, arg); err != nil { return err } } break } } // Enter sub-states of the new state for tempState := fsm.fsmStats[newState].initState; fsm.fsmStats[tempState] != nil; tempState = fsm.fsmStats[tempState].initState { fsm.currentState = fsm.fsmStats[tempState] if err := fsm.ExecuteEvent(EventEntry, arg); err != nil { return err } } return nil } func (fsm *FSM) findState(targetState, findState *FSMState) ([]*FSMState, bool) { var fsmStateList []*FSMState for tempState := targetState; tempState != nil; tempState = fsm.fsmStats[tempState.parent] { fsmStateList = append(fsmStateList, tempState) if findState.parent == tempState.parent { return fsmStateList, true } } return fsmStateList, false }