summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxengineering <mail2xengineering@protonmail.com>2021-06-15 15:51:38 +0200
committerxengineering <mail2xengineering@protonmail.com>2021-06-15 15:58:23 +0200
commit4a01c8e94921d9ddcaf6d6dee0feca4d9c3655a2 (patch)
tree634a3d6e380079d0da5e8fafaf866a33a63fb4bc
parent2269cc653577dffc58dba4ad5534583f28f224de (diff)
downloadbirdscan-4a01c8e94921d9ddcaf6d6dee0feca4d9c3655a2.tar
birdscan-4a01c8e94921d9ddcaf6d6dee0feca4d9c3655a2.tar.zst
birdscan-4a01c8e94921d9ddcaf6d6dee0feca4d9c3655a2.zip
Refactoring: Camera has Statemachine and not is Statemachine
-rw-r--r--src/camera.go38
-rw-r--r--src/main.go4
-rw-r--r--src/state.go69
-rw-r--r--src/web.go4
4 files changed, 67 insertions, 48 deletions
diff --git a/src/camera.go b/src/camera.go
new file mode 100644
index 0000000..12a9d54
--- /dev/null
+++ b/src/camera.go
@@ -0,0 +1,38 @@
+// vim: shiftwidth=4 tabstop=4 noexpandtab
+
+package main
+
+type Camera struct {
+ statemachine Machine
+}
+
+func NewCamera() Camera {
+ return Camera{
+ statemachine: 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),
+ state_listeners: make([]*(chan string), 0),
+ },
+ }
+}
+
+func (cam *Camera) run() {
+ cam.statemachine.Run()
+}
diff --git a/src/main.go b/src/main.go
index af82e8f..768920a 100644
--- a/src/main.go
+++ b/src/main.go
@@ -11,7 +11,7 @@ import (
)
var (
- camera Machine
+ camera Camera
)
type config struct {
@@ -31,7 +31,7 @@ func main() {
cfg := readConfig(configPath)
// setup camera state machine
- camera = newCamStateMachine()
+ camera = NewCamera()
// start goroutines
server := NewWebServer(&cfg.WebConfig)
diff --git a/src/state.go b/src/state.go
index 9a08738..5970ba8 100644
--- a/src/state.go
+++ b/src/state.go
@@ -7,6 +7,7 @@ import (
"time"
)
+// This struct represents the statemachine.
type Machine struct {
name string
initial string
@@ -28,6 +29,16 @@ type MachineTransition struct {
to string
}
+// This will run the statemachine (blocking).
+func (m *Machine) Run() {
+ var event string
+ for {
+ event = <-m.api // read api (blocking)
+ m.processEvent(event)
+ }
+}
+
+// A simple Getter to retrieve the current state.
func (m Machine) GetState() string {
if m.current == "" {
return m.initial
@@ -35,10 +46,14 @@ func (m Machine) GetState() string {
return m.current
}
+// This function blocks until the current state differs
+// from the 'known' state. It then returns the current
+// state.
func (m *Machine) GetStateOnChange(known string) string {
listener := make(chan string)
var current string
- m.RegisterListener(&listener)
+ m.registerListener(&listener)
+ defer m.deregisterListener(&listener)
for {
if m.GetState() != known {
current = m.GetState()
@@ -49,15 +64,19 @@ func (m *Machine) GetStateOnChange(known string) string {
break
}
}
- m.DeregisterListener(&listener)
return current
}
-func (m *Machine) RegisterListener(listener *(chan string)) {
+// Use this function to send an event to the statemachine.
+func (m *Machine) SendEvent(event string) {
+ m.api <- event // blocks until m.run() goroutine handles this event
+}
+
+func (m *Machine) registerListener(listener *(chan string)) {
m.state_listeners = append(m.state_listeners, listener)
}
-func (m *Machine) DeregisterListener(listener *(chan string)) {
+func (m *Machine) deregisterListener(listener *(chan string)) {
wasRemoved := false
if len(m.state_listeners) == 0 {
log.Println("Not able to unregister listener from empty state_listener slice")
@@ -90,25 +109,13 @@ func (m *Machine) processEvent(event string) string {
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)
- }
-}
-
+// In this function one can implement callback functions
+// which will be triggered on certain transitions.
func (m *Machine) runHooks(last string, next string) {
- log.Println("Send state to listeners ...")
for _,listener := range(m.state_listeners) {
*listener <- next
}
- log.Println("State sent to listeners")
if last == "idle" && next == "single_picture" {
// TODO implement launch of python subprocess here
@@ -118,29 +125,3 @@ func (m *Machine) runHooks(last string, next string) {
}()
}
}
-
-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),
- state_listeners: make([]*(chan string), 0),
- }
-}
-
diff --git a/src/web.go b/src/web.go
index 206681d..53f8c0a 100644
--- a/src/web.go
+++ b/src/web.go
@@ -58,11 +58,11 @@ func jsHandler(w http.ResponseWriter, r *http.Request) {
func stateHandler(w http.ResponseWriter, r *http.Request) {
known_state := r.FormValue("known_state")
- w.Write([]byte(fmt.Sprintf("%s", camera.GetStateOnChange(known_state))))
+ w.Write([]byte(fmt.Sprintf("%s", camera.statemachine.GetStateOnChange(known_state))))
}
func singlePictureHandler(w http.ResponseWriter, r *http.Request) {
- camera.SendEvent("take_single_picture")
+ camera.statemachine.SendEvent("take_single_picture")
fmt.Fprintf(w, http.StatusText(http.StatusOK))
}