added visualisation
This commit is contained in:
20776
Chart.bundle.js
Executable file
20776
Chart.bundle.js
Executable file
File diff suppressed because it is too large
Load Diff
7
Chart.bundle.min.js
vendored
Executable file
7
Chart.bundle.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
@@ -77,15 +77,5 @@ func getPatternHandler(pattern string) http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendInternalError(err error, context RequestContext) {
|
|
||||||
errorMessage := "Internal Server Error!"
|
|
||||||
if DEBUG {
|
|
||||||
errorMessage += fmt.Sprintf( " Happened during %s request for pattern '%s': %s",
|
|
||||||
context.MethodType,
|
|
||||||
context.Pattern,
|
|
||||||
err.Error())
|
|
||||||
}
|
|
||||||
fmt.Println(errorMessage)
|
|
||||||
http.Error(context.ResponseWriter, errorMessage, 500)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
115
charts.html
Normal file
115
charts.html
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Title</title>
|
||||||
|
<script src="Chart.bundle.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="chart-container" style="position: relative; height:40vh; width:80vw; margin: auto">
|
||||||
|
<canvas id="myChart"></canvas>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
const humidityColor = 'rgb(45,141,45)';
|
||||||
|
const tempColor = 'rgb(0,134,222)';
|
||||||
|
const co2Color = 'rgb(194,30,30)';
|
||||||
|
|
||||||
|
async function initChart() {
|
||||||
|
const ctx = document.getElementById('myChart').getContext('2d');
|
||||||
|
const data = await fetch("http://tortedda.local/climate/data/");
|
||||||
|
const {humidity, temp, co2} = transformData(await data.json());
|
||||||
|
const myChart = Chart.Line(ctx, {
|
||||||
|
data: {
|
||||||
|
datasets: [{
|
||||||
|
label: 'Humidity',
|
||||||
|
data: humidity,
|
||||||
|
borderColor: humidityColor,
|
||||||
|
fill: false,
|
||||||
|
yAxisID: 'y-axis-3',
|
||||||
|
}, {
|
||||||
|
label: 'Temperature',
|
||||||
|
data: temp,
|
||||||
|
borderColor: tempColor,
|
||||||
|
fill: false,
|
||||||
|
yAxisID: 'y-axis-2',
|
||||||
|
}, {
|
||||||
|
label: 'Co2 Concentration',
|
||||||
|
data: co2,
|
||||||
|
borderColor: co2Color,
|
||||||
|
fill: false,
|
||||||
|
yAxisID: 'y-axis-1',
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
stacked: false,
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Ledda\'s Room Climate',
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
xAxes: [{
|
||||||
|
type: 'time',
|
||||||
|
time: {
|
||||||
|
unit: 'second'
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
yAxes: [{
|
||||||
|
type: 'linear',
|
||||||
|
display: true,
|
||||||
|
position: 'right',
|
||||||
|
id: 'y-axis-1',
|
||||||
|
ticks: {
|
||||||
|
fontColor: co2Color,
|
||||||
|
min: 300,
|
||||||
|
max: 1200,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
type: 'linear',
|
||||||
|
display: true,
|
||||||
|
position: 'left',
|
||||||
|
id: 'y-axis-2',
|
||||||
|
ticks: {
|
||||||
|
fontColor: tempColor,
|
||||||
|
min: 5,
|
||||||
|
max: 40,
|
||||||
|
},
|
||||||
|
gridLines: {
|
||||||
|
drawOnChartArea: false,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
type: 'linear',
|
||||||
|
display: true,
|
||||||
|
position: 'left',
|
||||||
|
id: 'y-axis-3',
|
||||||
|
ticks: {
|
||||||
|
fontColor: humidityColor,
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
},
|
||||||
|
gridLines: {
|
||||||
|
drawOnChartArea: false,
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformData(json) {
|
||||||
|
const humidity = [];
|
||||||
|
const co2 = [];
|
||||||
|
const temp = [];
|
||||||
|
for (const snapshot of json.snapshots) {
|
||||||
|
co2.push({x: snapshot.time, y: snapshot.co2});
|
||||||
|
temp.push({x: snapshot.time, y: snapshot.temp});
|
||||||
|
humidity.push({x: snapshot.time, y: snapshot.humidity});
|
||||||
|
}
|
||||||
|
return {humidity, co2, temp};
|
||||||
|
}
|
||||||
|
|
||||||
|
initChart();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
@@ -31,40 +32,57 @@ func teardown() {
|
|||||||
|
|
||||||
func startServer() {
|
func startServer() {
|
||||||
port := "8001"
|
port := "8001"
|
||||||
MainRouter.setGet("/", showCharts)
|
r := mux.NewRouter()
|
||||||
MainRouter.setGet("/data/", sendData)
|
r.HandleFunc("/", showCharts).Methods("GET")
|
||||||
MainRouter.setPost("/", saveSnapshot)
|
r.HandleFunc("/data", sendData).Methods("GET")
|
||||||
|
r.HandleFunc("/", saveSnapshot).Methods("POST")
|
||||||
|
http.Handle("/", r)
|
||||||
fmt.Printf("Listening on port %s...\n", port)
|
fmt.Printf("Listening on port %s...\n", port)
|
||||||
log.Fatal(http.ListenAndServe(":" + port, nil))
|
log.Fatal(http.ListenAndServe(":"+port, nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
func showCharts(w http.ResponseWriter, r *http.Request) error {
|
func showCharts(w http.ResponseWriter, r *http.Request) {
|
||||||
_, err := fmt.Fprint(w, "<h1>Climate Stuff</h1><div>The data will show up here at some stage...</div>")
|
http.ServeFile(w, r, "charts.html")
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendData(w http.ResponseWriter, r *http.Request) error {
|
func sendData(w http.ResponseWriter, r *http.Request) {
|
||||||
records, err := getSnapshotRecordsFromDb(50)
|
records, err := getSnapshotRecordsFromDb(50)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("couldn't read rows from the database: %w", err)
|
sendInternalError(fmt.Errorf("couldn't read rows from the database: %w", err), w, r)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
json, err := createJsonFromSnapshotRecords(records)
|
json, err := createJsonFromSnapshotRecords(records)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("couldn't create a json from the records: %w", err)
|
sendInternalError(fmt.Errorf("couldn't create a json from the records: %w", err), w, r)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
_, err = fmt.Fprintf(w, string(json))
|
_, err = fmt.Fprintf(w, string(json))
|
||||||
return err
|
if err != nil {
|
||||||
|
sendInternalError(err, w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveSnapshot(w http.ResponseWriter, r *http.Request) error {
|
func saveSnapshot(w http.ResponseWriter, r *http.Request) {
|
||||||
snapshotSub, err := createSnapshotSubFromJsonBodyStream(r.Body)
|
snapshotSub, err := createSnapshotSubFromJsonBodyStream(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("couldn't create snapshot from JSON: %w", err)
|
sendInternalError(fmt.Errorf("couldn't create snapshot from JSON: %w", err), w, r)
|
||||||
}
|
}
|
||||||
err = writeSnapshotToDb(snapshotSub)
|
err = writeSnapshotToDb(snapshotSub)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("couldn't submit snapshot into the database: %w", err)
|
sendInternalError(fmt.Errorf("couldn't submit snapshot into the database: %w", err), w, r)
|
||||||
}
|
}
|
||||||
return nil
|
}
|
||||||
|
|
||||||
|
func sendInternalError(err error, w http.ResponseWriter, r *http.Request) {
|
||||||
|
errorMessage := "Internal Server Error!"
|
||||||
|
if DEBUG {
|
||||||
|
errorMessage += fmt.Sprintf(" Happened during %s request for pattern '%s': %s",
|
||||||
|
r.Method,
|
||||||
|
r.URL,
|
||||||
|
err.Error())
|
||||||
|
}
|
||||||
|
fmt.Println(errorMessage)
|
||||||
|
http.Error(w, errorMessage, 500)
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user