From 4a01c8e94921d9ddcaf6d6dee0feca4d9c3655a2 Mon Sep 17 00:00:00 2001 From: xengineering Date: Tue, 15 Jun 2021 15:51:38 +0200 Subject: Refactoring: Camera has Statemachine and not is Statemachine --- src/camera.go | 38 ++++++++++++++++++++++++++++++++ src/main.go | 4 ++-- src/state.go | 69 ++++++++++++++++++++++------------------------------------- src/web.go | 4 ++-- 4 files changed, 67 insertions(+), 48 deletions(-) create mode 100644 src/camera.go 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)) } -- cgit v1.2.3-70-g09d2