2021-01-31 16:56:33 +01:00
|
|
|
package uncanny
|
|
|
|
|
|
|
|
import (
|
|
|
|
"log"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"path"
|
|
|
|
"strconv"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// highest slot number (range: 0..maxSlot)
|
|
|
|
maxSlot = 4
|
2022-01-16 23:57:43 +01:00
|
|
|
// highest display line number (range: 0..maxLine)
|
|
|
|
maxLine = 1
|
2021-01-31 16:56:33 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
type Server struct {
|
|
|
|
*http.ServeMux
|
2021-02-08 13:04:30 +01:00
|
|
|
srv *http.Server
|
2021-01-31 16:56:33 +01:00
|
|
|
can *Can
|
|
|
|
}
|
|
|
|
|
2021-02-08 13:04:30 +01:00
|
|
|
func NewServer(can *Can, addr string) *Server {
|
2021-01-31 16:56:33 +01:00
|
|
|
ret := &Server{
|
|
|
|
ServeMux: http.NewServeMux(),
|
|
|
|
can: can,
|
2021-02-08 13:04:30 +01:00
|
|
|
srv: &http.Server{
|
|
|
|
Addr: addr,
|
|
|
|
},
|
2021-01-31 16:56:33 +01:00
|
|
|
}
|
|
|
|
ret.registerHandlers()
|
2021-02-08 13:04:30 +01:00
|
|
|
ret.srv.Handler = ret
|
2021-01-31 16:56:33 +01:00
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Server) registerHandlers() {
|
|
|
|
s.HandleFunc("/", http.NotFound)
|
|
|
|
s.HandleFunc("/dispense/", func(rw http.ResponseWriter, r *http.Request) {
|
2021-01-31 18:05:33 +01:00
|
|
|
if matched, _ := path.Match("/dispense/[0-9]*", r.URL.Path); matched {
|
2021-01-31 16:56:33 +01:00
|
|
|
slot, err := strconv.ParseUint(path.Base(r.URL.Path), 10, 32)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Error decoding slot number: %v", err)
|
|
|
|
http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
|
|
|
} else if slot < 0 || slot > maxSlot {
|
2021-01-31 18:05:33 +01:00
|
|
|
log.Printf("Invalid slot number: %d", slot)
|
2021-01-31 16:56:33 +01:00
|
|
|
http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
|
|
|
} else {
|
2021-02-08 13:04:30 +01:00
|
|
|
s.can.Dispense(int(slot))
|
|
|
|
rw.Header().Set("Content-Type", "text/plain")
|
|
|
|
rw.WriteHeader(http.StatusOK)
|
|
|
|
fmt.Fprintf(rw, "%d dispensing", slot)
|
2021-01-31 16:56:33 +01:00
|
|
|
}
|
|
|
|
} else {
|
2021-01-31 18:05:33 +01:00
|
|
|
log.Printf("Mismatched path: %s", r.URL.Path)
|
2021-01-31 16:56:33 +01:00
|
|
|
http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
s.HandleFunc("/stop", func(rw http.ResponseWriter, r *http.Request) {
|
2021-02-08 13:04:30 +01:00
|
|
|
s.can.Cancel()
|
|
|
|
rw.Header().Set("Content-Type", "text/plain")
|
|
|
|
rw.WriteHeader(http.StatusOK)
|
|
|
|
fmt.Fprintf(rw, "stopping")
|
2021-01-31 16:56:33 +01:00
|
|
|
})
|
|
|
|
s.HandleFunc("/level/", func(rw http.ResponseWriter, r *http.Request) {
|
2021-01-31 18:05:33 +01:00
|
|
|
if matched, _ := path.Match("/level/[0-9]*", r.URL.Path); matched {
|
2021-01-31 16:56:33 +01:00
|
|
|
slot, err := strconv.ParseUint(path.Base(r.URL.Path), 10, 32)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Error decoding slot number: %v", err)
|
|
|
|
http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
|
|
|
} else if slot < 0 || slot > maxSlot {
|
2021-01-31 18:05:33 +01:00
|
|
|
log.Printf("Invalid slot number: %d", slot)
|
2021-01-31 16:56:33 +01:00
|
|
|
http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
|
|
|
} else {
|
|
|
|
rw.Header().Set("Content-Type", "text/plain")
|
|
|
|
rw.WriteHeader(http.StatusOK)
|
|
|
|
if s.can.IsEmpty(int(slot)) {
|
2021-01-31 18:05:33 +01:00
|
|
|
fmt.Fprintf(rw, "%d empty", slot)
|
2021-01-31 16:56:33 +01:00
|
|
|
} else {
|
2021-01-31 18:05:33 +01:00
|
|
|
fmt.Fprintf(rw, "%d full", slot)
|
2021-01-31 16:56:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2021-01-31 18:05:33 +01:00
|
|
|
log.Printf("Mismatched path: %s", r.URL.Path)
|
2021-01-31 16:56:33 +01:00
|
|
|
http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
s.HandleFunc("/active/", func(rw http.ResponseWriter, r *http.Request) {
|
2021-01-31 18:05:33 +01:00
|
|
|
if matched, _ := path.Match("/active/[0-9]*", r.URL.Path); matched {
|
2021-01-31 16:56:33 +01:00
|
|
|
slot, err := strconv.ParseUint(path.Base(r.URL.Path), 10, 32)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Error decoding slot number: %v", err)
|
|
|
|
http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
|
|
|
} else if slot < 0 || slot > maxSlot {
|
2021-01-31 18:05:33 +01:00
|
|
|
log.Printf("Invalid slot number: %d", slot)
|
2021-01-31 16:56:33 +01:00
|
|
|
http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
|
|
|
} else {
|
|
|
|
rw.Header().Set("Content-Type", "text/plain")
|
|
|
|
rw.WriteHeader(http.StatusOK)
|
|
|
|
if s.can.IsDispensing(int(slot)) {
|
2021-01-31 18:05:33 +01:00
|
|
|
fmt.Fprintf(rw, "%d dispensing", slot)
|
2021-01-31 16:56:33 +01:00
|
|
|
} else {
|
2021-01-31 18:05:33 +01:00
|
|
|
fmt.Fprintf(rw, "%d off", slot)
|
2021-01-31 16:56:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2021-01-31 18:05:33 +01:00
|
|
|
log.Printf("Mismatched path: %s", r.URL.Path)
|
2021-01-31 16:56:33 +01:00
|
|
|
http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
|
|
|
}
|
|
|
|
})
|
2022-01-16 23:57:43 +01:00
|
|
|
s.HandleFunc("/display/", func(rw http.ResponseWriter, r *http.Request) {
|
|
|
|
if matched, _ := path.Match("/display/[0-9]*", r.URL.Path); matched {
|
|
|
|
line, err := strconv.ParseUint(path.Base(r.URL.Path), 10, 32)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Error decoding line number: %v", err)
|
|
|
|
http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
|
|
|
} else if line < 0 || line > maxLine {
|
|
|
|
log.Printf("Invalid line number: %d", line)
|
|
|
|
http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
|
|
|
} else {
|
|
|
|
query := r.URL.Query()
|
|
|
|
if _, ok := query["text"]; ok {
|
|
|
|
text := query.Get("text")
|
|
|
|
s.can.DisplayPrint(int(line), text)
|
|
|
|
rw.Header().Set("Content-Type", "text/plain")
|
|
|
|
rw.WriteHeader(http.StatusOK)
|
|
|
|
fmt.Fprintf(rw, "Printed on line %d: '%s'", int(line), text)
|
|
|
|
} else {
|
|
|
|
log.Printf("Invalid query, missing text")
|
|
|
|
http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
log.Printf("Mismatched path: %s", r.URL.Path)
|
|
|
|
http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
|
|
|
}
|
|
|
|
})
|
2021-01-31 16:56:33 +01:00
|
|
|
}
|
|
|
|
|
2021-02-08 13:04:30 +01:00
|
|
|
func (s *Server) Start() error {
|
|
|
|
log.Printf("Listening on %v", s.srv.Addr)
|
|
|
|
return s.srv.ListenAndServe()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Server) Stop() error {
|
|
|
|
return s.srv.Close()
|
2021-01-31 16:56:33 +01:00
|
|
|
}
|