From 2269cc653577dffc58dba4ad5534583f28f224de Mon Sep 17 00:00:00 2001 From: xengineering Date: Mon, 14 Jun 2021 11:48:39 +0200 Subject: Implement Frontend Status Display --- data/html/index.html | 1 + data/js/birdscan.js | 32 ++++++++++++++++++++++++++++++- src/state.go | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/web.go | 7 ++++++- 4 files changed, 91 insertions(+), 3 deletions(-) diff --git a/data/html/index.html b/data/html/index.html index eddc2b3..268aad9 100644 --- a/data/html/index.html +++ b/data/html/index.html @@ -25,6 +25,7 @@

birdscan

A software to take beautiful pictures of birds with a Raspberry Pi Camera.

+

diff --git a/data/js/birdscan.js b/data/js/birdscan.js index d1bba04..d3a46c3 100644 --- a/data/js/birdscan.js +++ b/data/js/birdscan.js @@ -1,7 +1,37 @@ +var state = "unknown"; +var updateStatePending = false; + function singlePicture() { const xhttp = new XMLHttpRequest(); - xhttp.open("POST", "./api/single_picture"); + xhttp.open("POST", "./api/single_picture", true); xhttp.send(); } +function updateState() { + if (!updateStatePending) { + updateStatePending = true; + var state_request = new XMLHttpRequest(); + state_request.onreadystatechange = updateStateCallback; + state_request.open("POST", "./api/state?known_state=" + state, true); + state_request.send(); + } +} + +function updateStateCallback() { + if (this.readyState == 4) { // readyState=DONE + updateStatePending = false; + if (this.status == 200) { // status=OK + state = this.responseText; + document.getElementById("state").innerHTML = "Current state: " + state; + setTimeout(updateState, 100); // restart state poll after short time on success + } else { + state = "Verbindung abgebrochen!"; + document.getElementById("state").innerHTML = "Current state: " + state; + } + } +} + +document.getElementById("state").innerHTML = "Current state: " + state; +updateState(); + diff --git a/src/state.go b/src/state.go index 9c6ab66..9a08738 100644 --- a/src/state.go +++ b/src/state.go @@ -13,6 +13,7 @@ type Machine struct { current string states StateMap api chan string + state_listeners []*(chan string) } type StateMap map[string]MachineState @@ -34,6 +35,49 @@ func (m Machine) GetState() string { return m.current } +func (m *Machine) GetStateOnChange(known string) string { + listener := make(chan string) + var current string + m.RegisterListener(&listener) + for { + if m.GetState() != known { + current = m.GetState() + break + } + current = <-listener + if current != known { + break + } + } + m.DeregisterListener(&listener) + return current +} + +func (m *Machine) RegisterListener(listener *(chan string)) { + m.state_listeners = append(m.state_listeners, listener) +} + +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") + } + for index,item := range(m.state_listeners) { + if item == listener { + if index == len(m.state_listeners) - 1 { + m.state_listeners = m.state_listeners[: len(m.state_listeners) - 1] + } else { + m.state_listeners[index] = m.state_listeners[len(m.state_listeners) - 1] + m.state_listeners = m.state_listeners[: len(m.state_listeners) - 1] + } + wasRemoved = true + } + } + if !wasRemoved { + log.Println("Could not find listener in slice state_listeners") + } +} + func (m *Machine) processEvent(event string) string { current := m.GetState() next := m.states[current].on[event].to @@ -59,7 +103,14 @@ func (m *Machine) run() { } func (m *Machine) runHooks(last string, next string) { - if next == "single_picture" { + + 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 go func() { time.Sleep(1 * time.Second) @@ -89,6 +140,7 @@ func newCamStateMachine() Machine { }, }, api: make(chan string), + state_listeners: make([]*(chan string), 0), } } diff --git a/src/web.go b/src/web.go index 6515f47..206681d 100644 --- a/src/web.go +++ b/src/web.go @@ -37,6 +37,7 @@ func (server *WebServer) run() { router.Get("/", indexHandler) router.Get("/css/birdscan.css", cssHandler) router.Get("/js/birdscan.js", jsHandler) + router.Post("/api/state", stateHandler) router.Post("/api/single_picture", singlePictureHandler) log.Println("Binding to 'http://" + server.config.BindAddress + ":" + server.config.BindPort + "'") @@ -55,8 +56,12 @@ func jsHandler(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, fmt.Sprintf("%s/js/birdscan.js", APP_DATA)) } +func stateHandler(w http.ResponseWriter, r *http.Request) { + known_state := r.FormValue("known_state") + w.Write([]byte(fmt.Sprintf("%s", camera.GetStateOnChange(known_state)))) +} + func singlePictureHandler(w http.ResponseWriter, r *http.Request) { - log.Println("Received request for a single picture") camera.SendEvent("take_single_picture") fmt.Fprintf(w, http.StatusText(http.StatusOK)) } -- cgit v1.2.3-70-g09d2