diff options
| author | Sam Anthony <sam@samanthony.xyz> | 2024-11-21 16:47:08 -0500 |
|---|---|---|
| committer | Sam Anthony <sam@samanthony.xyz> | 2024-11-21 16:47:08 -0500 |
| commit | dbc71139a5708b371ffb0b7580f562674707c558 (patch) | |
| tree | 1ee13dbfa44588a723a0d1fdf4eb24f5d76a9158 | |
| parent | a464e144485f69fd9ae6efee3e3fe5e00590e6de (diff) | |
| download | soen422-dbc71139a5708b371ffb0b7580f562674707c558.zip | |
server dashboard
| -rw-r--r-- | server/building.go | 40 | ||||
| -rw-r--r-- | server/dashboard.go | 78 | ||||
| -rw-r--r-- | server/humidity.go | 41 | ||||
| -rw-r--r-- | server/server.go | 9 |
4 files changed, 126 insertions, 42 deletions
diff --git a/server/building.go b/server/building.go new file mode 100644 index 0000000..c7d298f --- /dev/null +++ b/server/building.go @@ -0,0 +1,40 @@ +package main + +import "log" + +type Building map[RoomID]Record[Humidity] + +func newBuilding(roomIDs []RoomID) Building { + b := make(Building) + for _, id := range roomIDs { + b[id] = newRecord[Humidity]() + } + return b +} + +func (b Building) Close() { + for _, record := range b { + record.Close() + } +} + +// Calculate the average humidity in the building. Returns false if there is not enough data available. +func (b Building) average() (Humidity, bool) { + var sum Humidity = 0 + nRooms := 0 + for room, record := range b { + c := make(chan Humidity) + record.getRecent <- c + if humidity, ok := <-c; ok { + sum += humidity + nRooms++ + } else { + log.Printf("Warning: no humidity for room '%s'\n", room) + } + } + if nRooms == 0 { + log.Println("Warning: not enough data to calculate average humidity") + return -1.0, false + } + return sum / Humidity(nRooms), true +} diff --git a/server/dashboard.go b/server/dashboard.go new file mode 100644 index 0000000..0e9cd41 --- /dev/null +++ b/server/dashboard.go @@ -0,0 +1,78 @@ +package main + +import ( + "fmt" + "html/template" + "log" + "net/http" +) + +const dashboardHtml = ` +<!DOCTYPE html> +<html> + <head> + <title>HVAC Dashboard</title> + </head> + <body> + <p>Average humidity: {{ printf "%.1f %%" .Average }}</p> + <table> + <tr><th>Room</th><th>Humidity</th></tr> + {{ range .Rooms }} + <tr><td>{{ .RoomID }}</td><td>{{ printf "%.1f %%" .Humidity }}</td></tr> + {{ end }} + </table> + </body> +</html>` + +var dashboard = template.Must(template.New("dashboard").Parse(dashboardHtml)) + +type Dashboard struct { + Average Humidity + Rooms []Room +} + +type Room struct { + RoomID + Humidity +} + +type DashboardHandler struct { + Building +} + +func (h DashboardHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + log.Println(r.Method, r.URL) + + if r.Method != http.MethodGet { + w.WriteHeader(http.StatusMethodNotAllowed) + fmt.Fprintf(w, "invalid method: '%s'", r.Method) + return + } + + db := newDashboard(h.Building) + err := dashboard.Execute(w, db) + if err != nil { + log.Println(err) + } +} + +func newDashboard(b Building) Dashboard { + average, ok := b.average() + if !ok { + average = -1 + } + + // TODO: sort by room ID. + rooms := make([]Room, 0, len(b)) + for id, record := range b { + c := make(chan Humidity) + record.getRecent <- c + humidity, ok := <-c + if !ok { + humidity = -1 + } + rooms = append(rooms, Room{id, humidity}) + } + + return Dashboard{average, rooms} +} diff --git a/server/humidity.go b/server/humidity.go index 3982545..8cffccc 100644 --- a/server/humidity.go +++ b/server/humidity.go @@ -11,21 +11,7 @@ import ( type Humidity float32 type HumidityHandler struct { - rooms map[RoomID]Record[Humidity] -} - -func newHumidityHandler(rooms []RoomID) HumidityHandler { - h := HumidityHandler{make(map[RoomID]Record[Humidity])} - for _, id := range rooms { - h.rooms[id] = newRecord[Humidity]() - } - return h -} - -func (h HumidityHandler) Close() { - for _, record := range h.rooms { - record.Close() - } + Building } func (h HumidityHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -42,7 +28,7 @@ func (h HumidityHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } func (h HumidityHandler) get(w http.ResponseWriter, r *http.Request) { - if humidity, ok := h.average(); ok { + if humidity, ok := h.Building.average(); ok { fmt.Fprintf(w, "%.2f", humidity) } else { w.WriteHeader(http.StatusGone) @@ -65,7 +51,7 @@ func (h HumidityHandler) post(w http.ResponseWriter, r *http.Request) { return } - record, ok := h.rooms[room] + record, ok := h.Building[room] if !ok { badRequest(w, "invalid room ID: '%s'", room) return @@ -74,27 +60,6 @@ func (h HumidityHandler) post(w http.ResponseWriter, r *http.Request) { record.put <- Humidity(humidity) } -// Calculate the average humidity in the building. Returns false if there is not enough data available. -func (h HumidityHandler) average() (Humidity, bool) { - var sum Humidity = 0 - nRooms := 0 - for room, record := range h.rooms { - c := make(chan Humidity) - record.getRecent <- c - if humidity, ok := <-c; ok { - sum += humidity - nRooms++ - } else { - log.Printf("Warning: no humidity for room '%s'\n", room) - } - } - if nRooms == 0 { - log.Println("Warning: not enough data to calculate average humidity") - return -1.0, false - } - return sum / Humidity(nRooms), true -} - // Parse the value associated with each key in the query string. Returns a map of // keys and values, or error if one of the keys is missing or if there is no value // associated with one of the keys. diff --git a/server/server.go b/server/server.go index 31d53f4..7d5e977 100644 --- a/server/server.go +++ b/server/server.go @@ -8,7 +8,7 @@ import ( const addr = ":9090" -var rooms = []RoomID{ +var roomIDs = []RoomID{ "SNbeEcs7XVWMEvjeEYgwZnp9XYjToVhh", "rEKKa5TW5xjArmR25wT4Uiw7tksk4noE", } @@ -16,10 +16,11 @@ var rooms = []RoomID{ type RoomID string func main() { - humidityHandler := newHumidityHandler(rooms) - defer humidityHandler.Close() + building := newBuilding(roomIDs) + defer building.Close() - http.Handle("/humidity", humidityHandler) + http.Handle("/", DashboardHandler{building}) + http.Handle("/humidity", HumidityHandler{building}) http.Handle("/target_humidity", new(TargetHumidityHandler)) http.Handle("/duty_cycle", new(DutyCycleHandler)) |