Refactor the controller-server format a bit; also add in some protection to prevent overrun issues

This commit is contained in:
Kelvin Ly 2023-05-16 10:19:16 -04:00
parent 09504e9ec5
commit 5bb945e875
4 changed files with 73 additions and 42 deletions

View File

@ -64,9 +64,10 @@ function initCharts() {
}) })
} }
var cur_time_millis = 0
var max_interval_millis = 5*60*1000 var max_interval_millis = 5*60*1000
var decimation_rate = 1
var cur_time_millis = 0
var time = [] var time = []
var temp = [] var temp = []
var humd = [] var humd = []

View File

@ -1,13 +1,23 @@
import numpy as np import numpy as np
import json import json
import os
import serial import serial
import subprocess import subprocess
import threading import threading
import time import time
#process = subprocess.Popen(["ssh", "shrooms@localhost", "/usr/bin/env", "python", "/home/shrooms/go/src/shroom-server/shroom-pipe.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE) SERIAL_PATH = "/dev/ttyUSB0"
process = subprocess.Popen(["/usr/bin/env", "python", "/home/kelvin/src/shroom-server/shroom_pipe.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE) SERIAL_BAUD = 115200
SAMPLE_PERIOD = 0.2
DECIMATION_RATE = 10
is_mock = os.environ['MOCK']
if is_mock:
process = subprocess.Popen(["/usr/bin/env", "python", "/home/kelvin/src/shroom-server/shroom_pipe.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
else:
process = subprocess.Popen(["ssh", "shrooms@threefortiethofonehamster.com", "/usr/bin/env", "python", "/home/shrooms/go/src/shroom-server/shroom-pipe.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
def send_update(msg): def send_update(msg):
global process global process
@ -46,10 +56,10 @@ class MockSerial:
self.humidity[-1] = 0.2*0.20 + 0.8*self.humidity[-2] self.humidity[-1] = 0.2*0.20 + 0.8*self.humidity[-2]
self.humidity[0] = 0.20 self.humidity[0] = 0.20
if self.humidifier_on: if self.humidifier_on:
self.humidity[20] = 1.5 self.humidity[20] = 2
# use the gradient to determine the change in humidity # use the gradient to determine the change in humidity
avg = 0.5*(self.humidity[:-2] + self.humidity[2:]) avg = 0.5*(self.humidity[:-2] + self.humidity[2:])
self.humidity[1:-1] += 0.2*(avg - self.humidity[1:-1]) self.humidity[1:-1] += 0.10*(avg - self.humidity[1:-1])
#print(self.humidity) #print(self.humidity)
humidity = self.humidity[60] + np.random.random()*0.003 humidity = self.humidity[60] + np.random.random()*0.003
@ -59,10 +69,15 @@ class MockSerial:
hv = 0.0 hv = 0.0
return bytes("{},{},{}\n".format(humidity, temp, hv), "utf8") return bytes("{},{},{}\n".format(humidity, temp, hv), "utf8")
s = MockSerial() if is_mock:
s = MockSerial()
else:
s = serial.Serial(SERIAL_PATH, SERIAL_BAUD, timeout = 10)
def reset_serial(): def reset_serial():
pass if not is_mock:
s.close()
s = serial.Serial(SERIAL_PATH, SERIAL_BAUD, timeout = 10)
time.sleep(10) time.sleep(10)
class Humidifier: class Humidifier:
@ -84,13 +99,13 @@ class Humidifier:
if avg < 0.2: if avg < 0.2:
self.on = False self.on = False
print("send status off") print("send status off")
send_update({"status": 0}) send_update({"status": {"humidifier": False}})
self.switch_timeout = time.time() + 1 self.switch_timeout = time.time() + 1
else: else:
if avg > 2.6: if avg > 2.6:
self.on = True self.on = True
print("send status on") print("send status on")
send_update({"status": 1}) send_update({"status": {"humidifier": True}})
self.switch_timeout = time.time() + 1 self.switch_timeout = time.time() + 1
def toggle(self, s): def toggle(self, s):
@ -105,12 +120,13 @@ feedforward_coeff = 50
humidifier_history = np.zeros(30) humidifier_history = np.zeros(30)
first_sample = False first_sample = False
frame_num = 0
try: try:
last_sample = 0 last_sample = 0
while True: while True:
now = time.time() now = time.time()
if now - last_sample < 0.5: if now - last_sample < SAMPLE_PERIOD:
time.sleep(0.5 - (now - last_sample)) time.sleep(SAMPLE_PERIOD - (now - last_sample) + 0.001)
continue continue
last_sample = now last_sample = now
@ -144,13 +160,17 @@ try:
elif comp_humidity > target_upper and humidifier.on: elif comp_humidity > target_upper and humidifier.on:
humidifier.toggle(s) humidifier.toggle(s)
update = { if frame_num == 0:
"time": int(now*1000), update = {
"temp": temp, "data": {
"hum": humidity, "time": int(now*1000),
"hv": volts "temp": temp,
} "hum": humidity,
send_update(update) "hv": volts
}
}
send_update(update)
frame_num = (frame_num + 1) % DECIMATION_RATE
except Exception as e: except Exception as e:
print("pipe errored out, restarting: ", e) print("pipe errored out, restarting: ", e)
# restart the process I guess # restart the process I guess

View File

@ -66,8 +66,8 @@ func LatestTime(db *sql.DB) (int64, error) {
return t, nil return t, nil
} }
func InsertRow(db *sql.DB, s *ShroomData) error { func InsertRow(db *sql.DB, s *DataJson) error {
_, err := db.Exec("INSERT INTO shrooms (time, temperature, humidity, humidifier_volts) VALUES (?, ?, ?, ?)", _, err := db.Exec("INSERT INTO shrooms (time, temperature, humidity, humidifier_volts) VALUES (?, ?, ?, ?)",
s.Time, s.Temperature, s.Humidity, s.HumidifierVolts) *s.Time, *s.Temperature, *s.Humidity, *s.HumidifierVolts)
return err return err
} }

View File

@ -17,12 +17,20 @@ func newlinePos(s []byte) int {
return -1 return -1
} }
type ShroomData struct { type StatusJson struct {
Time uint64 `json:"time"` HumOn *bool `json:"humidifier"`
Temperature float32 `json:"temp"` }
Humidity float32 `json:"hum"`
HumidifierVolts float32 `json:"hv"` type DataJson struct {
Status int32 `json:"status"` Time *uint64 `json:"time"`
Temperature *float32 `json:"temp"`
Humidity *float32 `json:"hum"`
HumidifierVolts *float32 `json:"hv"`
}
type ShroomPacket struct {
Data *DataJson `json:"data"`
Status *StatusJson `json:"status"`
} }
type ShroomStatus struct { type ShroomStatus struct {
@ -50,14 +58,8 @@ func (s *ShroomStatus) StatusUpdate() {
} }
func parseMsg(line []byte, db *sql.DB, status *ShroomStatus) { func parseMsg(line []byte, db *sql.DB, status *ShroomStatus) {
data := ShroomData{ packet := ShroomPacket{}
Time: 0, err := json.Unmarshal(line, &packet)
Temperature: -274,
Humidity: -1,
HumidifierVolts: -1,
Status: -1,
}
err := json.Unmarshal(line, &data)
if err != nil { if err != nil {
log.Println("unable to parse tcp line: ", err) log.Println("unable to parse tcp line: ", err)
log.Println(string(line)) log.Println(string(line))
@ -65,18 +67,22 @@ func parseMsg(line []byte, db *sql.DB, status *ShroomStatus) {
return return
} }
//log.Println("received data ", data) //log.Println("received data ", data)
if data.Time > 0 && data.Temperature > -275 && data.Humidity > -1 && data.HumidifierVolts > -1 { if packet.Data != nil {
err = InsertRow(db, &data) if packet.Data.Time != nil && packet.Data.Temperature != nil && packet.Data.Humidity != nil && packet.Data.HumidifierVolts != nil {
if err != nil { err = InsertRow(db, packet.Data)
log.Println("unable to write to database: ", err) if err != nil {
log.Println("unable to write to database: ", err)
}
// we got a data packet
status.Update()
} }
// we got a data packet } else if packet.Status != nil {
status.Update()
} else if data.Status != -1 {
//log.Println("received status ", data.Status) //log.Println("received status ", data.Status)
status.Lock() status.Lock()
// TODO change to have more detailed data // TODO change to have more detailed data
status.HumidifierOn = (data.Status & 1) == 1 if packet.Status.HumOn != nil {
status.HumidifierOn = *packet.Status.HumOn
}
status.Unlock() status.Unlock()
status.StatusUpdate() status.StatusUpdate()
} else { } else {
@ -153,6 +159,10 @@ func InitTcpServer(db *sql.DB, status *ShroomStatus) {
left = left[num_read:] left = left[num_read:]
//log.Println("buf ", buf) //log.Println("buf ", buf)
//log.Println("left ", left) //log.Println("left ", left)
if len(left) == 0 {
log.Println("overflow detected, truncating data")
left = buf
}
if err != nil { if err != nil {
log.Println("tcp read error: ", err) log.Println("tcp read error: ", err)