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
|
// vim: shiftwidth=4 tabstop=4 noexpandtab
package main
import (
"log"
"time"
)
type Machine struct {
name string
initial string
current string
states StateMap
api chan string
}
type StateMap map[string]MachineState
type MachineState struct {
on TransitionMap
}
type TransitionMap map[string]MachineTransition
type MachineTransition struct {
to string
}
func (m Machine) GetState() string {
if m.current == "" {
return m.initial
}
return m.current
}
func (m *Machine) processEvent(event string) string {
current := m.GetState()
next := m.states[current].on[event].to
if next != "" {
log.Printf("State machine '%s' changes from '%s' to '%s'\n", m.name, current, next)
m.current = next
m.runHooks(current, next)
return next
}
return current
}
func (m *Machine) SendEvent(event string) {
m.api <- event // blocks until m.run() goroutine handles this event
}
func (m *Machine) run() {
var event string
for {
event = <-m.api // read api (blocking)
m.processEvent(event)
}
}
func (m *Machine) runHooks(last string, next string) {
if next == "single_picture" {
// TODO implement launch of python subprocess here
go func() {
time.Sleep(1 * time.Second)
m.SendEvent("single_picture_taken")
}()
}
}
func newCamStateMachine() Machine {
return Machine{
name: "camera",
initial: "idle",
states: StateMap{
"idle": MachineState{
on: TransitionMap{
"take_single_picture": MachineTransition{
to: "single_picture",
},
},
},
"single_picture": MachineState{
on: TransitionMap{
"single_picture_taken": MachineTransition{
to: "idle",
},
},
},
},
api: make(chan string),
}
}
|