Test out admin mode commands; it looks like the basic idea works for now at least

This commit is contained in:
Kelvin Ly 2023-05-15 16:38:37 -04:00
parent f1723ff5bd
commit 3a7bb60360
3 changed files with 98 additions and 7 deletions

View File

@ -166,11 +166,12 @@ async function updateCharts() {
}
const sleep = (ms) => new Promise(r => setTimeout(r, ms))
var chart_update_millis = 2000
async function chartupdater() {
if (chart_updater != null) return
await sleep(2000)
await sleep(chart_update_millis)
// wait at least two seconds to avoid wasting a lot of bandwidth
const resp = await fetch("/api/update")
chart_updater = null
@ -200,6 +201,19 @@ async function waitThenUpdateStatus() {
updateStatus()
}
async function testAdminMode() {
const msg = JSON.stringify({
auth: "password",
data: {
manual_mode: true
}
})
await fetch("/api/admin", {
method: "POST",
body: msg
})
}
window.onload = () => {
initCharts()
updateCharts()
@ -212,6 +226,9 @@ window.onload = () => {
document.getElementById('autoupdate').addEventListener('click', (e) => {
autoupdate = document.getElementById('autoupdate').checked
})
document.getElementById('test-admin').addEventListener('click', (e) => {
testAdminMode()
})
}
</script>
</head>
@ -223,6 +240,7 @@ window.onload = () => {
<form>
<input type=checkbox id=autoupdate checked>Autoupdate</input>
<input type=button id=update value="Update"></input>
<input type=button id=test-admin value="Test admin mode"></input>
<!-- TODO add decimation and window size options -->
<span id=status></span>
</form>

View File

@ -31,6 +31,8 @@ type ShroomStatus struct {
NumConnections int
Wait chan struct{}
StatusWait chan struct{}
Commands chan []byte
}
func (s *ShroomStatus) Update() {
@ -71,7 +73,7 @@ func parseMsg(line []byte, db *sql.DB, status *ShroomStatus) {
// we got a data packet
status.Update()
} else if data.Status != -1 {
log.Println("received status ", data.Status)
//log.Println("received status ", data.Status)
status.Lock()
// TODO change to have more detailed data
status.HumidifierOn = (data.Status & 1) == 1
@ -96,6 +98,12 @@ func InitTcpServer(db *sql.DB, status *ShroomStatus) {
log.Println("tcp accept error: ", err)
return
}
// not spawning a goroutine here
// should limit the number of connections to
// one hopefully
// wrapping in a func() so that I can use defer
// to automatically decrement the number of connections
func() {
status.Lock()
status.NumConnections += 1
@ -112,8 +120,27 @@ func InitTcpServer(db *sql.DB, status *ShroomStatus) {
log.Println("connection started")
// write loop; waits for commands and forwards them
exiting := make(chan struct{})
go func() {
// TODO deal with the write side of the connection
for {
select {
case v, ok := <-status.Commands:
if !ok {
return
}
v = append(v, []byte("\n")...)
_, err := conn.Write(v)
if err != nil {
log.Println("tcp write err: ", err)
}
case <-exiting:
return
}
}
}()
defer func() {
close(exiting)
}()
// deal with the read side of the connection

View File

@ -29,6 +29,11 @@ type statusJson struct {
Humidifier bool `json:"humidifier"`
}
type adminMsg struct {
Auth string `json:"auth"`
Msg map[string]interface{} `json:"data"`
}
func dumpData(db *sql.DB, multiplier int64) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, req *http.Request) {
now := time.Now().Unix()
@ -70,6 +75,7 @@ func main() {
status := s.ShroomStatus{
Wait: make(chan struct{}),
StatusWait: make(chan struct{}),
Commands: make(chan []byte),
}
s.InitTcpServer(db, &status)
@ -106,7 +112,7 @@ func main() {
msg, err := json.Marshal(s)
if err != nil {
err = fmt.Errorf("unable to marshal json: %w", err)
w.WriteHeader(500)
w.WriteHeader(400)
w.Write([]byte(err.Error()))
} else {
w.Write(msg)
@ -114,9 +120,49 @@ func main() {
}
adminHandler := func(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(500)
w.Write([]byte("unimplemented"))
// TODO
if req.Method != "POST" {
w.WriteHeader(405)
w.Write([]byte("method must be POST"))
return
}
msg := make([]byte, 256)
l, err := req.Body.Read(msg)
if err != nil && l == 0 {
err = fmt.Errorf("unable to read body: %w", err)
w.WriteHeader(400)
w.Write([]byte(err.Error()))
return
}
adminReq := adminMsg{}
err = json.Unmarshal(msg[:l], &adminReq)
if err != nil {
err = fmt.Errorf("unable to unmarshal body as json: %w", err)
w.WriteHeader(400)
w.Write([]byte(err.Error()))
return
}
// TODO switch to embedded secret
if adminReq.Auth != "password" {
w.WriteHeader(401)
w.Write([]byte(err.Error()))
return
}
inner_msg, err := json.Marshal(adminReq.Msg)
if err != nil {
err = fmt.Errorf("unable to marshal inner message: %w", err)
w.WriteHeader(400)
w.Write([]byte(err.Error()))
return
}
select {
case status.Commands <- inner_msg:
w.Write([]byte("ok"))
default:
w.WriteHeader(503)
w.Write([]byte("unable to forward request; controller may not be connected"))
}
}
updateHandler := func(w http.ResponseWriter, req *http.Request) {