Lots of small modifications and refactors

This commit is contained in:
Kelvin Ly 2023-05-14 15:23:47 -04:00
parent 928692334c
commit 25d3788478
5 changed files with 154 additions and 59 deletions

View File

@ -5,11 +5,61 @@
<link href="/c3.css" rel="stylesheet" type=text/css>
<!-- 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>
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>
<body>
<div id="chart"></div>
<div id="humidity-temp"></div>
<div id="volts"></div>
<p>Test!</p>
</body>
</html>

View File

@ -65,3 +65,9 @@ func LatestTime(db *sql.DB) (int64, error) {
}
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
}

View File

@ -18,26 +18,63 @@ func newlinePos(s []byte) int {
}
type ShroomData struct {
Time uint64
Temperature float32
Humidity float32
HumidifierVolts float32
Status uint32
}
type ShroomStatusData struct {
Status uint32
NumConnections int
Time uint64 `json:"time"`
Temperature float32 `json:"temp"`
Humidity float32 `json:"hum"`
HumidifierVolts float32 `json:"hv"`
Status uint32 `json:"status"`
}
type ShroomStatus struct {
Status uint32
sync.RWMutex
HumidifierOn bool
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) {
// 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")
if err != nil {
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)
continue
}
status.Lock.Lock()
status.Lock()
status.NumConnections += 1
status.Lock.Unlock()
status.Unlock()
defer func() {
status.Lock.Lock()
status.Lock()
status.NumConnections -= 1
status.Lock.Unlock()
status.Unlock()
log.Println("connection disconnected")
}()
@ -96,35 +133,8 @@ func InitTcpServer(db *sql.DB, status *ShroomStatus) {
if len(line) == 0 {
continue
}
data := ShroomData{
Time: 0,
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)
}
parseMsg(line, db, status)
}
// shift the remaining data back to the start of the buffer
copy(buf[:len(unread)], unread)

View File

@ -11,6 +11,8 @@ import (
import (
"database/sql"
"embed"
"encoding/json"
"fmt"
"io/fs"
"log"
"net/http"
@ -20,8 +22,9 @@ import (
//go:embed static/*
var content embed.FS
type commandDispatcher struct {
// TODO
type statusJson struct {
Connected bool `json:"connected"`
Humidifier bool `json:"humidifier"`
}
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)
}
status := s.ShroomStatus{}
status := s.ShroomStatus{
Wait: make(chan struct{}),
}
s.InitTcpServer(db, &status)
contentSub, err := fs.Sub(content, "static")
@ -63,6 +68,7 @@ func main() {
dumpWeek := dumpData(db, -7*24*60*60)
dumpDay := dumpData(db, -24*60*60)
dumpHour := dumpData(db, -60*60)
dumpMinute := dumpData(db, -60)
lastPoint := func(w http.ResponseWriter, _req *http.Request) {
msg, err := s.LastTime(db)
@ -75,27 +81,48 @@ func main() {
}
getStatus := func(w http.ResponseWriter, _req *http.Request) {
status.Lock.RLock()
status.RLock()
num_connections := status.NumConnections
status.Lock.RUnlock()
if num_connections > 0 {
w.Write([]byte("foo"))
humidifier := status.HumidifierOn
status.RUnlock()
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) {
w.WriteHeader(500)
w.Write([]byte("unimplemented"))
// 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("/", http.FileServer(http.FS(contentSub)))
http.HandleFunc("/api/data/week", dumpWeek)
http.HandleFunc("/api/data/day", dumpDay)
http.HandleFunc("/api/data/hour", dumpHour)
http.HandleFunc("/api/last/week", dumpWeek)
http.HandleFunc("/api/last/day", dumpDay)
http.HandleFunc("/api/last/hour", dumpHour)
http.HandleFunc("/api/last/minute", dumpMinute)
http.HandleFunc("/api/latest", lastPoint)
http.HandleFunc("/api/status", getStatus)
http.HandleFunc("/api/admin", adminHandler)
http.HandleFunc("/api/update", updateHandler)
// TODO periodically clear old entries from the database

2
static/d3.v5.min.js vendored Normal file

File diff suppressed because one or more lines are too long