...
|
...
|
@@ -17,19 +17,20 @@ import csv2geojs
|
17
|
17
|
import paho.mqtt.client as mqtt
|
18
|
18
|
|
19
|
19
|
# Common
|
|
20
|
+import re
|
20
|
21
|
import json
|
21
|
22
|
from userio import *
|
22
|
23
|
|
23
|
24
|
MAXLRR=4
|
24
|
|
-filename="mqttGeo.csv"
|
25
|
|
-filenameBase="mqttGeo"
|
26
|
25
|
|
27
|
26
|
server = None
|
28
|
27
|
broker = None
|
29
|
28
|
latestHtml = ""
|
|
29
|
+previousLastFcnt = 0
|
30
|
30
|
|
31
|
31
|
def htmlLatest(deveui,subID,FCnt,timeLatest,lat,lon,err,rssi):
|
32
|
32
|
global latestHtml
|
|
33
|
+ global previousLastFcnt
|
33
|
34
|
htmlOutput="<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n"
|
34
|
35
|
|
35
|
36
|
htmlOutput+=csv2geojs.networkSurveyAddLeaflet()
|
...
|
...
|
@@ -41,6 +42,8 @@ def htmlLatest(deveui,subID,FCnt,timeLatest,lat,lon,err,rssi):
|
41
|
42
|
htmlOutput+="</script>\n"
|
42
|
43
|
htmlOutput+="<body>\n"
|
43
|
44
|
htmlOutput+="<h2>"+server['name']+" Latest</h2>\n"
|
|
45
|
+ htmlOutput+="<p>\n<div id=\"home\"><button class=\"btn-green-menu\" onClick=\"window.location.href='.';\">Home</div>\n"
|
|
46
|
+ htmlOutput+="<div id=\"log\"><button class=\"btn-green-menu\" onClick=\"window.location.href='./log';\">Log</div>\n"
|
44
|
47
|
htmlOutput+=" <div id=\"mapid\" style=\"width: 95%; max-width: 400px; height: 200px; margin: auto\"></div>\n"
|
45
|
48
|
|
46
|
49
|
if 0 != lat and 0 != lon:
|
...
|
...
|
@@ -72,11 +75,12 @@ def htmlLatest(deveui,subID,FCnt,timeLatest,lat,lon,err,rssi):
|
72
|
75
|
htmlOutput+="<div id=\"deveui\">"+deveui+"</div>\n"
|
73
|
76
|
htmlOutput+="<div id=\"fcnt\">"+str(FCnt)+"</div>\n"
|
74
|
77
|
htmlOutput+="<div id=\"time\">"+timeLatest+"</div>\n"
|
75
|
|
- htmlOutput+="<div id=\"position\">"+str(lat)+" "+str(lon)+" "+str(err)+"</div>\n"
|
76
|
|
- htmlOutput+="<div id=\"rssi\">"+str(rssi)+"</div>\n"
|
|
78
|
+ htmlOutput+="<div id=\"position\">"+str(lat)+"° "+str(lon)+"° "+str(err)+"m</div>\n"
|
|
79
|
+ htmlOutput+="<div id=\"rssi\">"+str(rssi)+"dB</div>\n"
|
|
80
|
+ htmlOutput+="<div id=\"delta\">Δ: "+str(FCnt-previousLastFcnt)+"</div>\n"
|
|
81
|
+ previousLastFcnt = FCnt
|
77
|
82
|
htmlOutput+="</p>\n"
|
78
|
83
|
htmlOutput+="<p>\n<div id=\"reload\"><button class=\"btn-green\" onClick=\"window.location.reload();\">Reload</div>\n</p>\n"
|
79
|
|
- htmlOutput+="<p>\n<div id=\"home\"><button class=\"btn-green-menu\" onClick=\"window.location.href='.';\">Home</div>\n</p>\n"
|
80
|
84
|
|
81
|
85
|
htmlOutput+="</body>\n"
|
82
|
86
|
htmlOutput+="</html>\n"
|
...
|
...
|
@@ -103,18 +107,23 @@ def on_message(client, userdata, msg):
|
103
|
107
|
epoch = int(time.mktime(time.strptime(parsed['DevEUI_uplink']['Time'], pattern)))
|
104
|
108
|
FCnt=parsed['DevEUI_uplink']['FCntUp']
|
105
|
109
|
csvLine+=parseTopic[1]+","+parseTopic[3]+","+parsed['DevEUI_uplink']['Time']+","+str(epoch)+","+str(FCnt)
|
106
|
|
- say("UL SubID: "+parseTopic[1]+" DevEUI: "+parseTopic[3]+" Fcnt: "+str(FCnt))
|
107
|
110
|
except:
|
108
|
111
|
try:
|
109
|
112
|
dlFPort=parsed['DevEUI_downlink_Sent']['FPort']
|
110
|
113
|
dlFCntDn=parsed['DevEUI_downlink_Sent']['FCntDn']
|
111
|
|
- say("DL SubID: "+parseTopic[1]+" DevEUI: "+parseTopic[3]+" Fcnt: "+str(dlFCntDn)+" Fport: "+str(dlFPort))
|
|
114
|
+ say("DL ID: "+parseTopic[1]+" DevEUI: "+parseTopic[3]+" F: "+str(dlFCntDn)+" Fport: "+str(dlFPort))
|
112
|
115
|
except:
|
113
|
116
|
with open(server['logfile'],'a+') as f:
|
114
|
117
|
f.write("------------------------------\nMQTT Error: topic: "+msg.topic+" content: "+msg.payload.decode('ASCII')+"\n------------------------------\n")
|
115
|
118
|
f.close()
|
116
|
119
|
warn("Uh ho")
|
117
|
120
|
return
|
|
121
|
+ messageType = "UNKNOWN"
|
|
122
|
+ try:
|
|
123
|
+ messageType = parsed['DevEUI_uplink']['payload']['messageType']
|
|
124
|
+ except:
|
|
125
|
+ pass
|
|
126
|
+
|
118
|
127
|
|
119
|
128
|
lat=0
|
120
|
129
|
lon=0
|
...
|
...
|
@@ -127,23 +136,24 @@ def on_message(client, userdata, msg):
|
127
|
136
|
lon=parsed['DevEUI_uplink']['payload']['gpsLongitude']
|
128
|
137
|
err=parsed['DevEUI_uplink']['payload']['horizontalAccuracy']
|
129
|
138
|
except:
|
130
|
|
- warn("[WARN] GPS Timeout")
|
|
139
|
+ #warn("GPS Timeout")
|
|
140
|
+ warn("UL ID: "+parseTopic[1]+" D: "+parseTopic[3]+" F: "+str(FCnt)+" Type: "+messageType+" GPS Timeout")
|
131
|
141
|
return
|
132
|
|
-
|
133
|
|
-
|
134
|
142
|
else:
|
135
|
143
|
isPosition=False
|
136
|
|
- say("[WARN] Not a position message:"+parsed['DevEUI_uplink']['payload']['messageType'])
|
|
144
|
+ say("UL ID: "+parseTopic[1]+" D: "+parseTopic[3]+" F: "+str(FCnt)+" Type: "+messageType)
|
137
|
145
|
return
|
138
|
146
|
else:
|
139
|
147
|
isPosition=False
|
140
|
|
- warn("[WARN] No decoded payload")
|
|
148
|
+ warn("No decoded payload")
|
141
|
149
|
with open(server['logfile'],'a+') as f:
|
142
|
150
|
f.write("------------------------------\nMQTT Not decoded: topic: "+msg.topic+" content: "+msg.payload.decode('ASCII')+"\n------------------------------\n")
|
143
|
151
|
f.close()
|
144
|
152
|
return
|
145
|
153
|
csvLine+=","+str(lat)+","+str(lon)+","+str(err)
|
146
|
154
|
|
|
155
|
+ ok("UL ID: "+parseTopic[1]+" D: "+parseTopic[3]+" F: "+str(FCnt)+" Type: "+messageType+" "+str(lat)+" "+str(lon)+" "+str(err))
|
|
156
|
+
|
147
|
157
|
# Append GW details
|
148
|
158
|
#parsed['DevEUI_uplink']['Lrrs']['Lrr']
|
149
|
159
|
#Lrrid
|
...
|
...
|
@@ -163,7 +173,7 @@ def on_message(client, userdata, msg):
|
163
|
173
|
|
164
|
174
|
# Appending only if position is different from 0,0
|
165
|
175
|
if 0 != lat and 0 != lon:
|
166
|
|
- with open(filename,'a+') as f:
|
|
176
|
+ with open(server['rawCsv'],'a+') as f:
|
167
|
177
|
f.write(csvLine+"\n")
|
168
|
178
|
f.close()
|
169
|
179
|
if True == isPosition:
|
...
|
...
|
@@ -222,6 +232,38 @@ def httpDevEuiList(subId):
|
222
|
232
|
content += "</ul>"
|
223
|
233
|
return content
|
224
|
234
|
|
|
235
|
+def escape_ansi(line):
|
|
236
|
+ ansi_escape = re.compile(r'(?:\x1B[@-_]|[\x80-\x9F])[0-?]*[ -/]*[@-~]')
|
|
237
|
+ return ansi_escape.sub('', line)
|
|
238
|
+
|
|
239
|
+def httpReverseLog(numLines):
|
|
240
|
+ global server
|
|
241
|
+ content=""
|
|
242
|
+ #print("Log file:"+server['logfile'])
|
|
243
|
+ cpt = 0
|
|
244
|
+ content+="<h2>"+server['name']+" Log</h2>\n"
|
|
245
|
+ content+="<p>\n<div id=\"latest\"><button class=\"btn-green-menu\" onClick=\"window.location.href='./last';\">Latest</div>\n"
|
|
246
|
+ content+="<div id=\"home\"><button class=\"btn-green-menu\" onClick=\"window.location.href='.';\">Home</div>\n"
|
|
247
|
+ content+="<div id=\"reload\"><button class=\"btn-green\" onClick=\"window.location.reload();\">Reload</div>\n</p>\n"
|
|
248
|
+ content+="<div class=\"loglines\">\n"
|
|
249
|
+ for line in reversed(open(server['logfile']).readlines()):
|
|
250
|
+ lineColor=line.rstrip()
|
|
251
|
+ lineColor =escape_ansi(lineColor)
|
|
252
|
+ lineColor = lineColor.replace("[OK]","<div class=\"logOK\">[OK]</div>")
|
|
253
|
+ lineColor = lineColor.replace("[WARN]","<div class=\"logWARN\">[WARN]</div>")
|
|
254
|
+ lineColor = lineColor.replace("[ERR]","<div class=\"logERROR\">[ERR]</div>")
|
|
255
|
+ lineColor = lineColor.replace(server['name']+" ","")
|
|
256
|
+ lineColor = lineColor.replace("+0000","")
|
|
257
|
+ if not "/log/" in lineColor and not "------------------------------" in lineColor and not "From: " in lineColor:
|
|
258
|
+ cpt += 1
|
|
259
|
+ content+=lineColor+"<br>\n"
|
|
260
|
+
|
|
261
|
+ if cpt >= numLines:
|
|
262
|
+ continue
|
|
263
|
+
|
|
264
|
+ content+="</div>\n"
|
|
265
|
+ return content
|
|
266
|
+
|
225
|
267
|
|
226
|
268
|
class MyServer(BaseHTTPRequestHandler):
|
227
|
269
|
def log_message(self, format, *args):
|
...
|
...
|
@@ -246,11 +288,42 @@ class MyServer(BaseHTTPRequestHandler):
|
246
|
288
|
data_end += fEnd.read().replace( 'CSTAPPNAME', server['name'] ).replace( 'CSTAPPVERSION', server['version'] )
|
247
|
289
|
fEnd.close()
|
248
|
290
|
|
249
|
|
- if self.path == "/" or self.path == "/index.html":
|
|
291
|
+ if "/favicon.png" in self.path or "/style.css" in self.path:
|
|
292
|
+ splitPath=self.path.split('/')
|
|
293
|
+ localFilename=splitPath[len(splitPath)-1]
|
|
294
|
+ try:
|
|
295
|
+
|
|
296
|
+ f = open(rootdir + "/" + localFilename,'rb') #open requested file
|
|
297
|
+ self.send_response(200)
|
|
298
|
+ if self.path.endswith('.css'):
|
|
299
|
+ self.send_header('Content-type','text/css')
|
|
300
|
+ elif self.path.endswith('.bmp'):
|
|
301
|
+ self.send_header('Content-type','image/x-ms-bmp')
|
|
302
|
+ elif self.path.endswith('.png'):
|
|
303
|
+ self.send_header('Content-type','image/png')
|
|
304
|
+ elif self.path.endswith('.jpg'):
|
|
305
|
+ self.send_header('Content-type','image/jpeg')
|
|
306
|
+ else:
|
|
307
|
+ self.send_header('Content-type','text/html')
|
|
308
|
+ self.send_header('Server',server['name']+" v"+server['version'])
|
|
309
|
+ self.end_headers()
|
|
310
|
+ self.wfile.write(f.read())
|
|
311
|
+ f.close()
|
|
312
|
+ return
|
|
313
|
+ except:
|
|
314
|
+ with open(server['logfile'],'a+') as f:
|
|
315
|
+ f.write("------------------------------\nHTTP 404 "+self.path+"\n------------------------------\n")
|
|
316
|
+ f.close()
|
|
317
|
+ self.send_header('Server',server['name']+" v"+server['version'])
|
|
318
|
+ self.send_error(404, 'file not found')
|
|
319
|
+
|
|
320
|
+ elif self.path == "/" or self.path == "/index.html":
|
250
|
321
|
# Send list of SubID
|
251
|
|
- data_page ="<h2>"+server['name']+" Latest</h2>\n"
|
|
322
|
+ data_page ="<h2>"+server['name']+"</h2>\n"
|
|
323
|
+ data_page += "<p>\n<div id=\"latest\"><button class=\"btn-green-menu\" onClick=\"window.location.href='./last';\">Latest</div>\n"
|
|
324
|
+ data_page += "<div id=\"log\"><button class=\"btn-green-menu\" onClick=\"window.location.href='./log';\">Log</div>\n</p>\n"
|
|
325
|
+ data_page += "<h3>subId List</h3>\n"
|
252
|
326
|
data_page += httpSubIdList()
|
253
|
|
- data_page += "<p>\n<div id=\"latest\"><button class=\"btn-green-menu\" onClick=\"window.location.href='./last';\">Latest</div>\n</p>\n"
|
254
|
327
|
|
255
|
328
|
page.append(data_begin)
|
256
|
329
|
page.append(data_page)
|
...
|
...
|
@@ -262,7 +335,7 @@ class MyServer(BaseHTTPRequestHandler):
|
262
|
335
|
self.end_headers()
|
263
|
336
|
self.wfile.write(content.encode('utf-8'))
|
264
|
337
|
|
265
|
|
- elif self.path == "/last" or self.path == "/last/index.html":
|
|
338
|
+ elif self.path == "/last" or self.path == "/last/" or self.path == "/last/index.html":
|
266
|
339
|
page.append(data_begin)
|
267
|
340
|
page.append(latestHtml)
|
268
|
341
|
page.append(data_end)
|
...
|
...
|
@@ -272,8 +345,35 @@ class MyServer(BaseHTTPRequestHandler):
|
272
|
345
|
self.send_header('Server',server['name']+" v"+server['version'])
|
273
|
346
|
self.end_headers()
|
274
|
347
|
self.wfile.write(content.encode('utf-8'))
|
275
|
|
-
|
276
|
348
|
|
|
349
|
+ elif self.path == "/log" or self.path == "/log/" or self.path == "/log/index.html":
|
|
350
|
+ uriObject = urlparse(self.path)
|
|
351
|
+ queries = uriObject.query.split('&')
|
|
352
|
+ uriParams = {}
|
|
353
|
+
|
|
354
|
+ try:
|
|
355
|
+ for query in queries:
|
|
356
|
+ element=query.split("=")
|
|
357
|
+ uriParams[element[0]]=element[1]
|
|
358
|
+ except:
|
|
359
|
+ pass
|
|
360
|
+
|
|
361
|
+ try:
|
|
362
|
+ numLines=uriParams['last']
|
|
363
|
+ except:
|
|
364
|
+ numLines = 50
|
|
365
|
+
|
|
366
|
+ page.append(data_begin)
|
|
367
|
+ data_page = httpReverseLog(numLines)
|
|
368
|
+ page.append(data_page)
|
|
369
|
+ page.append(data_end)
|
|
370
|
+ content = ''.join(page)
|
|
371
|
+ self.send_response(200)
|
|
372
|
+ self.send_header("Content-type", "text/html")
|
|
373
|
+ self.send_header('Server',server['name']+" v"+server['version'])
|
|
374
|
+ self.end_headers()
|
|
375
|
+ self.wfile.write(content.encode('utf-8'))
|
|
376
|
+
|
277
|
377
|
elif "/?" in self.path:
|
278
|
378
|
uriObject = urlparse(self.path)
|
279
|
379
|
queries = uriObject.query.split('&')
|
...
|
...
|
@@ -290,13 +390,15 @@ class MyServer(BaseHTTPRequestHandler):
|
290
|
390
|
except:
|
291
|
391
|
devEUI=None
|
292
|
392
|
|
|
393
|
+ data_page += "<p><div id=\"home\"><button class=\"btn-green-menu\" onClick=\"window.location.href='.';\">Home</div>\n"
|
|
394
|
+ data_page += "<div id=\"latest\"><button class=\"btn-green-menu\" onClick=\"window.location.href='./last';\">Latest</div>\n"
|
|
395
|
+ data_page += "<div id=\"log\"><button class=\"btn-green-menu\" onClick=\"window.location.href='./log';\">Log</div>\n"
|
|
396
|
+
|
293
|
397
|
if subId is not None and devEUI is None:
|
294
|
|
- data_page = csv2geojs.networkSurvey(filename, subId, fileOutput=False)
|
|
398
|
+ data_page += csv2geojs.networkSurvey(server['rawCsv'], subId, fileOutput=False)
|
295
|
399
|
elif subId is not None and devEUI is not None:
|
296
|
|
- data_page = csv2geojs.networkSurvey(filename, subId, devEUI, fileOutput=False)
|
|
400
|
+ data_page += csv2geojs.networkSurvey(server['rawCsv'], subId, devEUI, fileOutput=False)
|
297
|
401
|
data_page += httpDevEuiList(subId)
|
298
|
|
- data_page += "<p>\n<div id=\"latest\"><button class=\"btn-green-menu\" onClick=\"window.location.href='./last';\">Latest</div>\n"
|
299
|
|
- data_page += "<div id=\"home\"><button class=\"btn-green-menu\" onClick=\"window.location.href='.';\">Home</div>\n</p>\n"
|
300
|
402
|
|
301
|
403
|
page.append(data_begin)
|
302
|
404
|
page.append(data_page)
|
...
|
...
|
@@ -329,11 +431,11 @@ class MyServer(BaseHTTPRequestHandler):
|
329
|
431
|
f.close()
|
330
|
432
|
return
|
331
|
433
|
except IOError:
|
332
|
|
- self.send_header('Server',server['name']+" v"+server['version'])
|
333
|
|
- self.send_error(404, 'file not found')
|
334
|
434
|
with open(server['logfile'],'a+') as f:
|
335
|
435
|
f.write("------------------------------\nHTTP 404 "+self.path+"\n------------------------------\n")
|
336
|
436
|
f.close()
|
|
437
|
+ self.send_header('Server',server['name']+" v"+server['version'])
|
|
438
|
+ self.send_error(404, 'file not found')
|
337
|
439
|
|
338
|
440
|
|
339
|
441
|
def mqttInitRun():
|
...
|
...
|
@@ -375,10 +477,13 @@ def httpInitRun():
|
375
|
477
|
pass
|
376
|
478
|
|
377
|
479
|
webServer.server_close()
|
378
|
|
- print("HTTP Server stopped.")
|
|
480
|
+ say("HTTP Server stopped.")
|
379
|
481
|
|
380
|
482
|
|
381
|
483
|
if __name__ == "__main__":
|
|
484
|
+ server = configuration.get_server()
|
|
485
|
+ say("===============================")
|
|
486
|
+ say("Starting")
|
382
|
487
|
Thread(target = mqttInitRun).start()
|
383
|
488
|
Thread(target = httpInitRun).start()
|
384
|
489
|
|