Lots of small modifications and refactors
This commit is contained in:
parent
928692334c
commit
25d3788478
54
dev/dev.htm
54
dev/dev.htm
|
@ -5,11 +5,61 @@
|
||||||
<link href="/c3.css" rel="stylesheet" type=text/css>
|
<link href="/c3.css" rel="stylesheet" type=text/css>
|
||||||
|
|
||||||
<!-- Load d3.js and c3.js -->
|
<!-- Load d3.js and c3.js -->
|
||||||
<script src="/d3.v7.min.js" charset="utf-8"></script>
|
<script src="/d3.v5.min.js" charset="utf-8"></script>
|
||||||
<script src="/c3.min.js"></script>
|
<script src="/c3.min.js"></script>
|
||||||
|
<script>
|
||||||
|
var chart = null
|
||||||
|
var chart2 = null
|
||||||
|
function initCharts() {
|
||||||
|
chart = c3.generate({
|
||||||
|
bindto: '#humidity-temp',
|
||||||
|
data: {
|
||||||
|
x: 'x',
|
||||||
|
columns: [
|
||||||
|
['x', 1, 2, 3, 4],
|
||||||
|
['temp', 24, 24.5, 24.3, 24.6],
|
||||||
|
['humidity', 83.1, 99.5, 74.3, 84.6],
|
||||||
|
],
|
||||||
|
axes: {
|
||||||
|
temp: 'y',
|
||||||
|
humidity: 'y2'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axis: {
|
||||||
|
y2: {
|
||||||
|
show: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
zoom: {
|
||||||
|
enabled: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
chart2 = c3.generate({
|
||||||
|
bindto: '#volts',
|
||||||
|
data: {
|
||||||
|
x: 'x',
|
||||||
|
columns: [
|
||||||
|
['x', 1, 2, 3, 4],
|
||||||
|
['voltage', 24, 24.5, 24.3, 24.6],
|
||||||
|
],
|
||||||
|
axes: {
|
||||||
|
voltage: 'y',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
zoom: {
|
||||||
|
enabled: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
window.onload = () => {
|
||||||
|
initCharts()
|
||||||
|
const xhr = new XmlHttpRequest()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="chart"></div>
|
<div id="humidity-temp"></div>
|
||||||
|
<div id="volts"></div>
|
||||||
<p>Test!</p>
|
<p>Test!</p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -65,3 +65,9 @@ func LatestTime(db *sql.DB) (int64, error) {
|
||||||
}
|
}
|
||||||
return t, nil
|
return t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func InsertRow(db *sql.DB, s *ShroomData) error {
|
||||||
|
_, err := db.Exec("INSERT INTO shrooms (time, temperature, humidity, humidifier_volts) VALUES (?, ?, ?, ?)",
|
||||||
|
s.Time, s.Temperature, s.Humidity, s.HumidifierVolts)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
|
@ -18,26 +18,63 @@ func newlinePos(s []byte) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ShroomData struct {
|
type ShroomData struct {
|
||||||
Time uint64
|
Time uint64 `json:"time"`
|
||||||
Temperature float32
|
Temperature float32 `json:"temp"`
|
||||||
Humidity float32
|
Humidity float32 `json:"hum"`
|
||||||
HumidifierVolts float32
|
HumidifierVolts float32 `json:"hv"`
|
||||||
Status uint32
|
Status uint32 `json:"status"`
|
||||||
}
|
|
||||||
|
|
||||||
type ShroomStatusData struct {
|
|
||||||
Status uint32
|
|
||||||
NumConnections int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ShroomStatus struct {
|
type ShroomStatus struct {
|
||||||
Status uint32
|
sync.RWMutex
|
||||||
|
HumidifierOn bool
|
||||||
NumConnections int
|
NumConnections int
|
||||||
Lock sync.RWMutex
|
Wait chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ShroomStatus) Update() {
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
close(s.Wait)
|
||||||
|
s.Wait = make(chan struct{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseMsg(line []byte, db *sql.DB, status *ShroomStatus) {
|
||||||
|
data := ShroomData{
|
||||||
|
Time: 0,
|
||||||
|
Temperature: -1,
|
||||||
|
Humidity: -1,
|
||||||
|
HumidifierVolts: -1,
|
||||||
|
Status: 0,
|
||||||
|
}
|
||||||
|
err := json.Unmarshal(line, &data)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("unable to parse tcp line: ", err)
|
||||||
|
log.Println(string(line))
|
||||||
|
log.Println(line)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if data.Time > 0 && data.Temperature > 0 && data.Humidity > 0 && data.HumidifierVolts > 0 {
|
||||||
|
err = InsertRow(db, &data)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("unable to write to database: ", err)
|
||||||
|
}
|
||||||
|
// we got a data packet
|
||||||
|
status.Update()
|
||||||
|
} else if data.Status > 0 {
|
||||||
|
status.Lock()
|
||||||
|
// TODO change to have more detailed data
|
||||||
|
status.HumidifierOn = (data.Status & 1) == 1
|
||||||
|
status.Unlock()
|
||||||
|
status.Update()
|
||||||
|
} else {
|
||||||
|
log.Println("unknown packet: ", line)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitTcpServer(db *sql.DB, status *ShroomStatus) {
|
func InitTcpServer(db *sql.DB, status *ShroomStatus) {
|
||||||
// TODO start TCP server for the pipe from the raspberry pi
|
// start TCP server for the pipe from the raspberry pi
|
||||||
ln, err := net.Listen("tcp", ":9876")
|
ln, err := net.Listen("tcp", ":9876")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("unable to open tcp server: ", err)
|
log.Fatal("unable to open tcp server: ", err)
|
||||||
|
@ -49,14 +86,14 @@ func InitTcpServer(db *sql.DB, status *ShroomStatus) {
|
||||||
log.Println("tcp accept error: ", err)
|
log.Println("tcp accept error: ", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
status.Lock.Lock()
|
status.Lock()
|
||||||
status.NumConnections += 1
|
status.NumConnections += 1
|
||||||
status.Lock.Unlock()
|
status.Unlock()
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
status.Lock.Lock()
|
status.Lock()
|
||||||
status.NumConnections -= 1
|
status.NumConnections -= 1
|
||||||
status.Lock.Unlock()
|
status.Unlock()
|
||||||
log.Println("connection disconnected")
|
log.Println("connection disconnected")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -96,35 +133,8 @@ func InitTcpServer(db *sql.DB, status *ShroomStatus) {
|
||||||
if len(line) == 0 {
|
if len(line) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
data := ShroomData{
|
|
||||||
Time: 0,
|
parseMsg(line, db, status)
|
||||||
Temperature: -1,
|
|
||||||
Humidity: -1,
|
|
||||||
HumidifierVolts: -1,
|
|
||||||
Status: 0,
|
|
||||||
}
|
|
||||||
err := json.Unmarshal(line, &data)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("unable to read humdifier data: ", err)
|
|
||||||
log.Println(string(line))
|
|
||||||
log.Println(line)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if data.Time > 0 && data.Temperature > 0 && data.Humidity > 0 && data.HumidifierVolts > 0 {
|
|
||||||
// we got a data packet
|
|
||||||
_, err = db.Exec("INSERT INTO shrooms (time, temperature, humidity, humidifier_volts) VALUES (?, ?, ?, ?)",
|
|
||||||
data.Time, data.Temperature, data.Humidity, data.HumidifierVolts)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("unable to write to database: ", err)
|
|
||||||
}
|
|
||||||
} else if data.Status > 0 {
|
|
||||||
status.Lock.Lock()
|
|
||||||
// TODO change to have more detailed data
|
|
||||||
status.Status = data.Status
|
|
||||||
status.Lock.Unlock()
|
|
||||||
} else {
|
|
||||||
log.Println("unknown packet: ", line)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// shift the remaining data back to the start of the buffer
|
// shift the remaining data back to the start of the buffer
|
||||||
copy(buf[:len(unread)], unread)
|
copy(buf[:len(unread)], unread)
|
||||||
|
|
|
@ -11,6 +11,8 @@ import (
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"embed"
|
"embed"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -20,8 +22,9 @@ import (
|
||||||
//go:embed static/*
|
//go:embed static/*
|
||||||
var content embed.FS
|
var content embed.FS
|
||||||
|
|
||||||
type commandDispatcher struct {
|
type statusJson struct {
|
||||||
// TODO
|
Connected bool `json:"connected"`
|
||||||
|
Humidifier bool `json:"humidifier"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func dumpData(db *sql.DB, offset int64) func(http.ResponseWriter, *http.Request) {
|
func dumpData(db *sql.DB, offset int64) func(http.ResponseWriter, *http.Request) {
|
||||||
|
@ -52,7 +55,9 @@ func main() {
|
||||||
log.Fatal("unable to create table ", err)
|
log.Fatal("unable to create table ", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
status := s.ShroomStatus{}
|
status := s.ShroomStatus{
|
||||||
|
Wait: make(chan struct{}),
|
||||||
|
}
|
||||||
s.InitTcpServer(db, &status)
|
s.InitTcpServer(db, &status)
|
||||||
|
|
||||||
contentSub, err := fs.Sub(content, "static")
|
contentSub, err := fs.Sub(content, "static")
|
||||||
|
@ -63,6 +68,7 @@ func main() {
|
||||||
dumpWeek := dumpData(db, -7*24*60*60)
|
dumpWeek := dumpData(db, -7*24*60*60)
|
||||||
dumpDay := dumpData(db, -24*60*60)
|
dumpDay := dumpData(db, -24*60*60)
|
||||||
dumpHour := dumpData(db, -60*60)
|
dumpHour := dumpData(db, -60*60)
|
||||||
|
dumpMinute := dumpData(db, -60)
|
||||||
|
|
||||||
lastPoint := func(w http.ResponseWriter, _req *http.Request) {
|
lastPoint := func(w http.ResponseWriter, _req *http.Request) {
|
||||||
msg, err := s.LastTime(db)
|
msg, err := s.LastTime(db)
|
||||||
|
@ -75,27 +81,48 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
getStatus := func(w http.ResponseWriter, _req *http.Request) {
|
getStatus := func(w http.ResponseWriter, _req *http.Request) {
|
||||||
status.Lock.RLock()
|
status.RLock()
|
||||||
num_connections := status.NumConnections
|
num_connections := status.NumConnections
|
||||||
status.Lock.RUnlock()
|
humidifier := status.HumidifierOn
|
||||||
if num_connections > 0 {
|
status.RUnlock()
|
||||||
w.Write([]byte("foo"))
|
s := statusJson{
|
||||||
|
Connected: num_connections > 0,
|
||||||
|
Humidifier: humidifier,
|
||||||
|
}
|
||||||
|
msg, err := json.Marshal(s)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("unable to marshal json: %w", err)
|
||||||
|
w.WriteHeader(500)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
} else {
|
||||||
|
w.Write(msg)
|
||||||
}
|
}
|
||||||
// TODO
|
|
||||||
}
|
}
|
||||||
|
|
||||||
adminHandler := func(w http.ResponseWriter, req *http.Request) {
|
adminHandler := func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
w.WriteHeader(500)
|
||||||
|
w.Write([]byte("unimplemented"))
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateHandler := func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
stillopen := true
|
||||||
|
for stillopen {
|
||||||
|
_, stillopen = <-status.Wait
|
||||||
|
}
|
||||||
|
w.Write([]byte("ok"))
|
||||||
|
}
|
||||||
|
|
||||||
http.Handle("/d/", http.StripPrefix("/d/", http.FileServer(http.Dir("./dev"))))
|
http.Handle("/d/", http.StripPrefix("/d/", http.FileServer(http.Dir("./dev"))))
|
||||||
http.Handle("/", http.FileServer(http.FS(contentSub)))
|
http.Handle("/", http.FileServer(http.FS(contentSub)))
|
||||||
http.HandleFunc("/api/data/week", dumpWeek)
|
http.HandleFunc("/api/last/week", dumpWeek)
|
||||||
http.HandleFunc("/api/data/day", dumpDay)
|
http.HandleFunc("/api/last/day", dumpDay)
|
||||||
http.HandleFunc("/api/data/hour", dumpHour)
|
http.HandleFunc("/api/last/hour", dumpHour)
|
||||||
|
http.HandleFunc("/api/last/minute", dumpMinute)
|
||||||
http.HandleFunc("/api/latest", lastPoint)
|
http.HandleFunc("/api/latest", lastPoint)
|
||||||
http.HandleFunc("/api/status", getStatus)
|
http.HandleFunc("/api/status", getStatus)
|
||||||
http.HandleFunc("/api/admin", adminHandler)
|
http.HandleFunc("/api/admin", adminHandler)
|
||||||
|
http.HandleFunc("/api/update", updateHandler)
|
||||||
|
|
||||||
// TODO periodically clear old entries from the database
|
// TODO periodically clear old entries from the database
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue