1 | 1 |
old mode 100644 |
2 | 2 |
new mode 100755 |
... | ... |
@@ -8,7 +8,7 @@ |
8 | 8 |
# device and the Internet web server. The agent periodically sends an http |
9 | 9 |
# request to the radiation monitoring device and processes the response from |
10 | 10 |
# the device and performs a number of operations: |
11 |
-# - conversion of data items |
|
11 |
+# - conversion of data itemsq |
|
12 | 12 |
# - update a round robin (rrdtool) database with the radiation data |
13 | 13 |
# - periodically generate graphic charts for display in html documents |
14 | 14 |
# - forward the radiation data to other services |
... | ... |
@@ -32,6 +32,7 @@ |
32 | 32 |
# Revision History |
33 | 33 |
# * v20 released 15 Sep 2015 by J L Owrey; first release |
34 | 34 |
# * v21 released 27 Nov 2017 by J L Owrey; bug fixes; updates |
35 |
+# * v22 released 03 Mar 2018 by J L Owrey; improved code readability |
|
35 | 36 |
# |
36 | 37 |
|
37 | 38 |
_MIRROR_SERVER = False |
... | ... |
@@ -49,10 +50,9 @@ _USER = os.environ['USER'] |
49 | 50 |
### DEFAULT WEATHER STATION URL ### |
50 | 51 |
|
51 | 52 |
# ip address of radiation monitoring device |
52 |
-_RADIATION_MONITOR_URL = "http://YOUR_RADIATION_MONITOR_IP_ADDRESS" |
|
53 |
+_DEFAULT_RADIATION_MONITOR_URL = '{your radiation monitor device url}' |
|
53 | 54 |
# url if this is a mirror server |
54 |
-_PRIMARY_SERVER_URL = \ |
|
55 |
- "http://YOUR_PRIMARY_SERVER/USER/radmon/dynamic/radmonInputData.dat" |
|
55 |
+_PRIMARY_SERVER_URL = '{your primary server radmon data url}' |
|
56 | 56 |
|
57 | 57 |
### FILE AND FOLDER LOCATIONS ### |
58 | 58 |
|
... | ... |
@@ -63,7 +63,7 @@ _CHARTS_DIRECTORY = _DOCROOT_PATH + "dynamic/" |
63 | 63 |
# database that stores weather data |
64 | 64 |
_RRD_FILE = "/home/%s/database/radmonData.rrd" % _USER |
65 | 65 |
# location of data input file |
66 |
-INPUT_DATA_FILE = _DOCROOT_PATH + "dynamic/radmonInputData.dat" |
|
66 |
+_INPUT_DATA_FILE = _DOCROOT_PATH + "dynamic/radmonInputData.dat" |
|
67 | 67 |
# location of data output file |
68 | 68 |
_OUTPUT_DATA_FILE = _DOCROOT_PATH + "dynamic/radmonOutputData.js" |
69 | 69 |
|
... | ... |
@@ -77,7 +77,7 @@ _DATABASE_UPDATE_INTERVAL = 30 |
77 | 77 |
# number seconds to wait for a response to HTTP request |
78 | 78 |
_HTTP_REQUEST_TIMEOUT = 3 |
79 | 79 |
# max number of failed data requests allowed |
80 |
-_MAX_RADIATION_MONITOR_OFFLINE_COUNT = 2 |
|
80 |
+_MAX_FAILED_DATA_REQUESTS = 2 |
|
81 | 81 |
# radmon chart dimensions |
82 | 82 |
_CHART_WIDTH = 600 |
83 | 83 |
_CHART_HEIGHT = 150 |
... | ... |
@@ -89,11 +89,11 @@ debugOption = False |
89 | 89 |
# online status of radiation monitor |
90 | 90 |
radiationMonitorOnline = True |
91 | 91 |
# number of unsuccessful http requests |
92 |
-radiationMonitorOfflineCount = 0 |
|
92 |
+failedDataRequestCount = 0 |
|
93 | 93 |
# status of reset command to radiation monitor |
94 | 94 |
remoteDeviceReset = False |
95 | 95 |
# ip address of radiation monitor |
96 |
-radiationMonitorUrl = _RADIATION_MONITOR_URL |
|
96 |
+radiationMonitorUrl = _DEFAULT_RADIATION_MONITOR_URL |
|
97 | 97 |
# web update frequency |
98 | 98 |
dataRequestInterval = _DEFAULT_DATA_REQUEST_INTERVAL |
99 | 99 |
|
... | ... |
@@ -115,27 +115,29 @@ def setOfflineStatus(dData): |
115 | 115 |
dData - dictionary object containing weather data |
116 | 116 |
Returns nothing. |
117 | 117 |
""" |
118 |
- global radiationMonitorOnline, radiationMonitorOfflineCount |
|
118 |
+ global radiationMonitorOnline, failedDataRequestCount |
|
119 | 119 |
|
120 |
- radiationMonitorOfflineCount += 1 |
|
120 |
+ if os.path.exists(_INPUT_DATA_FILE): |
|
121 |
+ os.remove(_INPUT_DATA_FILE) |
|
121 | 122 |
|
122 |
- if radiationMonitorOfflineCount < _MAX_RADIATION_MONITOR_OFFLINE_COUNT: |
|
123 |
+ if failedDataRequestCount < _MAX_FAILED_DATA_REQUESTS - 1: |
|
124 |
+ failedDataRequestCount += 1 |
|
123 | 125 |
return |
124 | 126 |
|
125 | 127 |
# If the radiation monitor was previously online, then send a message |
126 | 128 |
# that we are now offline. |
127 | 129 |
if radiationMonitorOnline: |
128 | 130 |
print "%s: radiation monitor offline" % getTimeStamp() |
129 |
- if os.path.exists(INPUT_DATA_FILE): |
|
130 |
- os.remove(INPUT_DATA_FILE) |
|
131 | 131 |
radiationMonitorOnline = False |
132 | 132 |
|
133 |
- for key in dData: |
|
134 |
- dData[key] = '' |
|
135 |
- |
|
136 |
- dData['status'] = 'offline' |
|
133 |
+ dData['UTC'] = '' |
|
134 |
+ dData['Mode'] = '' |
|
135 |
+ dData['uSvPerHr'] = '' |
|
136 |
+ dData['CPM'] = '' |
|
137 |
+ dData['CPS'] = '' |
|
138 |
+ dData['status'] = 'offline' |
|
137 | 139 |
|
138 |
- writeOutputDataFile(dData) |
|
140 |
+ writeOutputDataFile(dData) |
|
139 | 141 |
return |
140 | 142 |
##end def |
141 | 143 |
|
... | ... |
@@ -152,7 +154,7 @@ def getRadiationData(): |
152 | 154 |
Returns a string containing the radiation data, or None if |
153 | 155 |
not successful. |
154 | 156 |
""" |
155 |
- global radiationMonitorOnline, radiationMonitorOfflineCount, \ |
|
157 |
+ global radiationMonitorOnline, failedDataRequestCount, \ |
|
156 | 158 |
remoteDeviceReset |
157 | 159 |
|
158 | 160 |
if _MIRROR_SERVER: |
... | ... |
@@ -179,15 +181,13 @@ def getRadiationData(): |
179 | 181 |
print "http error: %s" % exError |
180 | 182 |
return None |
181 | 183 |
|
182 |
- radiationMonitorOfflineCount = 0 |
|
183 |
- |
|
184 | 184 |
# If the radiation monitor was previously offline, then send a message |
185 | 185 |
# that we are now online. |
186 | 186 |
if not radiationMonitorOnline: |
187 | 187 |
print "%s radiation monitor online" % getTimeStamp() |
188 | 188 |
radiationMonitorOnline = True |
189 | 189 |
|
190 |
- #print content |
|
190 |
+ failedDataRequestCount = 0 |
|
191 | 191 |
return content |
192 | 192 |
##end def |
193 | 193 |
|
... | ... |
@@ -250,6 +250,8 @@ def convertData(dData): |
250 | 250 |
|
251 | 251 |
dData['CPM'] = int(dData.pop('CPM')) |
252 | 252 |
|
253 |
+ dData['CPS'] = int(dData.pop('CPS')) |
|
254 |
+ |
|
253 | 255 |
except Exception, exError: |
254 | 256 |
print "%s convertData: %s" % (getTimeStamp(), exError) |
255 | 257 |
result = False |
... | ... |
@@ -258,7 +260,8 @@ def convertData(dData): |
258 | 260 |
##end def |
259 | 261 |
|
260 | 262 |
def writeOutputDataFile(dData): |
261 |
- """Convert individual weather string data items as necessary. |
|
263 |
+ """Write radiation data items to a JSON formatted file for use by |
|
264 |
+ HTML documents. |
|
262 | 265 |
Parameters: |
263 | 266 |
lsData - a list object containing the data to be written |
264 | 267 |
to the JSON file |
... | ... |
@@ -282,17 +285,20 @@ def writeOutputDataFile(dData): |
282 | 285 |
print "%s writeOutputDataFile: %s" % (getTimeStamp(), exError) |
283 | 286 |
return False |
284 | 287 |
|
285 |
- if debugOption and 0: |
|
286 |
- print sData |
|
287 |
- |
|
288 | 288 |
return True |
289 | 289 |
## end def |
290 | 290 |
|
291 | 291 |
def writeInputDataFile(sData): |
292 |
- # Write the string to the output data file for use by html documents. |
|
292 |
+ """Write raw data from radiation monitor to file for use by mirror |
|
293 |
+ servers. |
|
294 |
+ Parameters: |
|
295 |
+ sData - a string object containing the data string from |
|
296 |
+ the radiation monitor |
|
297 |
+ Returns true if successful, false otherwise. |
|
298 |
+ """ |
|
293 | 299 |
sData += "\n" |
294 | 300 |
try: |
295 |
- fc = open(INPUT_DATA_FILE, "w") |
|
301 |
+ fc = open(_INPUT_DATA_FILE, "w") |
|
296 | 302 |
fc.write(sData) |
297 | 303 |
fc.close() |
298 | 304 |
except Exception, exError: |
... | ... |
@@ -314,13 +320,12 @@ def updateDatabase(dData): |
314 | 320 |
global remoteDeviceReset |
315 | 321 |
|
316 | 322 |
# The RR database stores whole units, so convert uSv to Sv. |
317 |
- #Svvalue = float(dData['uSvPerHr']) * 1.0E-06 |
|
318 |
- Svvalue = dData['uSvPerHr'] * 1.0E-06 |
|
323 |
+ SvPerHr = dData['uSvPerHr'] * 1.0E-06 |
|
319 | 324 |
|
320 | 325 |
# Create the rrdtool update command. |
321 | 326 |
strCmd = "rrdtool update %s %s:%s:%s" % \ |
322 |
- (_RRD_FILE, dData['UTC'], dData['CPM'], Svvalue) |
|
323 |
- if debugOption: |
|
327 |
+ (_RRD_FILE, dData['UTC'], dData['CPM'], SvPerHr) |
|
328 |
+ if debugOption and False: |
|
324 | 329 |
print "%s" % strCmd # DEBUG |
325 | 330 |
|
326 | 331 |
# Run the command as a subprocess. |
... | ... |
@@ -329,15 +334,15 @@ def updateDatabase(dData): |
329 | 334 |
stderr=subprocess.STDOUT) |
330 | 335 |
except subprocess.CalledProcessError, exError: |
331 | 336 |
print "%s: rrdtool update failed: %s" % \ |
332 |
- (getTimeStamp(), exError.output) |
|
337 |
+ (getTimeStamp(), exError.output) |
|
333 | 338 |
if exError.output.find("illegal attempt to update using time") > -1: |
334 | 339 |
remoteDeviceReset = True |
335 |
- print "%s: rebooting radiation monitor" % \ |
|
336 |
- (getTimeStamp()) |
|
337 |
- |
|
340 |
+ print "%s: rebooting radiation monitor" % (getTimeStamp()) |
|
338 | 341 |
return False |
339 |
- |
|
340 |
- return True |
|
342 |
+ else: |
|
343 |
+ if debugOption: |
|
344 |
+ print 'database update sucessful' |
|
345 |
+ return True |
|
341 | 346 |
##end def |
342 | 347 |
|
343 | 348 |
def createGraph(fileName, dataItem, gLabel, gTitle, gStart, |
... | ... |
@@ -392,7 +397,7 @@ def createGraph(fileName, dataItem, gLabel, gTitle, gStart, |
392 | 397 |
strCmd += "CDEF:smoothed=dSeries,%s,TREND LINE3:smoothed#ff0000 " \ |
393 | 398 |
% trendWindow[gStart] |
394 | 399 |
|
395 |
- if debugOption: |
|
400 |
+ if debugOption and False: |
|
396 | 401 |
print "%s\n" % strCmd # DEBUG |
397 | 402 |
|
398 | 403 |
# Run the formatted rrdtool command as a subprocess. |
... | ... |
@@ -475,9 +480,6 @@ def main(): |
475 | 480 |
# last time the rrdtool database updated |
476 | 481 |
lastDatabaseUpdateTime = -1 |
477 | 482 |
|
478 |
- # define empty dictionary object for radmon data |
|
479 |
- dData = {} |
|
480 |
- |
|
481 | 483 |
## Get command line arguments. |
482 | 484 |
getCLarguments() |
483 | 485 |
|
... | ... |
@@ -496,6 +498,7 @@ def main(): |
496 | 498 |
# monitor and process the received data. |
497 | 499 |
if currentTime - lastDataRequestTime > dataRequestInterval: |
498 | 500 |
lastDataRequestTime = currentTime |
501 |
+ dData = {} |
|
499 | 502 |
result = True |
500 | 503 |
|
501 | 504 |
# Get the data string from the device. |
... | ... |
@@ -506,14 +509,13 @@ def main(): |
506 | 509 |
|
507 | 510 |
# If successful parse the data. |
508 | 511 |
if result: |
509 |
- dData = {} |
|
510 | 512 |
result = parseDataString(sData, dData) |
511 | 513 |
|
512 | 514 |
# If parsing successful, convert the data. |
513 | 515 |
if result: |
514 | 516 |
result = convertData(dData) |
515 | 517 |
|
516 |
- # If conversion successful, write data to output file. |
|
518 |
+ # If conversion successful, write data to data files. |
|
517 | 519 |
if result: |
518 | 520 |
writeInputDataFile(sData) |
519 | 521 |
writeOutputDataFile(dData) |
... | ... |
@@ -547,5 +549,11 @@ def main(): |
547 | 549 |
## end def |
548 | 550 |
|
549 | 551 |
if __name__ == '__main__': |
550 |
- main() |
|
551 |
- |
|
552 |
+ try: |
|
553 |
+ main() |
|
554 |
+ except KeyboardInterrupt: |
|
555 |
+ print '\nInterrupted' |
|
556 |
+ if os.path.exists(_OUTPUT_DATA_FILE): |
|
557 |
+ os.remove(_OUTPUT_DATA_FILE) |
|
558 |
+ if os.path.exists(_INPUT_DATA_FILE): |
|
559 |
+ os.remove(_INPUT_DATA_FILE) |
552 | 560 |
old mode 100644 |
553 | 561 |
new mode 100755 |
... | ... |
@@ -1,11 +1,24 @@ |
1 | 1 |
#!/bin/bash |
2 |
+# Stop the radmon agent process and clean up environment. |
|
2 | 3 |
|
3 |
-# Next stop the database update process. |
|
4 |
+# Set path to radmon input data file |
|
5 |
+RADMON_INPUT_DATA_FILE="/home/$USER/public_html/radmon/dynamic/radmonInputData.dat" |
|
6 |
+RADMON_OUTPUT_DATA_FILE="/home/$USER/public_html/radmon/dynamic/radmonOutputData.js" |
|
4 | 7 |
|
5 | 8 |
PROCESS_ID="$(ps x | awk '/[r]admonAgent.py/{print $1}')" |
9 |
+ |
|
6 | 10 |
if [ -n "$PROCESS_ID" ]; then |
7 | 11 |
printf "killing radmon agent [%s]\n" $PROCESS_ID |
8 | 12 |
kill $PROCESS_ID |
13 |
+ |
|
14 |
+ # Remove input data file to alert mirror servers |
|
15 |
+ if [ -e $RADMON_INPUT_DATA_FILE ]; then |
|
16 |
+ rm $RADMON_INPUT_DATA_FILE |
|
17 |
+ fi |
|
18 |
+ # Remove output data file to alert web clients |
|
19 |
+ if [ -e $RADMON_OUTPUT_DATA_FILE ]; then |
|
20 |
+ rm $RADMON_OUTPUT_DATA_FILE |
|
21 |
+ fi |
|
9 | 22 |
else |
10 | 23 |
echo radmon agent not running |
11 | 24 |
fi |
... | ... |
@@ -162,16 +162,17 @@ Radiation Dose Chart</a> by Randall Monroe.</li> |
162 | 162 |
|
163 | 163 |
<script> |
164 | 164 |
|
165 |
-/* Globals */ |
|
165 |
+/* Global constants */ |
|
166 |
+ |
|
166 | 167 |
var radmonDataUrl = "dynamic/radmonOutputData.js"; |
167 |
-var httpRequest = new XMLHttpRequest(); |
|
168 |
-var graphPeriod = 1; |
|
169 | 168 |
|
170 |
-/* Chart elements */ |
|
169 |
+/* Global DOM objects */ |
|
170 |
+ |
|
171 |
+// Chart elements |
|
171 | 172 |
var cpmChart_g = document.getElementById("cpmChart"); |
172 | 173 |
var uSvChart_g = document.getElementById("uSvChart"); |
173 | 174 |
|
174 |
-/* Text elements */ |
|
175 |
+// Text elements |
|
175 | 176 |
var date_e = document.getElementById("date"); |
176 | 177 |
var time_e = document.getElementById("time"); |
177 | 178 |
var cpm_e = document.getElementById("cpm"); |
... | ... |
@@ -180,16 +181,28 @@ var uSvPerHr_e = document.getElementById("uSvPerHr"); |
180 | 181 |
var mode_e = document.getElementById("mode"); |
181 | 182 |
var status_e = document.getElementById("status"); |
182 | 183 |
|
184 |
+/* Global objects */ |
|
185 |
+ |
|
186 |
+var httpRequest = new XMLHttpRequest(); |
|
187 |
+ |
|
188 |
+/* Global variables */ |
|
189 |
+ |
|
190 |
+var graphPeriod; |
|
191 |
+ |
|
192 |
+ |
|
183 | 193 |
function main() { |
184 | 194 |
/* Register call back function to process http requests */ |
185 | 195 |
httpRequest.onreadystatechange = function() { |
186 | 196 |
if (httpRequest.readyState == 4 && httpRequest.status == 200) { |
187 | 197 |
var dataArray = JSON.parse(httpRequest.responseText); |
188 | 198 |
displayData(dataArray[0]); |
199 |
+ } else if (httpRequest.readyState == 4 && httpRequest.status == 404) { |
|
200 |
+ displayOfflineStatus(); |
|
189 | 201 |
} |
190 | 202 |
}; |
191 | 203 |
|
192 | 204 |
getRadmonData(); |
205 |
+ graphPeriod = 1; |
|
193 | 206 |
getRadmonGraphs(); |
194 | 207 |
setInterval(getRadmonData, 5000); |
195 | 208 |
setInterval(getRadmonGraphs, 300000); |
... | ... |
@@ -251,6 +264,20 @@ function displayData(dataItem) { |
251 | 264 |
} |
252 | 265 |
} |
253 | 266 |
|
267 |
+function displayOfflineStatus(dataItem) { |
|
268 |
+ var d = new Date(); |
|
269 |
+ localTimeZone = d.getTimezoneOffset() / 60; |
|
270 |
+ date_e.innerHTML = (d.getMonth() + 1) + "/" + d.getDate() + "/" + d.getFullYear(); |
|
271 |
+ time_e.innerHTML = d.getHours() + ":" + d.getMinutes() + " <small>(GMT+" + |
|
272 |
+ localTimeZone + ")</small>"; |
|
273 |
+ cpm_e.innerHTML = ""; |
|
274 |
+ cps_e.innerHTML = ""; |
|
275 |
+ uSvPerHr_e.innerHTML = ""; |
|
276 |
+ mode_e.innerHTML = ""; |
|
277 |
+ status_e.innerHTML = "offline"; |
|
278 |
+ status_e.style.color = "red"; |
|
279 |
+} |
|
280 |
+ |
|
254 | 281 |
</script> |
255 | 282 |
|
256 | 283 |
</body> |