用GO实现的erlang的genfsm.

来源:互联网 发布:matlab 2014a for mac 编辑:程序博客网 时间:2024/05/01 22:23

关于erlang的有限状态机,参考erlang四大behaviour之二-gen_fsm这篇文章。

有限状态机可以用下面这个公式来表达

State(S) x Event(E) -> Actions(A), State(S')

这两天正好空闲,就用golang实现了一下,话不多说,直接上代码

package utilimport ("errors""reflect""sync""time""unicode""unicode/utf8""util/log")var typeOfError = reflect.TypeOf((*error)(nil)).Elem()type FSM struct {sync.MutexStopReason stringrcvr       reflect.Value // receiver of methods for the servicetyp        reflect.Type  // type of the receivermethod     map[string]reflect.Methodevent      chan Eventquit       chan intstate      stringstopped    bool}type Event struct {event   stringparam   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 = startgo 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 <- 1return}if errmsg != "" {log.LogError(errmsg)}fsm.state = nextstateif 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 = messagefsm.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.Typemname := method.Nameif !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事件。
0 0