-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstate_machine.go
173 lines (151 loc) · 3.8 KB
/
state_machine.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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
package gostatechart
import (
"context"
"fmt"
"reflect"
"time"
)
//namespace boost
//{
//namespace statechart
//{
// template<
// class MostDerived,
// class InitialState,
// class Allocator = std::allocator< void >,
// class ExceptionTranslator = null_exception_translator >
// class state_machine : noncopyable
// {
// public:
// typedef MostDerived outermost_context_type;
//
// void initiate();
// void terminate();
// bool terminated() const;
//
// void process_event( const event_base & );
//
// template< class Target >
// Target state_cast() const;
// template< class Target >
// Target state_downcast() const;
//
// // a model of the StateBase concept
// typedef implementation-defined state_base_type;
// // a model of the standard Forward Iterator concept
// typedef implementation-defined state_iterator;
//
// state_iterator state_begin() const;
// state_iterator state_end() const;
//
// void unconsumed_event( const event_base & ) {}
//
// protected:
// state_machine();
// ~state_machine();
//
// void post_event(
// const intrusive_ptr< const event_base > & );
// void post_event( const event_base & );
//
// const event_base * triggering_event() const;
// };
//}
//}
type StateMachine struct {
initialState reflect.Type
ctx context.Context
events chan Event
currentState State
currentTransitions Transitions
parent *StateMachine
}
func NewStateMachine(ctx context.Context, initialState State) *StateMachine {
if initialState == nil {
return nil
}
return &StateMachine{
initialState: reflect.TypeOf(initialState),
ctx: ctx,
events: make(chan Event, 32),
}
}
func (machine *StateMachine) CurrentState() State {
return machine.currentState
}
func (machine *StateMachine) Initiate(event Event) error {
if machine.currentState != nil {
return fmt.Errorf("already running")
}
machine.transit(machine.initialState, event)
return nil
}
func (machine *StateMachine) Parent() *StateMachine {
if machine.parent == nil {
return machine
}
return machine.parent.Parent()
}
func (machine *StateMachine) PostEvent(e Event) {
if e == nil {
return
}
if machine.parent == nil {
machine.events <- e
return
}
machine.parent.PostEvent(e)
}
func (machine *StateMachine) ProcessEvent(ctx context.Context, e Event, args ...interface{}) {
ne := machine.currentState.React(ctx, e, args...)
if ne != nil {
machine.PostEvent(ne)
}
next, ok := machine.currentTransitions[reflect.TypeOf(e)]
if ok {
machine.transit(next, e)
}
}
func (machine *StateMachine) Run(exitChan chan int) {
for machine.currentState != nil {
currentState := machine.currentState
if currentState != nil {
if e := currentState.GetEvent(); e != nil {
machine.PostEvent(e)
}
}
select {
case e := <-machine.events:
machine.ProcessEvent(context.Background(), e)
case <-time.After(10 * time.Millisecond):
continue
case <-exitChan:
machine.Terminate(nil)
return
}
}
}
func (machine *StateMachine) Terminate(event Event) {
machine.transit(nil, event)
}
func (machine *StateMachine) transit(stateType reflect.Type, event Event) {
if machine.currentState != nil {
machine.currentState.terminate(event)
if e := machine.currentState.End(context.Background(), event); e != nil {
machine.PostEvent(e)
}
machine.currentState = nil
}
if stateType == nil {
return
}
nextState := reflect.New(stateType.Elem()).Interface().(State)
machine.currentState = nextState
machine.currentTransitions = nextState.GetTransitions()
if e := machine.currentState.initiate(machine.ctx, machine, nextState, event); e != nil {
machine.PostEvent(e)
}
if e := machine.currentState.Begin(machine.ctx, event); e != nil {
machine.PostEvent(e)
}
}