-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathserver.go
359 lines (313 loc) · 10.2 KB
/
server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
// Daniel Delago
// Backend server for NExSyS Web Based Simulation Monitoring Tool frontend
package main
import (
"bufio"
"net"
"strconv"
"strings"
"net/http"
"github.com/gorilla/websocket"
"log"
"./Parsers"
"sync"
"os"
)
// Global Variables
var (
// Connected clients
clients = make(map[*websocket.Conn]net.Conn)
// Broadcast channel
broadcast = make(chan Message)
// Configure the upgrader
upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
// List of DCAPP Trick Variables
trickVarsAll = Parsers.ParseTrickXML("src/variables/trick/vars_displayed.xml")
// Get all Trick Variables in formatted list form
trickVarList = Parsers.GetTrickVarLists(trickVarsAll)
// Map of all variables to their values
trickVarsInitial = make(map[int]Message)
// Mutex lock
mutex = sync.RWMutex{}
// Increasing variable id for trickVarsInitial
id = 1
)
// Define our message object
type Message struct {
Variable string `json:"variable"`
Value string `json:"value"`
}
// Add new variable to keep track of
func addTrickVar(msg Message) {
trickVarsInitial[id] = Message{msg.Variable, msg.Value}
id++
}
// Send command to Trick server
func sendTrickCommand(command string, conn net.Conn ) {
conn.Write([]byte(command))
}
// Send message to client
func sendMessage(msg Message, client *websocket.Conn) {
err := client.WriteJSON(msg)
if err != nil {
log.Printf("error: %v", err)
client.Close()
delete(clients, client)
}
}
// Handle sockets and receive incoming messages from clients
func handleConnections(w http.ResponseWriter, r *http.Request) {
log.Printf("New client")
// Upgrade initial GET request to a websocket
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Fatal(err, ws)
}
// Make sure we close the connection when the function returns
defer ws.Close()
// ip and port for initial trick server
// ip := "192.70.126.237"
ip := "139.169.203.141"
port := 17000
addr := strings.Join([]string{ip, strconv.Itoa(port)}, ":")
var connTrick net.Conn
// A map of trick variables SPECIFIC to this client
trickVars := make(map[int]Message)
// Assign intial values to trickVars
for key, value := range trickVarsInitial {
trickVars[key] = value
}
// Channel for signalling when to end a Goroutuine
goSignal := make(chan string)
// Create goroutine to prevent blocking on Trick server connection attempt
go func() {
connTrick, err = net.Dial("tcp", addr)
if err != nil {
log.Printf("Can't connect to Trick server.")
log.Printf("%v", err)
} else {
log.Printf("Connected to Trick server.")
// Register our new client
clients[ws] = connTrick
// Begin Trick Server Goroutine
go getTrickVars(ip, port, ws, trickVars, goSignal)
}
}()
// Active Panel being viewed by client
activePanel := ""
// Infinite loop that continously waits for a new message to be written to the WebSocket
for {
// Read in a new message as JSON and map it to a Message object
var msg Message
err := ws.ReadJSON(&msg)
if err != nil {
log.Printf("error: %v", err)
delete(clients, ws)
break
}
// Switch on message recieved
switch {
// Active Panel has changed
case msg.Variable == "activePanel" :
// Get new active panel value
activePanel = msg.Value
// New map to send to client of updated values on the current panel being viewed
panelMap := make(map[int]Message)
// Switch on active panel
switch {
// Dashboard Active
case activePanel == "dashboard":
panelMap[0] = Message{"trickServer", addr}
panelMap[1] = Message{"panel", "dashboard"}
// MPCV Active
case activePanel == "mpcv":
var_id := 1
for var_id <= len(trickVarList[0]){
mutex.RLock()
panelMap[var_id] = trickVars[var_id]
mutex.RUnlock()
var_id++
}
panelMap[var_id] = Message{"panel", "mpcv"}
// PM Active
case activePanel == "pm":
var_id := len(trickVarList[0]) + 1
for var_id <= len(trickVarList[0]) + len(trickVarList[1]){
mutex.RLock()
panelMap[var_id] = trickVars[var_id]
mutex.RUnlock()
var_id++
}
panelMap[var_id] = Message{"panel", "pm"}
// Robo Active
case activePanel == "robo":
var_id := len(trickVarList[0]) + len(trickVarList[1]) + 1
for var_id <= len(trickVarList[0]) + len(trickVarList[1]) + len(trickVarList[2]){
mutex.RLock()
panelMap[var_id] = trickVars[var_id]
mutex.RUnlock()
var_id++
}
panelMap[var_id] = Message{"panel", "robo"}
// SubSys Active
case activePanel == "subsys":
var_id := len(trickVarList[0]) + len(trickVarList[1]) + len(trickVarList[2])
for var_id <= len(trickVarList[0]) + len(trickVarList[1]) + len(trickVarList[2]) + len(trickVarList[3]){
mutex.RLock()
panelMap[var_id] = trickVars[var_id]
// log.Printf("%v %v\n", panelMap[var_id], var_id)
mutex.RUnlock()
var_id++
}
panelMap[var_id] = Message{"panel", "subsys"}
// Cam Active
case activePanel == "cams":
var_id := len(trickVarList[0]) + len(trickVarList[1]) + len(trickVarList[2]) + len(trickVarList[3]) + 1
for var_id <= len(trickVarList[0]) + len(trickVarList[1]) + len(trickVarList[2]) + len(trickVarList[3]) + len(trickVarList[4]){
mutex.RLock()
panelMap[var_id] = trickVars[var_id]
mutex.RUnlock()
var_id++
}
panelMap[var_id] = Message{"panel", "cams"}
// RoverLLT Active
case activePanel == "rover_llt":
var_id := len(trickVarList[0]) + len(trickVarList[1]) + len(trickVarList[2]) + len(trickVarList[3]) + len(trickVarList[4]) + 1
for var_id <= len(trickVarList[0]) + len(trickVarList[1]) + len(trickVarList[2]) + len(trickVarList[3]) + len(trickVarList[4]) + len(trickVarList[5]){
mutex.RLock()
panelMap[var_id] = trickVars[var_id]
mutex.RUnlock()
var_id++
}
panelMap[var_id] = Message{"panel", "rover_llt"}
default:
panelMap[0] = Message{"Error, invalid response", ""}
}
// Send out map of variables to the client
err = ws.WriteJSON(panelMap)
if err != nil {
log.Printf("error: %v", err)
ws.Close()
delete(clients, ws)
}
// The client has requested to connect to a new Trick server
case msg.Variable == "trickServer":
log.Printf("Stopping connection to Trick server: %v, connecting to: %v", addr, msg.Value)
// Prevent blocking
select {
case goSignal <- "stop":
default:
}
// Create map to send to client
panelMap := make(map[int]Message)
// Save old address if new address is invalid
oldAddr := addr
addr = msg.Value
// Attempt to connect to new Trick server
connTrick, err = net.Dial("tcp", addr)
if err != nil {
log.Printf("Can't connect to Trick server.")
panelMap[0] = Message{"error", "invalid_trick_server"}
panelMap[1] = Message{"panel", "dashboard"}
addr = oldAddr
err = ws.WriteJSON(panelMap)
if err != nil {
log.Printf("error: %v", err)
ws.Close()
delete(clients, ws)
}
break;
}
// If connected successfully to new Trick server, update client and start new Goroutine
clients[ws] = connTrick
go getTrickVars(ip, port, ws, trickVars, goSignal)
}
}
connTrick.Close()
}
// Get Trick variable values from server
func getTrickVars(ip string, port int, ws *websocket.Conn, trickVars map[int]Message, goSignal <-chan string) {
// Write to server, pause and send messages using ASCII encoding
sendTrickCommand("trick.var_pause()\n", clients[ws])
sendTrickCommand("trick.var_ascii()\n", clients[ws])
// Variables to retrieve from Trick
for var_id := 1; var_id < len(trickVars) + 1; var_id++ {
sendTrickCommand("trick.var_add(\"" + trickVars[var_id].Variable + "\") \n", clients[ws])
}
// Begin sending data
sendTrickCommand("trick.var_unpause()\n", clients[ws])
// Input buffer
reader := bufio.NewReader(clients[ws])
msg := ""
// Infinite loop over incoming data from Trick server
for {
// check if Goroutine was signalled to stop
select {
case msg = <-goSignal:
default:
}
if msg == "stop" {
log.Printf("Stopping goroutine")
clients[ws].Close()
break
}
// Get all incoming values from Trick
reply, err := reader.ReadBytes('\n')
if err != nil {
log.Printf("Connection with Trick server lost.")
break;
}
// Split on whitespace only up to the number of received input variables, values begin on index 1
data := strings.Fields(string(reply))
// log.Printf("\nDATA %v %v\n", data, len(trickVars))
// For every variable, update values
for var_id := 1 ; var_id < len(data); var_id++ {
// Workaround to update a GoLang map struct value
mutex.RLock()
trickVarCurrent := trickVars[var_id].Value
mutex.RUnlock()
if trickVarCurrent != data[var_id] {
mutex.RLock()
var temp = trickVars[var_id]
mutex.RUnlock()
temp.Value = data[var_id]
mutex.Lock()
trickVars[var_id] = temp
mutex.Unlock()
}
}
}
}
func main() {
// Create a simple file server, THIS IS WHERE GO IMPLEMENTS FRONTEND IF npm run build WAS USED
fs := http.FileServer(http.Dir("build"))
http.Handle("/", fs)
// Initialize Trick Variables
// MPCV
for i := range trickVarList[0] { addTrickVar(Message{trickVarList[0][i], "0"}) }
// PM
for i := range trickVarList[1] { addTrickVar(Message{trickVarList[1][i], "0"}) }
// Robo
for i := range trickVarList[2] { addTrickVar(Message{trickVarList[2][i], "0"}) }
// SubSys
for i := range trickVarList[3] { addTrickVar(Message{trickVarList[3][i], "0"}) }
// Cam
for i := range trickVarList[4] { addTrickVar(Message{trickVarList[4][i], "0"}) }
// RoverLLT
for i := range trickVarList[5] { addTrickVar(Message{trickVarList[5][i], "0"}) }
// Configure websocket route
http.HandleFunc("/ws", handleConnections)
// Start the server on localhost port 3000 and log any errors
// log.Println("http server started on 139.169.203.141:3000")
// err2 := http.ListenAndServe("139.169.203.141:3000", nil)
log.Println("http server started on", os.Args[1])
err2 := http.ListenAndServe(os.Args[1], nil)
if err2 != nil {
log.Fatal("ListenAndServe: ", err2)
}
}