关于erlang的有限状态机,参考erlang四大behaviour之二-gen_fsm这篇文章。
有限状态机可以用下面这个公式来表达
State(S) x Event(E) -> Actions(A), State(S')
这两天正好空闲,就用golang实现了一下,话不多说,直接上代码
package util
import (
"errors"
"reflect"
"sync"
"time"
"unicode"
"unicode/utf8"
"util/log"
)
var typeOfError = reflect.TypeOf((*error)(nil)).Elem()
type FSM struct {
sync.Mutex
StopReason string
rcvr reflect.Value // receiver of methods for the service
typ reflect.Type // type of the receiver
method map[string]reflect.Method
event chan Event
quit chan int
state string
stopped bool
}
type Event struct {
event string
param interface{}
timeout int
}
func (fsm *FSM) IsStopped() bool {
fsm.Lock()
defer fsm.Unlock()
return fsm.stopped
}
func (fsm *FSM) SendEvent(event string, param interface{}) {
fsm.Lock()
defer fsm.Unlock()
if fsm.stopped {
return
}
fsm.event <- Event{event, param, 0}
}
func (fsm *FSM) Init(start string) error {
if _, ok := fsm.method[start]; !ok {
return errors.New("not found state")
}
fsm.state = start
go func() {
for {
select {
case e := <-fsm.event:
go fsm.CallState(e)
case <-fsm.quit:
goto close
}
}
close:
close(fsm.event)
close(fsm.quit)
}()
return nil
}
func (fsm *FSM) CallState(e Event) {
fsm.Lock()
defer fsm.Unlock()
if function, ok := fsm.method[fsm.state]; ok {
returnValues := function.Func.Call([]reflect.Value{fsm.rcvr, reflect.ValueOf(e.event), reflect.ValueOf(e.param), reflect.ValueOf(e.timeout)})
nextstate := returnValues[0].String()
timeout := returnValues[1].Int()
errInter := returnValues[2].Interface()
errmsg := ""
if errInter != nil {
errmsg = errInter.(error).Error()
}
if nextstate == "stop" {
fsm.Stop(errmsg)
fsm.quit <- 1
return
}
if errmsg != "" {
log.LogError(errmsg)
}
fsm.state = nextstate
if timeout > 0 {
go fsm.DelayCall(time.Duration(timeout))
}
}
}
func (fsm *FSM) DelayCall(timeout time.Duration) {
select {
case <-time.After(timeout * time.Millisecond):
fsm.event <- Event{"timeout", 0, int(timeout)}
}
}
func (fsm *FSM) Stop(message string) {
fsm.StopReason = message
fsm.stopped = true
}
func (fsm *FSM) Close() {
fsm.Lock()
defer fsm.Unlock()
if fsm.stopped {
return
}
fsm.quit <- 1
}
func NewFSM(fsm interface{}) *FSM {
f := &FSM{typ: reflect.TypeOf(fsm), rcvr: reflect.ValueOf(fsm), event: make(chan Event), quit: make(chan int)}
f.method = suitableMethods(f.typ, true)
return f
}
func isExported(name string) bool {
rune, _ := utf8.DecodeRuneInString(name)
return unicode.IsUpper(rune)
}
func suitableMethods(typ reflect.Type, reportErr bool) map[string]reflect.Method {
methods := make(map[string]reflect.Method)
for m := 0; m < typ.NumMethod(); m++ {
method := typ.Method(m)
mtype := method.Type
mname := method.Name
if !isExported(mname) {
continue
}
// Method needs four ins: receiver, string, interface{}, int.
if mtype.NumIn() != 4 {
if reportErr {
log.LogError("method", mname, "has wrong number of ins:", mtype.NumIn())
}
continue
}
// First arg must be a string.
if mtype.In(1).Kind() != reflect.String {
if reportErr {
log.LogError("method", mname, "arg1 type not a string:", mtype.In(1).Kind())
}
continue
}
// Second arg must be a interface.
if mtype.In(2).Kind() != reflect.Interface {
if reportErr {
log.LogError("method", mname, "arg2 type not a interface:", mtype.In(2).Kind())
}
continue
}
// Third arg must be a int.
if mtype.In(3).Kind() != reflect.Int {
if reportErr {
log.LogError("method", mname, "arg3 type not a int:", mtype.In(3).Kind())
}
continue
}
// Method needs three out.
if mtype.NumOut() != 3 {
if reportErr {
log.LogError("method", mname, "has wrong number of outs:", mtype.NumOut())
}
continue
}
if mtype.Out(0).Kind() != reflect.String {
if reportErr {
log.LogError("method", mname, "out1 type not a string:", mtype.Out(0).Kind())
}
continue
}
if mtype.Out(1).Kind() != reflect.Int {
if reportErr {
log.LogError("method", mname, "out1 type not a int:", mtype.Out(1).Kind())
}
continue
}
if mtype.Out(2) != typeOfError {
if reportErr {
log.LogError("method", mname, "out3 type not a error:", mtype.In(2).Kind())
}
continue
}
methods[mname] = method
}
return methods
}
下面就是使用方法:
type GoFSM struct {
}
func (f *GoFSM) State1(event string, param interface{}, t int) (nextstate string, timeout int, err error) {
log.LogMessage(event, param.(int))
return "State2", 100, nil //如果timeout大于0,则在timeout毫秒后,自动调用下一个状态,下一个状态的event为timeout
}
func (f *GoFSM) State2(event string, param interface{}, t int) (nextstate string, timeout int, err error) {
log.LogMessage(event)
return "stop", 0, errors.New("stop ok") //nextstate=stop则停止状态机,err为停止原因
}
func main() {
f := util.NewFSM(&GoFSM{})
f.Init("State1") //初始状态
f.SendEvent("Do", 1) //发送事件
time.Sleep(time.Second * 1)
}
所有的状态回调函数,必须以大写字母开头,原型必须是
func(event string, param interface{}, t int) (nextstate string, timeout int, err error)
event是事件名,param为事件的参数,t>0表示这是一个延时事件。返回值:nextstate为新的状态,必须和状态回调函数同名,如果为"stop"则表示没有后续的状态,状态机停止。timeout>0表示延时回调,将在timeout时间后,产生一个timeout事件。