Skip to content

Commit 6261f50

Browse files
committed
set a machine's current state from a provided StateMap
1 parent 163382a commit 6261f50

File tree

4 files changed

+132
-45
lines changed

4 files changed

+132
-45
lines changed

error.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import (
44
"errors"
55
)
66

7-
var ErrNotInitialized = errors.New("state machine not initialized")
8-
var ErrNoSuchEvent = errors.New("no such event")
97
var ErrNoMatchingTransition = errors.New("no matching transition")
108
var ErrTransitionNotAllowed = errors.New("transition not allowed")
11-
var ErrStateNotCurrent = errors.New("state not current")
9+
var ErrStateTypeNotSupported = errors.New("state type not supported")

examples/cognizant/process.go

+11-5
Original file line numberDiff line numberDiff line change
@@ -139,15 +139,21 @@ func main() {
139139

140140
_ = process.Fire("monitor")
141141

142-
time.AfterFunc(2*time.Second, func() {
143-
_ = process.Fire("start")
144142
// _ = process.Send(statemachine.TriggerEvent{
145143
// Event: "monitor",
146144
// })
147145

148-
time.AfterFunc(2*time.Second, func() {
149-
process.Stop()
150-
})
146+
// _ = process.Send(statemachine.OverrideState{
147+
// State: statemachine.StateMap{
148+
// "stopped": nil,
149+
// },
150+
// })
151+
152+
stateJSON, _ := json.MarshalIndent(process.GetStateMap(), "", " ")
153+
fmt.Printf("%s\n", stateJSON)
154+
155+
time.AfterFunc(2*time.Second, func() {
156+
process.Stop()
151157
})
152158

153159
done := make(chan os.Signal, 1)

machine.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,23 @@ type Machine interface {
66
Build(machineBuilderFn func(machineBuilder MachineBuilder))
77
SetMachineDef(def *MachineDef)
88

9+
GetStateMap() StateMap
10+
911
GetState() string
10-
SetCurrentState(state string)
12+
SetCurrentState(state interface{}) error
1113
IsState(state string) bool
1214

13-
Submachine(state string) (Machine, error)
15+
Submachine(idPath ...string) (Machine, error)
1416

1517
Fire(event string) error
1618

19+
Send(signal Message) error
20+
1721
// TODO: ctx.ForceShutdownSubmachines(true), etc.
1822
// FireContext(, event string) error
1923
}
2024

2125
var _ Machine = (*machineImpl)(nil)
2226
var _ MachineBuildable = (*machineImpl)(nil)
27+
28+
type StateMap map[string]interface{}

machine_impl.go

+112-35
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package statemachine
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"reflect"
78
"sync"
@@ -62,7 +63,9 @@ func (m *machineImpl) SetMachineDef(def *MachineDef) {
6263
// fmt.Printf("machine def = %s\n", string(b))
6364

6465
m.def = def
65-
m.setCurrentState(m.def.InitialState)
66+
if err := m.setCurrentState(m.def.InitialState); err != nil {
67+
panic(err)
68+
}
6669
m.restartTimedEventsLoops()
6770
}
6871

@@ -86,17 +89,34 @@ func (m *machineImpl) restartTimedEventsLoops() {
8689
}
8790
}
8891

92+
// GetStateMap implements Machine.
93+
func (m *machineImpl) GetStateMap() StateMap {
94+
substate := StateMap{}
95+
if submachines, ok := m.submachines[m.currentState]; ok {
96+
for _, submachine := range submachines {
97+
if _, ok := submachine.submachines[submachine.currentState]; ok {
98+
substate[submachine.def.ID] = submachine.GetStateMap()
99+
} else {
100+
substate[submachine.def.ID] = submachine.currentState
101+
}
102+
}
103+
}
104+
return StateMap{
105+
m.currentState: substate,
106+
}
107+
}
108+
89109
// GetState implements Machine.
90110
func (m *machineImpl) GetState() string {
91111
return m.currentState
92112
}
93113

94114
// SetCurrentState implements Machine.
95-
func (m *machineImpl) SetCurrentState(state string) {
115+
func (m *machineImpl) SetCurrentState(state interface{}) error {
96116
m.mutex.Lock()
97117
defer m.mutex.Unlock()
98118

99-
m.setCurrentState(state)
119+
return m.setCurrentState(state)
100120
}
101121

102122
// IsState implements Machine.
@@ -142,7 +162,7 @@ func (m *machineImpl) Fire(event string) (err error) {
142162
}()
143163

144164
if m.IsState("") {
145-
err = ErrNotInitialized
165+
err = errors.New("state machine not initialized")
146166
return
147167
}
148168

@@ -164,7 +184,7 @@ func (m *machineImpl) Fire(event string) (err error) {
164184
func (m *machineImpl) findTransition(event string, fromState string) (transition Transition, err error) {
165185
eventDef, ok := m.def.Events[event]
166186
if !ok {
167-
err = ErrNoSuchEvent
187+
err = errors.New("no such event")
168188
return
169189
}
170190

@@ -228,48 +248,105 @@ func (m *machineImpl) matchTransition(transitions []*TransitionDef, fromState st
228248
return
229249
}
230250

231-
// TODO: get submachine by its id.
232-
func (m *machineImpl) Submachine(state string) (Machine, error) {
233-
// func (m *machineImpl) Submachine(state, id string) (Machine, error) {
234-
if m.currentState != state {
235-
return nil, ErrStateNotCurrent
251+
func (m *machineImpl) Submachine(idPath ...string) (Machine, error) {
252+
for _, submachine := range m.submachines[m.currentState] {
253+
if submachine.def.ID == idPath[0] {
254+
if len(idPath) > 1 {
255+
return submachine.Submachine(idPath[1:]...)
256+
}
257+
return submachine, nil
258+
}
236259
}
237260

238-
// for _, submachine := range m.submachines[state] {
239-
// if submachine.def.ID == id {
240-
// return submachine, nil
241-
// }
242-
// }
261+
return nil, errors.New("submachine not active")
262+
}
263+
264+
func (m *machineImpl) setCurrentStateMap(state StateMap) error {
265+
for rootState, subStates := range state {
266+
switch subStates.(type) {
267+
case nil:
268+
// fmt.Printf("setting state to '%s'\n", rootState)
269+
return m.setCurrentState(rootState)
270+
271+
case StateMap:
272+
// fmt.Printf("setting state to '%s'\n", rootState)
273+
if err := m.setCurrentState(rootState); err != nil {
274+
return err
275+
}
276+
277+
for id, state := range subStates.(StateMap) {
278+
for _, submachine := range m.submachines[rootState] {
279+
if submachine.def.ID == id {
280+
switch state.(type) {
281+
case StateMap:
282+
// fmt.Printf("nesting into submachine '%s'\n", id)
283+
if err := submachine.setCurrentStateMap(state.(StateMap)); err != nil {
284+
return err
285+
}
286+
case string:
287+
// fmt.Printf("setting submachine '%s' to '%s'\n", id, state)
288+
if err := submachine.SetCurrentState(state); err != nil {
289+
return err
290+
}
291+
default:
292+
return ErrStateTypeNotSupported
293+
}
294+
}
295+
}
296+
}
243297

244-
return m.submachines[state][0], nil
298+
default:
299+
if err := m.setCurrentState(rootState); err != nil {
300+
return err
301+
}
302+
303+
return ErrStateTypeNotSupported
304+
}
305+
306+
// there is only one kv in any given StateMap
307+
break
308+
}
309+
310+
return nil
245311
}
246312

247-
func (m *machineImpl) setCurrentState(state string) {
248-
for _, s := range m.def.States {
249-
if s == state {
250-
m.previousState = m.currentState
251-
m.currentState = state
252-
return
313+
func (m *machineImpl) setCurrentState(state interface{}) error {
314+
if state, ok := state.(StateMap); ok {
315+
if err := m.setCurrentStateMap(state); err != nil {
316+
return err
253317
}
318+
return nil
254319
}
255320

256-
for s, submachineDefs := range m.def.Submachines {
257-
if s == state {
258-
m.submachines[state] = []*machineImpl{}
259-
for _, submachineDef := range submachineDefs {
260-
submachine := &machineImpl{
261-
supermachine: m,
262-
submachines: map[string][]*machineImpl{},
263-
}
264-
submachine.SetMachineDef(submachineDef)
265-
m.submachines[state] = append(m.submachines[state], submachine)
321+
if state, ok := state.(string); ok {
322+
for _, s := range m.def.States {
323+
if s == state {
324+
m.previousState = m.currentState
325+
m.currentState = state
326+
return nil
266327
}
328+
}
267329

268-
m.previousState = m.currentState
269-
m.currentState = state
270-
return
330+
for s, submachineDefs := range m.def.Submachines {
331+
if s == state {
332+
m.submachines[state] = []*machineImpl{}
333+
for _, submachineDef := range submachineDefs {
334+
submachine := &machineImpl{
335+
supermachine: m,
336+
submachines: map[string][]*machineImpl{},
337+
}
338+
submachine.SetMachineDef(submachineDef)
339+
m.submachines[state] = append(m.submachines[state], submachine)
340+
}
341+
342+
m.previousState = m.currentState
343+
m.currentState = state
344+
return nil
345+
}
271346
}
272347
}
348+
349+
return ErrStateTypeNotSupported
273350
}
274351

275352
func (m *machineImpl) applyTransition(transition Transition) error {

0 commit comments

Comments
 (0)