用GO實現的erlang的genfsm.

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

關於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事件。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.