-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathautomata.go
155 lines (139 loc) · 3.3 KB
/
automata.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// Copyright 2018 University of Glasgow.
// Use of this source code is governed by an Apache
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"log"
"sync"
)
const (
stateClosed State = iota
stateOpening
stateOpened
stateBinding
stateBindError
stateListening
stateProcessingMessage
stateMessageError
)
const (
eventOpen Event = iota + 100
eventClose
eventBind
eventSuccess
eventError
eventUnderLimit
eventOverLimit
eventChannelExpired
)
// Automata is a simple Finite State Machine (FSM). We can assign a callback function
// that will be invoked when FSM reaches a particular state. We can also raise an event
// to trigger a transition from one to another state.
type Automata struct {
sync.RWMutex
current State
transitions transitions
callbacks callbacks
}
// State represents a state of the automata
type State int
// Event represents an event of the automata
type Event int
// Transition is a state transition from state `Src` to state `Dest` when event `Event`
// raised.
type Transition struct {
Src State
Event Event
Dest State
}
type transitions map[State]map[Event]State
// Callback is a function that will be invoked when Automata reaches a particular state.
type Callback func(data []interface{})
type callbacks map[State]Callback
// NewAutomata returns an instance of Automata.
func NewAutomata(current State, ts []Transition, callbacks callbacks) *Automata {
fsm := &Automata{
current: current,
transitions: make(transitions),
callbacks: callbacks,
}
for i := range ts {
if _, ok := fsm.transitions[ts[i].Src]; !ok {
fsm.transitions[ts[i].Src] = make(map[Event]State, 0)
}
fsm.transitions[ts[i].Src][ts[i].Event] = ts[i].Dest
}
return fsm
}
// Current returns the current state of Automata.
func (a *Automata) Current() State {
a.RLock()
defer a.RUnlock()
return a.current
}
// Event triggers a transition of Automata from one to another state.
// Event returns an error when it cannot made the transition, for example:
// there is no available transition.
func (a *Automata) Event(event Event, data ...interface{}) error {
var (
dest State
ok bool
cb Callback
)
if dest, ok = a.transitions[a.current][event]; ok {
a.Lock()
log.Println("event", event.String(), "transition from",
a.current.String(), "to", dest.String())
a.current = dest
a.Unlock()
if cb, ok = a.callbacks[a.current]; ok {
cb(data)
}
return nil
}
return fmt.Errorf("state %s does not have transition for event %s",
a.current.String(), event.String())
}
func (s State) String() string {
switch s {
case stateClosed:
return "closed"
case stateOpening:
return "opening"
case stateOpened:
return "opened"
case stateBinding:
return "binding"
case stateBindError:
return "bindError"
case stateListening:
return "listening"
case stateProcessingMessage:
return "processingMessage"
case stateMessageError:
return "messageError"
}
return "undefined"
}
func (e Event) String() string {
switch e {
case eventBind:
return "bind"
case eventChannelExpired:
return "channelExpired"
case eventClose:
return "close"
case eventError:
return "error"
case eventOpen:
return "open"
case eventOverLimit:
return "overLimit"
case eventSuccess:
return "success"
case eventUnderLimit:
return "underLimit"
}
return "undefined"
}