Skip to content

Commit

Permalink
Allow restarting a sensor from web UI
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewmarklloyd committed Jun 10, 2021
1 parent 03678ad commit cefdba1
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 15 deletions.
7 changes: 7 additions & 0 deletions client/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ func main() {

configureHeartbeat(mqttClient, *sensorSource)

mqttClient.subscribeRestart(func(messageString string) {
if *sensorSource == messageString {
logger.Println("Received restart message, restarting app now")
os.Exit(0)
}
})

lastStatus := UNKNOWN
var currentStatus string
for true {
Expand Down
15 changes: 15 additions & 0 deletions client/mqtt.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ package main
import (
"fmt"
"strconv"
"sync"

mqtt "github.com/eclipse/paho.mqtt.golang"
"github.com/gofrs/uuid"
)

type fn func(string)

const (
sensorHeartbeatChannel = "sensor/heartbeat"
restartTopic = "sensor/restart"
)

var connectHandler mqtt.OnConnectHandler = func(client mqtt.Client) {
Expand Down Expand Up @@ -66,3 +70,14 @@ func (c mqttClient) publishHeartbeat(sensorSource string, timestamp int64) {
token := c.client.Publish(sensorHeartbeatChannel, 0, false, text)
token.Wait()
}

func (c mqttClient) subscribeRestart(subscribeHandler fn) {
var wg sync.WaitGroup
wg.Add(1)

if token := c.client.Subscribe(restartTopic, 0, func(client mqtt.Client, msg mqtt.Message) {
subscribeHandler(string(msg.Payload()))
}); token.Wait() && token.Error() != nil {
logger.Fatal(token.Error())
}
}
16 changes: 16 additions & 0 deletions server/frontend/src/SensorPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@ class SensorPage extends Component {
this.state = this.props.location.state
}

restartSensor(source) {
fetch("/api/sensor/restart", {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json'
},
referrerPolicy: 'no-referrer',
body: JSON.stringify({source: source})
})
.then(r => r.json())
}

render() {
return (
<SiteWrapper>
Expand All @@ -25,6 +38,9 @@ class SensorPage extends Component {
</Card.Header>
<Card.Body>
<p>Last activity: {this.state.timesince}</p>
<button onClick={() => this.restartSensor(this.state.source)}>
Restart
</button>
</Card.Body>
</Card>
<Link to={{pathname: "/"}}><Button color="secondary">Back</Button></Link>
Expand Down
26 changes: 22 additions & 4 deletions server/main.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package main

import (
"encoding/json"
"flag"
"fmt"
"log"
"net/http"
"os"
"strconv"
"time"
Expand Down Expand Up @@ -38,6 +40,7 @@ const (

var _webServer webServer
var _redisClient redisClient
var _mqttClient mqttClient

func newClientHandler() {
state, err := _redisClient.ReadAllState()
Expand All @@ -48,6 +51,21 @@ func newClientHandler() {
}
}

type Sensor struct {
Source string
}

func sensorRestartHandler(w http.ResponseWriter, req *http.Request) {
var sensor Sensor
err := json.NewDecoder(req.Body).Decode(&sensor)
if err != nil {
http.Error(w, "Error parsing request", http.StatusBadRequest)
return
}
_mqttClient.publishSensorRestart(sensor.Source)
fmt.Fprintf(w, "{\"status\":\"success\"}")
}

func main() {
logger.Println("Initializing server")
flag.Parse()
Expand Down Expand Up @@ -119,9 +137,9 @@ func main() {

messenger := newMessenger(serverConfig.twilioConfig)
var delayTimerMap map[string]*time.Timer = make(map[string]*time.Timer)
_webServer = newWebServer(serverConfig, newClientHandler)
mqttClient := newMQTTClient(serverConfig)
mqttClient.Subscribe(sensorStatusChannel, func(messageString string) {
_webServer = newWebServer(serverConfig, newClientHandler, sensorRestartHandler)
_mqttClient = newMQTTClient(serverConfig)
_mqttClient.Subscribe(sensorStatusChannel, func(messageString string) {
message := toStruct(messageString)
lastMessageString, _ := _redisClient.ReadState(message.Source)
lastMessage := toStruct(lastMessageString)
Expand All @@ -147,7 +165,7 @@ func main() {
})

var heartbeatTimerMap map[string]*time.Timer = make(map[string]*time.Timer)
mqttClient.Subscribe(sensorHeartbeatChannel, func(messageString string) {
_mqttClient.Subscribe(sensorHeartbeatChannel, func(messageString string) {
heartbeat := toHeartbeat(messageString)
currentTimer := heartbeatTimerMap[heartbeat.Source]
if currentTimer != nil {
Expand Down
5 changes: 5 additions & 0 deletions server/mqtt.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,8 @@ func (c mqttClient) Subscribe(topic string, subscribeHandler fn) {
logger.Fatal(token.Error())
}
}

func (c mqttClient) publishSensorRestart(sensorSource string) {
token := c.client.Publish(sensorRestartChannel, 0, false, sensorSource)
token.Wait()
}
22 changes: 11 additions & 11 deletions server/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,33 +25,32 @@ const (
// Time allowed to read the next pong message from the peer.
pongWait = 60 * time.Second

publicDir = "/frontend/build/"
channelName = "sensor"
sessionName = "pi-sensor" // TODO need to make this dynamic?
sessionUserKey = "9024685F-97A4-441E-90D3-F0F11AA7A602"
post = "post"
sensorListChannel = "sensor/list"
publicDir = "/frontend/build/"
channelName = "sensor"
sessionName = "pi-sensor" // TODO need to make this dynamic?
sessionUserKey = "9024685F-97A4-441E-90D3-F0F11AA7A602"
post = "post"
get = "get"
sensorListChannel = "sensor/list"
sensorRestartChannel = "sensor/restart"
)

var sessionStore *sessions.CookieStore

type newClientHandlerFunc func()

var channel *gosocketio.Channel

type webServer struct {
httpServer *http.Server
socketServer *gosocketio.Server
}

func newWebServer(serverConfig ServerConfig, newClientHandler newClientHandlerFunc) webServer {
func newWebServer(serverConfig ServerConfig, newClientHandler newClientHandlerFunc, sensorRestartHandler http.HandlerFunc) webServer {
router := gmux.NewRouter().StrictSlash(true)
socketServer := gosocketio.NewServer(transport.GetDefaultWebsocketTransport())
socketServer.On(gosocketio.OnConnection, func(c *gosocketio.Channel) {
channel = c
// c.BroadcastTo("chat", "message", msg)
newClientHandler()
})

router.Handle("/socket.io/", socketServer)
oauth2Config := &oauth2.Config{
ClientID: serverConfig.googleConfig.clientId,
Expand All @@ -62,6 +61,7 @@ func newWebServer(serverConfig ServerConfig, newClientHandler newClientHandlerFu
}
sessionStore = sessions.NewCookieStore([]byte(serverConfig.googleConfig.sessionSecret), nil)
stateConfig := gologin.DebugOnlyCookieConfig
router.Handle("/api/sensor/restart", requireLogin(http.HandlerFunc(sensorRestartHandler))).Methods(post)
router.Handle("/google/login", google.StateHandler(stateConfig, google.LoginHandler(oauth2Config, nil)))
router.Handle("/google/callback", google.StateHandler(stateConfig, google.CallbackHandler(oauth2Config, issueSession(serverConfig), nil)))
router.HandleFunc("/logout", logoutHandler)
Expand Down

0 comments on commit cefdba1

Please sign in to comment.