summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxengineering <mail2xengineering@protonmail.com>2021-06-14 11:48:39 +0200
committerxengineering <mail2xengineering@protonmail.com>2021-06-14 11:48:39 +0200
commit2269cc653577dffc58dba4ad5534583f28f224de (patch)
tree31d99e3bc92a49ca87e5dac363481fed982128f9
parent9d5a2f9870e52bfc0fe6db8c27981f29d91dcb55 (diff)
downloadbirdscan-2269cc653577dffc58dba4ad5534583f28f224de.tar
birdscan-2269cc653577dffc58dba4ad5534583f28f224de.tar.zst
birdscan-2269cc653577dffc58dba4ad5534583f28f224de.zip
Implement Frontend Status Display
-rw-r--r--data/html/index.html1
-rw-r--r--data/js/birdscan.js32
-rw-r--r--src/state.go54
-rw-r--r--src/web.go7
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 @@
<main>
<h1>birdscan</h1>
<p>A software to take beautiful pictures of birds with a Raspberry Pi Camera.</p>
+ <p id="state"></p>
<button onclick="singlePicture()">Take single picture</button>
</main>
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))
}