Browse code

minor revision

Gandolf authored on 07/09/2021 23:54:21
Showing 1 changed files
... ...
@@ -60,7 +60,7 @@ _USE_RADMON_TIMESTAMP = True
60 60
    ### DEFAULT RADIATION MONITOR URL ###
61 61
 
62 62
 _DEFAULT_RADIATION_MONITOR_URL = \
63
-    "{your radiation monitor url"
63
+    "{your radiation monitor url}"
64 64
 
65 65
     ### FILE AND FOLDER LOCATIONS ###
66 66
 
Browse code

minor revision

Gandolf authored on 07/09/2021 23:53:38
Showing 1 changed files
... ...
@@ -60,7 +60,7 @@ _USE_RADMON_TIMESTAMP = True
60 60
    ### DEFAULT RADIATION MONITOR URL ###
61 61
 
62 62
 _DEFAULT_RADIATION_MONITOR_URL = \
63
-    "http://192.168.1.24"
63
+    "{your radiation monitor url"
64 64
 
65 65
     ### FILE AND FOLDER LOCATIONS ###
66 66
 
Browse code

minor revision

Gandolf authored on 07/09/2021 23:37:24
Showing 1 changed files
... ...
@@ -36,6 +36,8 @@
36 36
 #   * v23 released 16 Nov 2018 by J L Owrey: improved fault handling
37 37
 #         and data conversion
38 38
 #   * v24 released 14 Jun 2021 by J L Owrey; minor revisions
39
+#   * v25 released 9 Jul 2021 by J L Owrey; improved handling of
40
+#         monitor status function
39 41
 #
40 42
 #2345678901234567890123456789012345678901234567890123456789012345678901234567890
41 43
 
... ...
@@ -58,7 +60,7 @@ _USE_RADMON_TIMESTAMP = True
58 60
    ### DEFAULT RADIATION MONITOR URL ###
59 61
 
60 62
 _DEFAULT_RADIATION_MONITOR_URL = \
61
-    "{your radiation monitor url}"
63
+    "http://192.168.1.24"
62 64
 
63 65
     ### FILE AND FOLDER LOCATIONS ###
64 66
 
... ...
@@ -100,7 +102,7 @@ debugMode = False
100 102
 # count of failed attempts to get data from radiation monitor
101 103
 failedUpdateCount = 0
102 104
 # detected status of radiation monitor device
103
-radmonOnline = True
105
+radmonOnline = False
104 106
 
105 107
 # status of reset command to radiation monitor
106 108
 remoteDeviceReset = False
... ...
@@ -147,11 +149,9 @@ def terminateAgentProcess(signal, frame):
147 149
            signal, frame - dummy parameters
148 150
        Returns: nothing
149 151
     """
150
-    # Inform downstream clients by removing output data file.
151
-    if os.path.exists(_OUTPUT_DATA_FILE):
152
-       os.remove(_OUTPUT_DATA_FILE)
153 152
     print('%s terminating radmon agent process' % \
154 153
               (getTimeStamp()))
154
+    setStatusToOffline()
155 155
     sys.exit(0)
156 156
 ##end def
157 157
 
... ...
@@ -174,12 +174,8 @@ def getRadiationData(dData):
174 174
 
175 175
     try:
176 176
         currentTime = time.time()
177
-
178 177
         response = urlopen(sUrl, timeout=_HTTP_REQUEST_TIMEOUT)
179
-
180
-        if verboseMode:
181
-            requestTime = time.time() - currentTime
182
-            print("http request: %.4f seconds" % requestTime)
178
+        requestTime = time.time() - currentTime
183 179
 
184 180
         content = response.read().decode('utf-8')
185 181
         content = content.replace('\n', '')
... ...
@@ -198,9 +194,10 @@ def getRadiationData(dData):
198 194
 
199 195
     if debugMode:
200 196
         print(content)
197
+    if verboseMode:
198
+        print("http request successful: %.4f sec" % requestTime)
201 199
     
202 200
     dData['content'] = content
203
-
204 201
     return True
205 202
 ##end def
206 203
 
... ...
@@ -221,17 +218,19 @@ def parseDataString(dData):
221 218
         print("%s parseDataString: %s" % (getTimeStamp(), exError))
222 219
         return False
223 220
 
221
+    # Verfy the expected number of data items have been received.
222
+    if len(lData) != 5:
223
+        print("%s parse failed: corrupted data string" % getTimeStamp())
224
+        return False;
225
+
224 226
     # Load the parsed data into a dictionary for easy access.
225 227
     for item in lData:
226 228
         if "=" in item:
227 229
             dData[item.split('=')[0]] = item.split('=')[1]
230
+
228 231
     # Add status to dictionary object
229 232
     dData['status'] = 'online'
230
-
231
-    # Verfy the expected number of data items have been received.
232
-    if len(dData) != 6:
233
-        print("%s parse failed: corrupted data string" % getTimeStamp())
234
-        return False;
233
+    dData['serverMode'] = _SERVER_MODE
235 234
 
236 235
     return True
237 236
 ##end def
... ...
@@ -278,18 +277,11 @@ def writeOutputFile(dData):
278 277
                    to the output data file
279 278
        Returns: True if successful, False otherwise
280 279
     """
281
-    # Create temporary copy of output data dictionary
282
-    # and remove unnecessary items.
283
-    dTemp = dict(dData)
284
-    dTemp.pop('ELT')
285
-    dTemp.pop('UTC')
286
-
287 280
     # Format the radmon data as string using java script object notation.
288 281
     jsData = json.loads("{}")
289 282
     try:
290
-        for key in dTemp:
291
-            jsData.update({key:dTemp[key]})
292
-        jsData.update({"serverMode":"%s" % _SERVER_MODE })
283
+        for key in dData:
284
+            jsData.update({key:dData[key]})
293 285
         sData = "[%s]" % json.dumps(jsData)
294 286
     except Exception as exError:
295 287
         print("%s writeOutputFile: %s" % (getTimeStamp(), exError))
... ...
@@ -310,6 +302,35 @@ def writeOutputFile(dData):
310 302
     return True
311 303
 ## end def
312 304
 
305
+def setRadmonStatus(updateSuccess):
306
+    """Detect if radiation monitor is offline or not available on
307
+       the network. After a set number of attempts to get data
308
+       from the monitor set a flag that the radmon is offline.
309
+       Parameters:
310
+           updateSuccess - a boolean that is True if data request
311
+                           successful, False otherwise
312
+       Returns: nothing
313
+    """
314
+    global failedUpdateCount, radmonOnline
315
+
316
+    if updateSuccess:
317
+        failedUpdateCount = 0
318
+        # Set status and send a message to the log if the device
319
+        # previously offline and is now online.
320
+        if not radmonOnline:
321
+            print('%s radiation monitor online' % getTimeStamp())
322
+            radmonOnline = True
323
+        return
324
+    elif failedUpdateCount == _MAX_FAILED_DATA_REQUESTS - 1:
325
+        # Max number of failed data requests, so set
326
+        # device status to offline.
327
+        setStatusToOffline()
328
+    ## end if
329
+    failedUpdateCount += 1
330
+##end def
331
+
332
+    ### DATABASE FUNCTIONS ###
333
+
313 334
 def updateDatabase(dData):
314 335
     """
315 336
     Update the rrdtool database by executing an rrdtool system command.
... ...
@@ -343,40 +364,11 @@ def updateDatabase(dData):
343 364
         return False
344 365
 
345 366
     if verboseMode and not debugMode:
346
-        print("update database")
367
+        print("database update successful")
347 368
 
348 369
     return True
349 370
 ##end def
350 371
 
351
-def setRadmonStatus(updateSuccess):
352
-    """Detect if radiation monitor is offline or not available on
353
-       the network. After a set number of attempts to get data
354
-       from the monitor set a flag that the radmon is offline.
355
-       Parameters:
356
-           updateSuccess - a boolean that is True if data request
357
-                           successful, False otherwise
358
-       Returns: nothing
359
-    """
360
-    global failedUpdateCount, radmonOnline
361
-
362
-    if updateSuccess:
363
-        failedUpdateCount = 0
364
-        # Set status and send a message to the log if the radmon was
365
-        # previously offline and is now online.
366
-        if not radmonOnline:
367
-            print('%s radiation monitor online' % getTimeStamp())
368
-            radmonOnline = True
369
-    else:
370
-        # The last attempt failed, so update the failed attempts
371
-        # count.
372
-        failedUpdateCount += 1
373
-
374
-    if failedUpdateCount >= _MAX_FAILED_DATA_REQUESTS:
375
-        # Max number of failed data requests, so set
376
-        # monitor status to offline.
377
-        setStatusToOffline()
378
-##end def
379
-
380 372
 def createGraph(fileName, dataItem, gLabel, gTitle, gStart,
381 373
                 lower, upper, addTrend, autoScale):
382 374
     """Uses rrdtool to create a graph of specified radmon data item.
... ...
@@ -514,6 +506,7 @@ def main():
514 506
     signal.signal(signal.SIGTERM, terminateAgentProcess)
515 507
     signal.signal(signal.SIGINT, terminateAgentProcess)
516 508
 
509
+    print('===================')
517 510
     print('%s starting up radmon agent process' % \
518 511
                   (getTimeStamp()))
519 512
 
Browse code

minor revisions

Gandolf authored on 06/23/2021 00:44:49
Showing 1 changed files
... ...
@@ -1,4 +1,4 @@
1
-#!/usr/bin/python -u
1
+#!/usr/bin/python3 -u
2 2
 # The -u option above turns off block buffering of python output. This 
3 3
 # assures that each error message gets individually printed to the log file.
4 4
 #
... ...
@@ -35,23 +35,30 @@
35 35
 #         improved radmon device offline status handling
36 36
 #   * v23 released 16 Nov 2018 by J L Owrey: improved fault handling
37 37
 #         and data conversion
38
+#   * v24 released 14 Jun 2021 by J L Owrey; minor revisions
39
+#
38 40
 #2345678901234567890123456789012345678901234567890123456789012345678901234567890
39 41
 
40 42
 import os
41
-import urllib2
42 43
 import sys
43 44
 import signal
44 45
 import subprocess
45 46
 import multiprocessing
46 47
 import time
47 48
 import calendar
49
+import json
50
+from urllib.request import urlopen
51
+
52
+   ### ENVIRONMENT ###
48 53
 
49 54
 _USER = os.environ['USER']
55
+_SERVER_MODE = "primary"
56
+_USE_RADMON_TIMESTAMP = True
50 57
 
51 58
    ### DEFAULT RADIATION MONITOR URL ###
52 59
 
53
-# ip address of radiation monitoring device
54
-_DEFAULT_RADIATION_MONITOR_URL = "{your radiation monitor url}"
60
+_DEFAULT_RADIATION_MONITOR_URL = \
61
+    "{your radiation monitor url}"
55 62
 
56 63
     ### FILE AND FOLDER LOCATIONS ###
57 64
 
... ...
@@ -60,42 +67,40 @@ _DOCROOT_PATH = "/home/%s/public_html/radmon/" % _USER
60 67
 # folder for charts and output data file
61 68
 _CHARTS_DIRECTORY = _DOCROOT_PATH + "dynamic/"
62 69
 # location of data output file
63
-_OUTPUT_DATA_FILE = _DOCROOT_PATH + "dynamic/radmonOutputData.js"
70
+_OUTPUT_DATA_FILE = _DOCROOT_PATH + "dynamic/radmonData.js"
64 71
 # database that stores radmon data
65 72
 _RRD_FILE = "/home/%s/database/radmonData.rrd" % _USER
66 73
 
67 74
     ### GLOBAL CONSTANTS ###
68 75
 
69 76
 # max number of failed data requests allowed
70
-_MAX_FAILED_DATA_REQUESTS = 2
71
-# interval in seconds between data requests to radiation monitor
72
-_DEFAULT_DATA_REQUEST_INTERVAL = 5
73
-# defines how often the charts get updated in seconds
74
-_CHART_UPDATE_INTERVAL = 300
75
-# defines how often the database gets updated
76
-_DATABASE_UPDATE_INTERVAL = 30
77
+_MAX_FAILED_DATA_REQUESTS = 3
78
+# interval in seconds between data requests
79
+_DEFAULT_DATA_REQUEST_INTERVAL = 2
77 80
 # number seconds to wait for a response to HTTP request
78 81
 _HTTP_REQUEST_TIMEOUT = 3
82
+
83
+# interval in seconds between database updates
84
+_DATABASE_UPDATE_INTERVAL = 30
85
+# interval in seconds between chart updates
86
+_CHART_UPDATE_INTERVAL = 300
79 87
 # standard chart width in pixels
80 88
 _CHART_WIDTH = 600
81 89
 # standard chart height in pixels
82 90
 _CHART_HEIGHT = 150
83
-# source of time stamp attached to output data file
84
-_USE_RADMON_TIMESTAMP = True
85 91
 
86 92
    ### GLOBAL VARIABLES ###
87 93
 
88 94
 # turn on or off of verbose debugging information
89
-debugOption = False
90
-verboseDebug = False
95
+verboseMode = False
96
+debugMode = False
91 97
 
92 98
 # The following two items are used for detecting system faults
93 99
 # and radiation monitor online or offline status.
94
-
95 100
 # count of failed attempts to get data from radiation monitor
96 101
 failedUpdateCount = 0
97 102
 # detected status of radiation monitor device
98
-stationOnline = True
103
+radmonOnline = True
99 104
 
100 105
 # status of reset command to radiation monitor
101 106
 remoteDeviceReset = False
... ...
@@ -122,16 +127,16 @@ def setStatusToOffline():
122 127
        Parameters: none
123 128
        Returns: nothing
124 129
     """
125
-    global stationOnline
130
+    global radmonOnline
126 131
 
127 132
     # Inform downstream clients by removing output data file.
128 133
     if os.path.exists(_OUTPUT_DATA_FILE):
129 134
        os.remove(_OUTPUT_DATA_FILE)
130 135
     # If the radiation monitor was previously online, then send
131 136
     # a message that we are now offline.
132
-    if stationOnline:
133
-        print '%s radiation monitor offline' % getTimeStamp()
134
-    stationOnline = False
137
+    if radmonOnline:
138
+        print('%s radiation monitor offline' % getTimeStamp())
139
+    radmonOnline = False
135 140
 ##end def
136 141
 
137 142
 def terminateAgentProcess(signal, frame):
... ...
@@ -145,14 +150,14 @@ def terminateAgentProcess(signal, frame):
145 150
     # Inform downstream clients by removing output data file.
146 151
     if os.path.exists(_OUTPUT_DATA_FILE):
147 152
        os.remove(_OUTPUT_DATA_FILE)
148
-    print '%s terminating radmon agent process' % \
149
-              (getTimeStamp())
153
+    print('%s terminating radmon agent process' % \
154
+              (getTimeStamp()))
150 155
     sys.exit(0)
151 156
 ##end def
152 157
 
153 158
   ###  PUBLIC METHODS  ###
154 159
 
155
-def getRadiationData():
160
+def getRadiationData(dData):
156 161
     """Send http request to radiation monitoring device.  The
157 162
        response from the device contains the radiation data as
158 163
        unformatted ascii text.
... ...
@@ -160,8 +165,6 @@ def getRadiationData():
160 165
        Returns: a string containing the radiation data if successful,
161 166
                 or None if not successful
162 167
     """
163
-    global remoteDeviceReset
164
-
165 168
     sUrl = radiationMonitorUrl
166 169
 
167 170
     if remoteDeviceReset:
... ...
@@ -170,49 +173,64 @@ def getRadiationData():
170 173
         sUrl += "/rdata" # request data from the monitor
171 174
 
172 175
     try:
173
-        conn = urllib2.urlopen(sUrl, timeout=_HTTP_REQUEST_TIMEOUT)
176
+        currentTime = time.time()
177
+
178
+        response = urlopen(sUrl, timeout=_HTTP_REQUEST_TIMEOUT)
179
+
180
+        if verboseMode:
181
+            requestTime = time.time() - currentTime
182
+            print("http request: %.4f seconds" % requestTime)
174 183
 
175
-        # Format received data into a single string.
176
-        content = ""
177
-        for line in conn:
178
-            content += line.strip()
179
-        del conn
184
+        content = response.read().decode('utf-8')
185
+        content = content.replace('\n', '')
186
+        content = content.replace('\r', '')
187
+        if content == "":
188
+            raise Exception("empty response")
180 189
 
181
-    except Exception, exError:
190
+    except Exception as exError:
182 191
         # If no response is received from the device, then assume that
183 192
         # the device is down or unavailable over the network.  In
184 193
         # that case return None to the calling function.
185
-        if debugOption:
186
-            print "http error: %s" % exError
187
-        return None
194
+        if verboseMode:
195
+            print("%s getRadiationData: %s" % (getTimeStamp(), exError))
196
+        return False
197
+    ##end try
198
+
199
+    if debugMode:
200
+        print(content)
201
+    
202
+    dData['content'] = content
188 203
 
189
-    return content
204
+    return True
190 205
 ##end def
191 206
 
192
-def parseDataString(sData, dData):
193
-    """Parse the radiation data JSON string from the radiation 
194
-       monitoring device into its component parts.  
207
+def parseDataString(dData):
208
+    """Parse the data string returned by the radiation monitor
209
+       into its component parts.
195 210
        Parameters:
196
-           sData - the string containing the data to be parsed
197
-           dData - a dictionary object to contain the parsed data items
211
+            dData - a dictionary object to contain the parsed data items
198 212
        Returns: True if successful, False otherwise
199 213
     """
214
+    # Example radiation monitor data string
215
+    # $,UTC=17:09:33 6/22/2021,CPS=0,CPM=26,uSv/hr=0.14,Mode=SLOW,#
216
+    
200 217
     try:
201
-        sTmp = sData[2:-2]
202
-        lsTmp = sTmp.split(',')
203
-    except Exception, exError:
204
-        print "%s parseDataString: %s" % (getTimeStamp(), exError)
218
+        sData = dData.pop('content')
219
+        lData = sData[2:-2].split(',')
220
+    except Exception as exError:
221
+        print("%s parseDataString: %s" % (getTimeStamp(), exError))
205 222
         return False
206 223
 
207 224
     # Load the parsed data into a dictionary for easy access.
208
-    for item in lsTmp:
225
+    for item in lData:
209 226
         if "=" in item:
210 227
             dData[item.split('=')[0]] = item.split('=')[1]
228
+    # Add status to dictionary object
211 229
     dData['status'] = 'online'
212 230
 
213 231
     # Verfy the expected number of data items have been received.
214 232
     if len(dData) != 6:
215
-        print "%s parse failed: corrupted data string" % getTimeStamp()
233
+        print("%s parse failed: corrupted data string" % getTimeStamp())
216 234
         return False;
217 235
 
218 236
     return True
... ...
@@ -238,88 +256,60 @@ def convertData(dData):
238 256
             # that occur when the radiation monitoring device fails to
239 257
             # synchronize with a valid NTP time server.
240 258
             dData['ELT'] = time.time()
241
-        
242
-        dData['Mode'] = dData['Mode'].lower()
259
+
260
+        dData['date'] = \
261
+            time.strftime("%m/%d/%Y %T", time.localtime(dData['ELT']))      
262
+        dData['mode'] = dData.pop('Mode').lower()
243 263
         dData['uSvPerHr'] = '%.2f' % float(dData.pop('uSv/hr'))
244 264
 
245
-    except Exception, exError:
246
-        print "%s data conversion failed: %s" % (getTimeStamp(), exError)
265
+    except Exception as exError:
266
+        print("%s data conversion failed: %s" % (getTimeStamp(), exError))
247 267
         return False
248 268
 
249 269
     return True
250 270
 ##end def
251 271
 
252
-def writeOutputDataFile(dData):
272
+def writeOutputFile(dData):
253 273
     """Write radiation data items to the output data file, formatted as 
254
-       a Javascript file.  This file may then be accessed and used by
274
+       a JSON file.  This file may then be accessed and used by
255 275
        by downstream clients, for instance, in HTML documents.
256 276
        Parameters:
257 277
            dData - a dictionary object containing the data to be written
258 278
                    to the output data file
259 279
        Returns: True if successful, False otherwise
260 280
     """
261
-    # Create temporary copy of output data items.
281
+    # Create temporary copy of output data dictionary
282
+    # and remove unnecessary items.
262 283
     dTemp = dict(dData)
263
-    # Set date to current time and data
264
-    dTemp['date'] = time.strftime("%m/%d/%Y %T", time.localtime(dData['ELT']))
265
-    # Remove unnecessary data items.
266 284
     dTemp.pop('ELT')
267 285
     dTemp.pop('UTC')
268 286
 
269 287
     # Format the radmon data as string using java script object notation.
270
-    sData = '[{'
271
-    for key in dTemp:
272
-        sData += '\"%s\":\"%s\",' % (key, dTemp[key])
273
-    sData = sData[:-1] + '}]\n'
288
+    jsData = json.loads("{}")
289
+    try:
290
+        for key in dTemp:
291
+            jsData.update({key:dTemp[key]})
292
+        jsData.update({"serverMode":"%s" % _SERVER_MODE })
293
+        sData = "[%s]" % json.dumps(jsData)
294
+    except Exception as exError:
295
+        print("%s writeOutputFile: %s" % (getTimeStamp(), exError))
296
+        return False
274 297
 
275
-    if verboseDebug:
276
-        print sData,
298
+    if debugMode:
299
+        print(sData)
277 300
 
278 301
     # Write the string to the output data file for use by html documents.
279 302
     try:
280 303
         fc = open(_OUTPUT_DATA_FILE, "w")
281 304
         fc.write(sData)
282 305
         fc.close()
283
-    except Exception, exError:
284
-        print "%s writeOutputDataFile: %s" % (getTimeStamp(), exError)
306
+    except Exception as exError:
307
+        print("%s writeOutputFile: %s" % (getTimeStamp(), exError))
285 308
         return False
286 309
 
287 310
     return True
288 311
 ## end def
289 312
 
290
-def setStationStatus(updateSuccess):
291
-    """Detect if radiation monitor is offline or not available on
292
-       the network. After a set number of attempts to get data
293
-       from the monitor set a flag that the station is offline.
294
-       Parameters:
295
-           updateSuccess - a boolean that is True if data request
296
-                           successful, False otherwise
297
-       Returns: nothing
298
-    """
299
-    global failedUpdateCount, stationOnline
300
-
301
-    if updateSuccess:
302
-        failedUpdateCount = 0
303
-        # Set status and send a message to the log if the station was
304
-        # previously offline and is now online.
305
-        if not stationOnline:
306
-            print '%s radiation monitor online' % getTimeStamp()
307
-            stationOnline = True
308
-        if debugOption:
309
-            print 'data request successful'
310
-    else:
311
-        # The last attempt failed, so update the failed attempts
312
-        # count.
313
-        failedUpdateCount += 1
314
-        if debugOption:
315
-           print 'data request failed'
316
-
317
-    if failedUpdateCount >= _MAX_FAILED_DATA_REQUESTS:
318
-        # Max number of failed data requests, so set
319
-        # monitor status to offline.
320
-        setStatusToOffline()
321
-##end def
322
-
323 313
 def updateDatabase(dData):
324 314
     """
325 315
     Update the rrdtool database by executing an rrdtool system command.
... ...
@@ -337,24 +327,54 @@ def updateDatabase(dData):
337 327
     # Format the rrdtool update command.
338 328
     strCmd = "rrdtool update %s %s:%s:%s" % \
339 329
                        (_RRD_FILE, dData['ELT'], dData['CPM'], SvPerHr)
340
-    if verboseDebug:
341
-        print "%s" % strCmd # DEBUG
330
+    if debugMode:
331
+        print("%s" % strCmd) # DEBUG
342 332
 
343 333
     # Run the command as a subprocess.
344 334
     try:
345 335
         subprocess.check_output(strCmd, shell=True,  \
346 336
                              stderr=subprocess.STDOUT)
347
-    except subprocess.CalledProcessError, exError:
348
-        print "%s: rrdtool update failed: %s" % \
349
-                    (getTimeStamp(), exError.output)
337
+    except subprocess.CalledProcessError as exError:
338
+        print("%s: rrdtool update failed: %s" % \
339
+                    (getTimeStamp(), exError.output))
350 340
         if exError.output.find("illegal attempt to update using time") > -1:
351 341
             remoteDeviceReset = True
352
-            print "%s: rebooting radiation monitor" % (getTimeStamp())
342
+            print("%s: rebooting radiation monitor" % (getTimeStamp()))
353 343
         return False
344
+
345
+    if verboseMode and not debugMode:
346
+        print("update database")
347
+
348
+    return True
349
+##end def
350
+
351
+def setRadmonStatus(updateSuccess):
352
+    """Detect if radiation monitor is offline or not available on
353
+       the network. After a set number of attempts to get data
354
+       from the monitor set a flag that the radmon is offline.
355
+       Parameters:
356
+           updateSuccess - a boolean that is True if data request
357
+                           successful, False otherwise
358
+       Returns: nothing
359
+    """
360
+    global failedUpdateCount, radmonOnline
361
+
362
+    if updateSuccess:
363
+        failedUpdateCount = 0
364
+        # Set status and send a message to the log if the radmon was
365
+        # previously offline and is now online.
366
+        if not radmonOnline:
367
+            print('%s radiation monitor online' % getTimeStamp())
368
+            radmonOnline = True
354 369
     else:
355
-        if debugOption:
356
-            print 'database update sucessful'
357
-        return True
370
+        # The last attempt failed, so update the failed attempts
371
+        # count.
372
+        failedUpdateCount += 1
373
+
374
+    if failedUpdateCount >= _MAX_FAILED_DATA_REQUESTS:
375
+        # Max number of failed data requests, so set
376
+        # monitor status to offline.
377
+        setStatusToOffline()
358 378
 ##end def
359 379
 
360 380
 def createGraph(fileName, dataItem, gLabel, gTitle, gStart,
... ...
@@ -403,27 +423,27 @@ def createGraph(fileName, dataItem, gLabel, gTitle, gStart,
403 423
     if addTrend == 0:
404 424
         strCmd += "LINE1:dSeries#0400ff "
405 425
     elif addTrend == 1:
406
-        strCmd += "CDEF:smoothed=dSeries,%s,TREND LINE3:smoothed#ff0000 " \
426
+        strCmd += "CDEF:smoothed=dSeries,%s,TREND LINE2:smoothed#006600 " \
407 427
                   % trendWindow[gStart]
408 428
     elif addTrend == 2:
409 429
         strCmd += "LINE1:dSeries#0400ff "
410
-        strCmd += "CDEF:smoothed=dSeries,%s,TREND LINE3:smoothed#ff0000 " \
430
+        strCmd += "CDEF:smoothed=dSeries,%s,TREND LINE2:smoothed#006600 " \
411 431
                   % trendWindow[gStart]
412 432
      
413
-    if verboseDebug:
414
-        print "\n%s" % strCmd # DEBUG
433
+    if debugMode:
434
+        print("\n%s" % strCmd) # DEBUG
415 435
     
416 436
     # Run the formatted rrdtool command as a subprocess.
417 437
     try:
418 438
         result = subprocess.check_output(strCmd, \
419 439
                      stderr=subprocess.STDOUT,   \
420 440
                      shell=True)
421
-    except subprocess.CalledProcessError, exError:
422
-        print "rrdtool graph failed: %s" % (exError.output)
441
+    except subprocess.CalledProcessError as exError:
442
+        print("rrdtool graph failed: %s" % (exError.output))
423 443
         return False
424 444
 
425
-    if debugOption:
426
-        print "rrdtool graph: %s" % result,
445
+    if verboseMode:
446
+        print("rrdtool graph: %s" % result.decode('utf-8'), end='')
427 447
     return True
428 448
 
429 449
 ##end def
... ...
@@ -435,14 +455,17 @@ def generateGraphs():
435 455
     """
436 456
     autoScale = False
437 457
 
458
+    # past 24 hours
438 459
     createGraph('24hr_cpm', 'CPM', 'counts\ per\ minute', 
439 460
                 'CPM\ -\ Last\ 24\ Hours', 'end-1day', 0, 0, 2, autoScale)
440 461
     createGraph('24hr_svperhr', 'SvperHr', 'Sv\ per\ hour',
441 462
                 'Sv/Hr\ -\ Last\ 24\ Hours', 'end-1day', 0, 0, 2, autoScale)
463
+    # past 4 weeks
442 464
     createGraph('4wk_cpm', 'CPM', 'counts\ per\ minute',
443 465
                 'CPM\ -\ Last\ 4\ Weeks', 'end-4weeks', 0, 0, 2, autoScale)
444 466
     createGraph('4wk_svperhr', 'SvperHr', 'Sv\ per\ hour',
445 467
                 'Sv/Hr\ -\ Last\ 4\ Weeks', 'end-4weeks', 0, 0, 2, autoScale)
468
+    # past year
446 469
     createGraph('12m_cpm', 'CPM', 'counts\ per\ minute',
447 470
                 'CPM\ -\ Past\ Year', 'end-12months', 0, 0, 2, autoScale)
448 471
     createGraph('12m_svperhr', 'SvperHr', 'Sv\ per\ hour',
... ...
@@ -452,34 +475,32 @@ def generateGraphs():
452 475
 def getCLarguments():
453 476
     """Get command line arguments.  There are four possible arguments
454 477
           -d turns on debug mode
455
-          -v turns on verbose debug mode
478
+          -v turns on verbose mode
456 479
           -t sets the radiation device query interval
457 480
           -u sets the url of the radiation monitoring device
458 481
        Returns: nothing
459 482
     """
460
-    global debugOption, verboseDebug, dataRequestInterval, \
483
+    global verboseMode, debugMode, dataRequestInterval, \
461 484
            radiationMonitorUrl
462 485
 
463 486
     index = 1
464 487
     while index < len(sys.argv):
465
-        if sys.argv[index] == '-d':
466
-            debugOption = True
467
-        elif sys.argv[index] == '-v':
468
-            debugOption = True
469
-            verboseDebug = True
488
+        if sys.argv[index] == '-v':
489
+            verboseMode = True
490
+        elif sys.argv[index] == '-d':
491
+            verboseMode = True
492
+            debugMode = True
470 493
         elif sys.argv[index] == '-t':
471
-            try:
472
-                dataRequestInterval = abs(int(sys.argv[index + 1]))
473
-            except:
474
-                print "invalid polling period"
475
-                exit(-1)
494
+            dataRequestInterval = abs(int(sys.argv[index + 1]))
476 495
             index += 1
477 496
         elif sys.argv[index] == '-u':
478 497
             radiationMonitorUrl = sys.argv[index + 1]
498
+            if radiationMonitorUrl.find('http://') < 0:
499
+                radiationMonitorUrl = 'http://' + radiationMonitorUrl
479 500
             index += 1
480 501
         else:
481 502
             cmd_name = sys.argv[0].split('/')
482
-            print "Usage: %s [-d] [-t seconds] [-u url}" % cmd_name[-1]
503
+            print("Usage: %s [-d] [-t seconds] [-u url}" % cmd_name[-1])
483 504
             exit(-1)
484 505
         index += 1
485 506
 ##end def
... ...
@@ -491,9 +512,10 @@ def main():
491 512
        Returns: nothing
492 513
     """
493 514
     signal.signal(signal.SIGTERM, terminateAgentProcess)
515
+    signal.signal(signal.SIGINT, terminateAgentProcess)
494 516
 
495
-    print '%s starting up radmon agent process' % \
496
-                  (getTimeStamp())
517
+    print('%s starting up radmon agent process' % \
518
+                  (getTimeStamp()))
497 519
 
498 520
     # last time output JSON file updated
499 521
     lastDataRequestTime = -1
... ...
@@ -507,9 +529,9 @@ def main():
507 529
 
508 530
     ## Exit with error if rrdtool database does not exist.
509 531
     if not os.path.exists(_RRD_FILE):
510
-        print 'rrdtool database does not exist\n' \
532
+        print('rrdtool database does not exist\n' \
511 533
               'use createRadmonRrd script to ' \
512
-              'create rrdtool database\n'
534
+              'create rrdtool database\n')
513 535
         exit(1)
514 536
  
515 537
     ## main loop
... ...
@@ -517,21 +539,18 @@ def main():
517 539
 
518 540
         currentTime = time.time() # get current time in seconds
519 541
 
520
-        # Every web update interval request data from the radiation
542
+        # Every data update interval request data from the radiation
521 543
         # monitor and process the received data.
522 544
         if currentTime - lastDataRequestTime > dataRequestInterval:
523 545
             lastDataRequestTime = currentTime
524 546
             dData = {}
525
-            result = True
526 547
 
527 548
             # Get the data string from the device.
528
-            sData = getRadiationData()
529
-            if sData == None:
530
-                result = False
549
+            result = getRadiationData(dData)
531 550
 
532 551
             # If successful parse the data.
533 552
             if result:
534
-                result = parseDataString(sData, dData)
553
+                result = parseDataString(dData)
535 554
 
536 555
             # If parsing successful, convert the data.
537 556
             if result:
... ...
@@ -539,18 +558,18 @@ def main():
539 558
 
540 559
             # If conversion successful, write data to data files.
541 560
             if result:
542
-                writeOutputDataFile(dData)
561
+                writeOutputFile(dData)
543 562
 
544
-                # At the rrdtool database update interval, update the database.
545
-                if currentTime - lastDatabaseUpdateTime > \
546
-                        _DATABASE_UPDATE_INTERVAL:   
547
-                    lastDatabaseUpdateTime = currentTime
548
-                    ## Update the round robin database with the parsed data.
549
-                    updateDatabase(dData)
563
+            # At the rrdtool database update interval, update the database.
564
+            if result and (currentTime - lastDatabaseUpdateTime > \
565
+                           _DATABASE_UPDATE_INTERVAL):   
566
+                lastDatabaseUpdateTime = currentTime
567
+                ## Update the round robin database with the parsed data.
568
+                result = updateDatabase(dData)
550 569
 
551
-            # Set the station status to online or offline depending on the
570
+            # Set the radmon status to online or offline depending on the
552 571
             # success or failure of the above operations.
553
-            setStationStatus(result)
572
+            setRadmonStatus(result)
554 573
 
555 574
 
556 575
         # At the chart generation interval, generate charts.
... ...
@@ -563,10 +582,13 @@ def main():
563 582
         # the next update interval.
564 583
 
565 584
         elapsedTime = time.time() - currentTime
566
-        if debugOption and not verboseDebug:
567
-            pass #print
568
-        if verboseDebug:
569
-            print "processing time: %6f sec\n" % elapsedTime
585
+        if verboseMode:
586
+            if result:
587
+                print("update successful: %6f sec\n"
588
+                      % elapsedTime)
589
+            else:
590
+                print("update failed: %6f sec\n"
591
+                      % elapsedTime)
570 592
         remainingTime = dataRequestInterval - elapsedTime
571 593
         if remainingTime > 0.0:
572 594
             time.sleep(remainingTime)
... ...
@@ -575,8 +597,5 @@ def main():
575 597
 ## end def
576 598
 
577 599
 if __name__ == '__main__':
578
-    try:
579
-        main()
580
-    except KeyboardInterrupt:
581
-        print '\n',
582
-        terminateAgentProcess('KeyboardInterrupt','Module')
600
+    main()
601
+
Browse code

added global option for timestamp source

Gandolf authored on 12/02/2019 02:52:21
Showing 1 changed files
... ...
@@ -51,7 +51,7 @@ _USER = os.environ['USER']
51 51
    ### DEFAULT RADIATION MONITOR URL ###
52 52
 
53 53
 # ip address of radiation monitoring device
54
-_DEFAULT_RADIATION_MONITOR_URL = "http://192.168.1.24"
54
+_DEFAULT_RADIATION_MONITOR_URL = "{your radiation monitor url}"
55 55
 
56 56
     ### FILE AND FOLDER LOCATIONS ###
57 57
 
Browse code

added global option for timestamp source

Gandolf authored on 12/02/2019 02:49:26
Showing 1 changed files
... ...
@@ -11,7 +11,7 @@
11 11
 #     - conversion of data items
12 12
 #     - update a round robin (rrdtool) database with the radiation data
13 13
 #     - periodically generate graphic charts for display in html documents
14
-#     - write the processed weather data to a JSON file for use by html
14
+#     - write the processed radmon data to a JSON file for use by html
15 15
 #       documents
16 16
 #
17 17
 # Copyright 2015 Jeff Owrey
... ...
@@ -35,6 +35,7 @@
35 35
 #         improved radmon device offline status handling
36 36
 #   * v23 released 16 Nov 2018 by J L Owrey: improved fault handling
37 37
 #         and data conversion
38
+#2345678901234567890123456789012345678901234567890123456789012345678901234567890
38 39
 
39 40
 import os
40 41
 import urllib2
... ...
@@ -50,7 +51,7 @@ _USER = os.environ['USER']
50 51
    ### DEFAULT RADIATION MONITOR URL ###
51 52
 
52 53
 # ip address of radiation monitoring device
53
-_DEFAULT_RADIATION_MONITOR_URL = "{your radiation monitor url}"
54
+_DEFAULT_RADIATION_MONITOR_URL = "http://192.168.1.24"
54 55
 
55 56
     ### FILE AND FOLDER LOCATIONS ###
56 57
 
... ...
@@ -58,11 +59,9 @@ _DEFAULT_RADIATION_MONITOR_URL = "{your radiation monitor url}"
58 59
 _DOCROOT_PATH = "/home/%s/public_html/radmon/" % _USER
59 60
 # folder for charts and output data file
60 61
 _CHARTS_DIRECTORY = _DOCROOT_PATH + "dynamic/"
61
-# location of data input file
62
-_INPUT_DATA_FILE = _DOCROOT_PATH + "dynamic/radmonInputData.dat"
63 62
 # location of data output file
64 63
 _OUTPUT_DATA_FILE = _DOCROOT_PATH + "dynamic/radmonOutputData.js"
65
-# database that stores weather data
64
+# database that stores radmon data
66 65
 _RRD_FILE = "/home/%s/database/radmonData.rrd" % _USER
67 66
 
68 67
     ### GLOBAL CONSTANTS ###
... ...
@@ -81,6 +80,8 @@ _HTTP_REQUEST_TIMEOUT = 3
81 80
 _CHART_WIDTH = 600
82 81
 # standard chart height in pixels
83 82
 _CHART_HEIGHT = 150
83
+# source of time stamp attached to output data file
84
+_USE_RADMON_TIMESTAMP = True
84 85
 
85 86
    ### GLOBAL VARIABLES ###
86 87
 
... ...
@@ -123,13 +124,9 @@ def setStatusToOffline():
123 124
     """
124 125
     global stationOnline
125 126
 
126
-    # Inform downstream clients by removing input and output
127
-    # data files.
128
-    if os.path.exists(_INPUT_DATA_FILE):
129
-        os.remove(_INPUT_DATA_FILE)
127
+    # Inform downstream clients by removing output data file.
130 128
     if os.path.exists(_OUTPUT_DATA_FILE):
131 129
        os.remove(_OUTPUT_DATA_FILE)
132
-
133 130
     # If the radiation monitor was previously online, then send
134 131
     # a message that we are now offline.
135 132
     if stationOnline:
... ...
@@ -145,15 +142,11 @@ def terminateAgentProcess(signal, frame):
145 142
            signal, frame - dummy parameters
146 143
        Returns: nothing
147 144
     """
145
+    # Inform downstream clients by removing output data file.
146
+    if os.path.exists(_OUTPUT_DATA_FILE):
147
+       os.remove(_OUTPUT_DATA_FILE)
148 148
     print '%s terminating radmon agent process' % \
149 149
               (getTimeStamp())
150
-
151
-    # Inform downstream clients by removing input and output
152
-    # data files.
153
-    if os.path.exists(_OUTPUT_DATA_FILE):
154
-        os.remove(_OUTPUT_DATA_FILE)
155
-    if os.path.exists(_INPUT_DATA_FILE):
156
-        os.remove(_INPUT_DATA_FILE)
157 150
     sys.exit(0)
158 151
 ##end def
159 152
 
... ...
@@ -232,18 +225,19 @@ def convertData(dData):
232 225
        Returns: True if successful, False otherwise
233 226
     """
234 227
     try:
235
-        # Convert the UTC timestamp provided by the radiation monitoring
236
-        # device to epoch local time in seconds.
237
-        ts_utc = time.strptime(dData['UTC'], "%H:%M:%S %m/%d/%Y")
238
-        epoch_local_sec = calendar.timegm(ts_utc)
239
-        dData['ELT'] = epoch_local_sec
240
-
241
-        # Uncomment the code line below to use a timestamp generated by the
242
-        # requesting server (this) instead of the timestamp provided by the
243
-        # radiation monitoring device.  Using the server generated timestamp
244
-        # prevents errors that occur when the radiation monitoring device
245
-        # fails to synchronize with a valid NTP time server.
246
-        #dData['ELT'] = time.time()
228
+        if _USE_RADMON_TIMESTAMP:
229
+            # Convert the UTC timestamp provided by the radiation monitoring
230
+            # device to epoch local time in seconds.
231
+            ts_utc = time.strptime(dData['UTC'], "%H:%M:%S %m/%d/%Y")
232
+            epoch_local_sec = calendar.timegm(ts_utc)
233
+            dData['ELT'] = epoch_local_sec
234
+        else:
235
+            # Use a timestamp generated by the requesting server (this)
236
+            # instead of the timestamp provided by the radiation monitoring
237
+            # device.  Using the server generated timestamp prevents errors
238
+            # that occur when the radiation monitoring device fails to
239
+            # synchronize with a valid NTP time server.
240
+            dData['ELT'] = time.time()
247 241
         
248 242
         dData['Mode'] = dData['Mode'].lower()
249 243
         dData['uSvPerHr'] = '%.2f' % float(dData.pop('uSv/hr'))
... ...
@@ -264,22 +258,22 @@ def writeOutputDataFile(dData):
264 258
                    to the output data file
265 259
        Returns: True if successful, False otherwise
266 260
     """
261
+    # Create temporary copy of output data items.
262
+    dTemp = dict(dData)
267 263
     # Set date to current time and data
268
-    dData['date'] = time.strftime("%m/%d/%Y %T", time.localtime(dData['ELT']))
269
-
264
+    dTemp['date'] = time.strftime("%m/%d/%Y %T", time.localtime(dData['ELT']))
270 265
     # Remove unnecessary data items.
271
-    dTemp = dict(dData)
272 266
     dTemp.pop('ELT')
273 267
     dTemp.pop('UTC')
274
-    
275
-    # Format the weather data as string using java script object notation.
268
+
269
+    # Format the radmon data as string using java script object notation.
276 270
     sData = '[{'
277 271
     for key in dTemp:
278
-        sData += '\"%s\":\"%s\",' % (key, dData[key])
272
+        sData += '\"%s\":\"%s\",' % (key, dTemp[key])
279 273
     sData = sData[:-1] + '}]\n'
280 274
 
281 275
     if verboseDebug:
282
-        print sData
276
+        print sData,
283 277
 
284 278
     # Write the string to the output data file for use by html documents.
285 279
     try:
... ...
@@ -293,26 +287,6 @@ def writeOutputDataFile(dData):
293 287
     return True
294 288
 ## end def
295 289
 
296
-def writeInputDataFile(sData):
297
-    """Write raw data from radiation monitor to the input data file.
298
-       This file may then be accessed by downstream mirror servers.
299
-       Parameters:
300
-           sData - a string object containing the raw data from
301
-                   the radiation monitor
302
-       Returns: True if successful, False otherwise
303
-    """
304
-    sData += "\n"
305
-    try:
306
-        fc = open(_INPUT_DATA_FILE, "w")
307
-        fc.write(sData)
308
-        fc.close()
309
-    except Exception, exError:
310
-        print "%s writeInputDataFile: %s" % (getTimeStamp(), exError)
311
-        return False
312
-
313
-    return True
314
-##end def
315
-
316 290
 def setStationStatus(updateSuccess):
317 291
     """Detect if radiation monitor is offline or not available on
318 292
        the network. After a set number of attempts to get data
... ...
@@ -332,13 +306,13 @@ def setStationStatus(updateSuccess):
332 306
             print '%s radiation monitor online' % getTimeStamp()
333 307
             stationOnline = True
334 308
         if debugOption:
335
-            print 'radiation update successful'
309
+            print 'data request successful'
336 310
     else:
337 311
         # The last attempt failed, so update the failed attempts
338 312
         # count.
339 313
         failedUpdateCount += 1
340 314
         if debugOption:
341
-           print 'radiation update failed'
315
+           print 'data request failed'
342 316
 
343 317
     if failedUpdateCount >= _MAX_FAILED_DATA_REQUESTS:
344 318
         # Max number of failed data requests, so set
... ...
@@ -346,7 +320,6 @@ def setStationStatus(updateSuccess):
346 320
         setStatusToOffline()
347 321
 ##end def
348 322
 
349
-
350 323
 def updateDatabase(dData):
351 324
     """
352 325
     Update the rrdtool database by executing an rrdtool system command.
... ...
@@ -386,7 +359,7 @@ def updateDatabase(dData):
386 359
 
387 360
 def createGraph(fileName, dataItem, gLabel, gTitle, gStart,
388 361
                 lower, upper, addTrend, autoScale):
389
-    """Uses rrdtool to create a graph of specified weather data item.
362
+    """Uses rrdtool to create a graph of specified radmon data item.
390 363
        Parameters:
391 364
            fileName - name of file containing the graph
392 365
            dataItem - data item to be graphed
... ...
@@ -438,7 +411,7 @@ def createGraph(fileName, dataItem, gLabel, gTitle, gStart,
438 411
                   % trendWindow[gStart]
439 412
      
440 413
     if verboseDebug:
441
-        print "%s\n" % strCmd # DEBUG
414
+        print "\n%s" % strCmd # DEBUG
442 415
     
443 416
     # Run the formatted rrdtool command as a subprocess.
444 417
     try:
... ...
@@ -450,7 +423,7 @@ def createGraph(fileName, dataItem, gLabel, gTitle, gStart,
450 423
         return False
451 424
 
452 425
     if debugOption:
453
-        print "rrdtool graph: %s" % result
426
+        print "rrdtool graph: %s" % result,
454 427
     return True
455 428
 
456 429
 ##end def
... ...
@@ -535,7 +508,7 @@ def main():
535 508
     ## Exit with error if rrdtool database does not exist.
536 509
     if not os.path.exists(_RRD_FILE):
537 510
         print 'rrdtool database does not exist\n' \
538
-              'use createWeatherRrd script to ' \
511
+              'use createRadmonRrd script to ' \
539 512
               'create rrdtool database\n'
540 513
         exit(1)
541 514
  
... ...
@@ -566,7 +539,6 @@ def main():
566 539
 
567 540
             # If conversion successful, write data to data files.
568 541
             if result:
569
-                writeInputDataFile(sData)
570 542
                 writeOutputDataFile(dData)
571 543
 
572 544
                 # At the rrdtool database update interval, update the database.
... ...
@@ -592,7 +564,7 @@ def main():
592 564
 
593 565
         elapsedTime = time.time() - currentTime
594 566
         if debugOption and not verboseDebug:
595
-            print
567
+            pass #print
596 568
         if verboseDebug:
597 569
             print "processing time: %6f sec\n" % elapsedTime
598 570
         remainingTime = dataRequestInterval - elapsedTime
Browse code

rev_20190802

Gandolf authored on 08/02/2019 19:39:44
Showing 1 changed files
... ...
@@ -36,8 +36,6 @@
36 36
 #   * v23 released 16 Nov 2018 by J L Owrey: improved fault handling
37 37
 #         and data conversion
38 38
 
39
-_MIRROR_SERVER = False
40
-
41 39
 import os
42 40
 import urllib2
43 41
 import sys
... ...
@@ -53,9 +51,6 @@ _USER = os.environ['USER']
53 51
 
54 52
 # ip address of radiation monitoring device
55 53
 _DEFAULT_RADIATION_MONITOR_URL = "{your radiation monitor url}"
56
-# url if this is a mirror server
57
-_PRIMARY_SERVER_URL = "{your primary server url}" \
58
-                      "/radmon/dynamic/radmonInputData.dat"
59 54
 
60 55
     ### FILE AND FOLDER LOCATIONS ###
61 56
 
... ...
@@ -174,14 +169,12 @@ def getRadiationData():
174 169
     """
175 170
     global remoteDeviceReset
176 171
 
177
-    if _MIRROR_SERVER:
178
-        sUrl = _PRIMARY_SERVER_URL
172
+    sUrl = radiationMonitorUrl
173
+
174
+    if remoteDeviceReset:
175
+        sUrl += "/reset" # reboot the radiation monitor
179 176
     else:
180
-        sUrl = radiationMonitorUrl
181
-        if remoteDeviceReset:
182
-            sUrl += "/reset" # reboot the radiation monitor
183
-        else:
184
-            sUrl += "/rdata" # request data from the monitor
177
+        sUrl += "/rdata" # request data from the monitor
185 178
 
186 179
     try:
187 180
         conn = urllib2.urlopen(sUrl, timeout=_HTTP_REQUEST_TIMEOUT)
... ...
@@ -285,6 +278,9 @@ def writeOutputDataFile(dData):
285 278
         sData += '\"%s\":\"%s\",' % (key, dData[key])
286 279
     sData = sData[:-1] + '}]\n'
287 280
 
281
+    if verboseDebug:
282
+        print sData
283
+
288 284
     # Write the string to the output data file for use by html documents.
289 285
     try:
290 286
         fc = open(_OUTPUT_DATA_FILE, "w")
Browse code

revisions 11-17-2018

fractalxaos authored on 11/17/2018 19:58:01
Showing 1 changed files
... ...
@@ -5,13 +5,12 @@
5 5
 # Module: radmonAgent.py
6 6
 #
7 7
 # Description: This module acts as an agent between the radiation monitoring
8
-# device and the Internet web server.  The agent periodically sends an http
8
+# device and Internet web services.  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 itemsq
11
+#     - conversion of data items
12 12
 #     - update a round robin (rrdtool) database with the radiation data
13 13
 #     - periodically generate graphic charts for display in html documents
14
-#     - forward the radiation data to other services
15 14
 #     - write the processed weather data to a JSON file for use by html
16 15
 #       documents
17 16
 #
... ...
@@ -56,7 +55,7 @@ _USER = os.environ['USER']
56 55
 _DEFAULT_RADIATION_MONITOR_URL = "{your radiation monitor url}"
57 56
 # url if this is a mirror server
58 57
 _PRIMARY_SERVER_URL = "{your primary server url}" \
59
-                      "/{user}/radmon/dynamic/radmonInputData.dat"
58
+                      "/radmon/dynamic/radmonInputData.dat"
60 59
 
61 60
     ### FILE AND FOLDER LOCATIONS ###
62 61
 
... ...
@@ -72,6 +71,7 @@ _OUTPUT_DATA_FILE = _DOCROOT_PATH + "dynamic/radmonOutputData.js"
72 71
 _RRD_FILE = "/home/%s/database/radmonData.rrd" % _USER
73 72
 
74 73
     ### GLOBAL CONSTANTS ###
74
+
75 75
 # max number of failed data requests allowed
76 76
 _MAX_FAILED_DATA_REQUESTS = 2
77 77
 # interval in seconds between data requests to radiation monitor
... ...
@@ -91,10 +91,16 @@ _CHART_HEIGHT = 150
91 91
 
92 92
 # turn on or off of verbose debugging information
93 93
 debugOption = False
94
-# used for detecting system faults and radiation monitor
95
-# online or offline status
94
+verboseDebug = False
95
+
96
+# The following two items are used for detecting system faults
97
+# and radiation monitor online or offline status.
98
+
99
+# count of failed attempts to get data from radiation monitor
96 100
 failedUpdateCount = 0
101
+# detected status of radiation monitor device
97 102
 stationOnline = True
103
+
98 104
 # status of reset command to radiation monitor
99 105
 remoteDeviceReset = False
100 106
 # ip address of radiation monitor
... ...
@@ -106,41 +112,49 @@ dataRequestInterval = _DEFAULT_DATA_REQUEST_INTERVAL
106 112
 
107 113
 def getTimeStamp():
108 114
     """
109
-    Sets the error message time stamp to the local system time.
115
+    Set the error message time stamp to the local system time.
110 116
     Parameters: none
111
-    Returns string containing the time stamp.
117
+    Returns: string containing the time stamp
112 118
     """
113 119
     return time.strftime( "%m/%d/%Y %T", time.localtime() )
114 120
 ##end def
115 121
 
116 122
 def setStatusToOffline():
117
-    """Set the status of the the upstream device to "offline" and sends
118
-       blank data to the downstream clients.
119
-       Parameters:
120
-           dData - dictionary object containing weather data
121
-       Returns nothing.
123
+    """Set the detected status of the radiation monitor to
124
+       "offline" and inform downstream clients by removing input
125
+       and output data files.
126
+       Parameters: none
127
+       Returns: nothing
122 128
     """
123 129
     global stationOnline
124 130
 
131
+    # Inform downstream clients by removing input and output
132
+    # data files.
125 133
     if os.path.exists(_INPUT_DATA_FILE):
126 134
         os.remove(_INPUT_DATA_FILE)
127 135
     if os.path.exists(_OUTPUT_DATA_FILE):
128 136
        os.remove(_OUTPUT_DATA_FILE)
129 137
 
130
-    # If the radiation monitor was previously online, then send a message
131
-    # that we are now offline.
138
+    # If the radiation monitor was previously online, then send
139
+    # a message that we are now offline.
132 140
     if stationOnline:
133 141
         print '%s radiation monitor offline' % getTimeStamp()
134 142
     stationOnline = False
135 143
 ##end def
136 144
 
137 145
 def terminateAgentProcess(signal, frame):
138
-    """Send message to log when process killed
139
-       Parameters: signal, frame - sigint parameters
146
+    """Send a message to log when the agent process gets killed
147
+       by the operating system.  Inform downstream clients
148
+       by removing input and output data files.
149
+       Parameters:
150
+           signal, frame - dummy parameters
140 151
        Returns: nothing
141 152
     """
142 153
     print '%s terminating radmon agent process' % \
143 154
               (getTimeStamp())
155
+
156
+    # Inform downstream clients by removing input and output
157
+    # data files.
144 158
     if os.path.exists(_OUTPUT_DATA_FILE):
145 159
         os.remove(_OUTPUT_DATA_FILE)
146 160
     if os.path.exists(_INPUT_DATA_FILE):
... ...
@@ -151,15 +165,12 @@ def terminateAgentProcess(signal, frame):
151 165
   ###  PUBLIC METHODS  ###
152 166
 
153 167
 def getRadiationData():
154
-    """Send http request to radiation monitoring device.  The response
155
-       from the device contains the radiation data.  The data is formatted
156
-       as an html document.
157
-    Parameters: 
158
-        radiationMonitorUrl - url of radiation monitoring device
159
-        HttpRequesttimeout - how long to wait for device
160
-                             to respond to http request
161
-    Returns a string containing the radiation data, or None if
162
-    not successful.
168
+    """Send http request to radiation monitoring device.  The
169
+       response from the device contains the radiation data as
170
+       unformatted ascii text.
171
+       Parameters: none 
172
+       Returns: a string containing the radiation data if successful,
173
+                or None if not successful
163 174
     """
164 175
     global remoteDeviceReset
165 176
 
... ...
@@ -168,9 +179,9 @@ def getRadiationData():
168 179
     else:
169 180
         sUrl = radiationMonitorUrl
170 181
         if remoteDeviceReset:
171
-            sUrl += "/reset"
182
+            sUrl += "/reset" # reboot the radiation monitor
172 183
         else:
173
-            sUrl += "/rdata"
184
+            sUrl += "/rdata" # request data from the monitor
174 185
 
175 186
     try:
176 187
         conn = urllib2.urlopen(sUrl, timeout=_HTTP_REQUEST_TIMEOUT)
... ...
@@ -184,7 +195,7 @@ def getRadiationData():
184 195
     except Exception, exError:
185 196
         # If no response is received from the device, then assume that
186 197
         # the device is down or unavailable over the network.  In
187
-        # that case set the status of the device to offline.
198
+        # that case return None to the calling function.
188 199
         if debugOption:
189 200
             print "http error: %s" % exError
190 201
         return None
... ...
@@ -198,7 +209,7 @@ def parseDataString(sData, dData):
198 209
        Parameters:
199 210
            sData - the string containing the data to be parsed
200 211
            dData - a dictionary object to contain the parsed data items
201
-       Returns true if successful, false otherwise.
212
+       Returns: True if successful, False otherwise
202 213
     """
203 214
     try:
204 215
         sTmp = sData[2:-2]
... ...
@@ -213,6 +224,7 @@ def parseDataString(sData, dData):
213 224
             dData[item.split('=')[0]] = item.split('=')[1]
214 225
     dData['status'] = 'online'
215 226
 
227
+    # Verfy the expected number of data items have been received.
216 228
     if len(dData) != 6:
217 229
         print "%s parse failed: corrupted data string" % getTimeStamp()
218 230
         return False;
... ...
@@ -223,12 +235,9 @@ def parseDataString(sData, dData):
223 235
 def convertData(dData):
224 236
     """Convert individual radiation data items as necessary.
225 237
        Parameters:
226
-           lsData - a list object containing the radiation data
227 238
            dData - a dictionary object containing the radiation data
228
-       Returns true if successful, false otherwise.
239
+       Returns: True if successful, False otherwise
229 240
     """
230
-    result = True
231
- 
232 241
     try:
233 242
         # Convert the UTC timestamp provided by the radiation monitoring
234 243
         # device to epoch local time in seconds.
... ...
@@ -248,22 +257,24 @@ def convertData(dData):
248 257
 
249 258
     except Exception, exError:
250 259
         print "%s data conversion failed: %s" % (getTimeStamp(), exError)
251
-        result = False
260
+        return False
252 261
 
253
-    return result
262
+    return True
254 263
 ##end def
255 264
 
256 265
 def writeOutputDataFile(dData):
257
-    """Write radiation data items to a JSON formatted file for use by
258
-       HTML documents.
266
+    """Write radiation data items to the output data file, formatted as 
267
+       a Javascript file.  This file may then be accessed and used by
268
+       by downstream clients, for instance, in HTML documents.
259 269
        Parameters:
260
-           lsData - a list object containing the data to be written
261
-                    to the JSON file
262
-       Returns true if successful, false otherwise.
270
+           dData - a dictionary object containing the data to be written
271
+                   to the output data file
272
+       Returns: True if successful, False otherwise
263 273
     """
264 274
     # Set date to current time and data
265 275
     dData['date'] = time.strftime("%m/%d/%Y %T", time.localtime(dData['ELT']))
266 276
 
277
+    # Remove unnecessary data items.
267 278
     dTemp = dict(dData)
268 279
     dTemp.pop('ELT')
269 280
     dTemp.pop('UTC')
... ...
@@ -287,12 +298,12 @@ def writeOutputDataFile(dData):
287 298
 ## end def
288 299
 
289 300
 def writeInputDataFile(sData):
290
-    """Write raw data from radiation monitor to file for use by mirror
291
-       servers.
301
+    """Write raw data from radiation monitor to the input data file.
302
+       This file may then be accessed by downstream mirror servers.
292 303
        Parameters:
293
-           sData - a string object containing the data string from
304
+           sData - a string object containing the raw data from
294 305
                    the radiation monitor
295
-       Returns true if successful, false otherwise.
306
+       Returns: True if successful, False otherwise
296 307
     """
297 308
     sData += "\n"
298 309
     try:
... ...
@@ -307,6 +318,14 @@ def writeInputDataFile(sData):
307 318
 ##end def
308 319
 
309 320
 def setStationStatus(updateSuccess):
321
+    """Detect if radiation monitor is offline or not available on
322
+       the network. After a set number of attempts to get data
323
+       from the monitor set a flag that the station is offline.
324
+       Parameters:
325
+           updateSuccess - a boolean that is True if data request
326
+                           successful, False otherwise
327
+       Returns: nothing
328
+    """
310 329
     global failedUpdateCount, stationOnline
311 330
 
312 331
     if updateSuccess:
... ...
@@ -319,33 +338,37 @@ def setStationStatus(updateSuccess):
319 338
         if debugOption:
320 339
             print 'radiation update successful'
321 340
     else:
341
+        # The last attempt failed, so update the failed attempts
342
+        # count.
322 343
         failedUpdateCount += 1
323 344
         if debugOption:
324 345
            print 'radiation update failed'
325 346
 
326 347
     if failedUpdateCount >= _MAX_FAILED_DATA_REQUESTS:
348
+        # Max number of failed data requests, so set
349
+        # monitor status to offline.
327 350
         setStatusToOffline()
328 351
 ##end def
329 352
 
330 353
 
331 354
 def updateDatabase(dData):
332 355
     """
333
-    Updates the rrdtool database by executing an rrdtool system command.
334
-    Formats the command using the data extracted from the radiation
356
+    Update the rrdtool database by executing an rrdtool system command.
357
+    Format the command using the data extracted from the radiation
335 358
     monitor response.   
336 359
     Parameters: dData - dictionary object containing data items to be
337 360
                         written to the rr database file
338
-    Returns true if successful, false otherwise.
361
+    Returns: True if successful, False otherwise
339 362
     """
340 363
     global remoteDeviceReset
341 364
 
342 365
     # The RR database stores whole units, so convert uSv to Sv.
343 366
     SvPerHr = float(dData['uSvPerHr']) * 1.0E-06 
344 367
 
345
-    # Create the rrdtool update command.
368
+    # Format the rrdtool update command.
346 369
     strCmd = "rrdtool update %s %s:%s:%s" % \
347 370
                        (_RRD_FILE, dData['ELT'], dData['CPM'], SvPerHr)
348
-    if debugOption and False:
371
+    if verboseDebug:
349 372
         print "%s" % strCmd # DEBUG
350 373
 
351 374
     # Run the command as a subprocess.
... ...
@@ -369,10 +392,11 @@ def createGraph(fileName, dataItem, gLabel, gTitle, gStart,
369 392
                 lower, upper, addTrend, autoScale):
370 393
     """Uses rrdtool to create a graph of specified weather data item.
371 394
        Parameters:
372
-           fileName - name of graph image file
395
+           fileName - name of file containing the graph
373 396
            dataItem - data item to be graphed
374 397
            gLabel - string containing a graph label for the data item
375 398
            gTitle - string containing a title for the graph
399
+           gStart - beginning time of the graphed data
376 400
            lower - lower bound for graph ordinate #NOT USED
377 401
            upper - upper bound for graph ordinate #NOT USED
378 402
            addTrend - 0, show only graph data
... ...
@@ -381,7 +405,7 @@ def createGraph(fileName, dataItem, gLabel, gTitle, gStart,
381 405
            autoScale - if True, then use vertical axis auto scaling
382 406
                (lower and upper parameters are ignored), otherwise use
383 407
                lower and upper parameters to set vertical axis scale
384
-       Returns true if successful, false otherwise.
408
+       Returns: True if successful, False otherwise
385 409
     """
386 410
     gPath = _CHARTS_DIRECTORY + fileName + ".png"
387 411
     trendWindow = { 'end-1day': 7200,
... ...
@@ -417,7 +441,7 @@ def createGraph(fileName, dataItem, gLabel, gTitle, gStart,
417 441
         strCmd += "CDEF:smoothed=dSeries,%s,TREND LINE3:smoothed#ff0000 " \
418 442
                   % trendWindow[gStart]
419 443
      
420
-    if debugOption and False:
444
+    if verboseDebug:
421 445
         print "%s\n" % strCmd # DEBUG
422 446
     
423 447
     # Run the formatted rrdtool command as a subprocess.
... ...
@@ -438,7 +462,7 @@ def createGraph(fileName, dataItem, gLabel, gTitle, gStart,
438 462
 def generateGraphs():
439 463
     """Generate graphs for display in html documents.
440 464
        Parameters: none
441
-       Returns nothing.
465
+       Returns: nothing
442 466
     """
443 467
     autoScale = False
444 468
 
... ...
@@ -457,18 +481,23 @@ def generateGraphs():
457 481
 ##end def
458 482
 
459 483
 def getCLarguments():
460
-    """Get command line arguments.  There are three possible arguments
484
+    """Get command line arguments.  There are four possible arguments
461 485
           -d turns on debug mode
486
+          -v turns on verbose debug mode
462 487
           -t sets the radiation device query interval
463 488
           -u sets the url of the radiation monitoring device
464
-       Returns nothing.
489
+       Returns: nothing
465 490
     """
466
-    global debugOption, dataRequestInterval, radiationMonitorUrl
491
+    global debugOption, verboseDebug, dataRequestInterval, \
492
+           radiationMonitorUrl
467 493
 
468 494
     index = 1
469 495
     while index < len(sys.argv):
470 496
         if sys.argv[index] == '-d':
471 497
             debugOption = True
498
+        elif sys.argv[index] == '-v':
499
+            debugOption = True
500
+            verboseDebug = True
472 501
         elif sys.argv[index] == '-t':
473 502
             try:
474 503
                 dataRequestInterval = abs(int(sys.argv[index + 1]))
... ...
@@ -490,7 +519,7 @@ def main():
490 519
     """Handles timing of events and acts as executive routine managing
491 520
        all other functions.
492 521
        Parameters: none
493
-       Returns nothing.
522
+       Returns: nothing
494 523
     """
495 524
     signal.signal(signal.SIGTERM, terminateAgentProcess)
496 525
 
... ...
@@ -566,9 +595,10 @@ def main():
566 595
         # the next update interval.
567 596
 
568 597
         elapsedTime = time.time() - currentTime
569
-        if debugOption:
598
+        if debugOption and not verboseDebug:
570 599
             print
571
-            #print "processing time: %6f sec\n" % elapsedTime
600
+        if verboseDebug:
601
+            print "processing time: %6f sec\n" % elapsedTime
572 602
         remainingTime = dataRequestInterval - elapsedTime
573 603
         if remainingTime > 0.0:
574 604
             time.sleep(remainingTime)
Browse code

revisions 16-11-2018

fractalxaos authored on 11/16/2018 19:57:38
Showing 1 changed files
... ...
@@ -53,10 +53,10 @@ _USER = os.environ['USER']
53 53
    ### DEFAULT RADIATION MONITOR URL ###
54 54
 
55 55
 # ip address of radiation monitoring device
56
-_DEFAULT_RADIATION_MONITOR_URL = "http://192.168.1.24"
56
+_DEFAULT_RADIATION_MONITOR_URL = "{your radiation monitor url}"
57 57
 # url if this is a mirror server
58
-_PRIMARY_SERVER_URL = "http://73.157.139.23:7361" \
59
-                      "/~pi/radmon/dynamic/radmonInputData.dat"
58
+_PRIMARY_SERVER_URL = "{your primary server url}" \
59
+                      "/{user}/radmon/dynamic/radmonInputData.dat"
60 60
 
61 61
     ### FILE AND FOLDER LOCATIONS ###
62 62
 
Browse code

revisions 11-16-2018

fractalxaos authored on 11/16/2018 19:15:09
Showing 1 changed files
... ...
@@ -34,10 +34,10 @@
34 34
 #   * v21 released 27 Nov 2017 by J L Owrey; bug fixes; updates
35 35
 #   * v22 released 03 Mar 2018 by J L Owrey; improved code readability;
36 36
 #         improved radmon device offline status handling
37
-#   * v23 released 15 Nov 2018 by J L Owrey; improved system fault
38
-#         handling and radiation monitor offline handling
37
+#   * v23 released 16 Nov 2018 by J L Owrey: improved fault handling
38
+#         and data conversion
39 39
 
40
-_MIRROR_SERVER = True
40
+_MIRROR_SERVER = False
41 41
 
42 42
 import os
43 43
 import urllib2
... ...
@@ -53,9 +53,10 @@ _USER = os.environ['USER']
53 53
    ### DEFAULT RADIATION MONITOR URL ###
54 54
 
55 55
 # ip address of radiation monitoring device
56
-_DEFAULT_RADIATION_MONITOR_URL = "{your radiation monitor url}"
56
+_DEFAULT_RADIATION_MONITOR_URL = "http://192.168.1.24"
57 57
 # url if this is a mirror server
58
-_PRIMARY_SERVER_URL = "{your primary server url}"
58
+_PRIMARY_SERVER_URL = "http://73.157.139.23:7361" \
59
+                      "/~pi/radmon/dynamic/radmonInputData.dat"
59 60
 
60 61
     ### FILE AND FOLDER LOCATIONS ###
61 62
 
... ...
@@ -243,15 +244,10 @@ def convertData(dData):
243 244
         #dData['ELT'] = time.time()
244 245
         
245 246
         dData['Mode'] = dData['Mode'].lower()
246
- 
247
-        dData['uSvPerHr'] = float(dData.pop('uSv/hr'))
248
-        
249
-        dData['CPM'] = int(dData.pop('CPM'))
250
-
251
-        dData['CPS'] = int(dData.pop('CPS'))
247
+        dData['uSvPerHr'] = '%.2f' % float(dData.pop('uSv/hr'))
252 248
 
253 249
     except Exception, exError:
254
-        print "%s convert data failed: %s" % (getTimeStamp(), exError)
250
+        print "%s data conversion failed: %s" % (getTimeStamp(), exError)
255 251
         result = False
256 252
 
257 253
     return result
... ...
@@ -268,14 +264,14 @@ def writeOutputDataFile(dData):
268 264
     # Set date to current time and data
269 265
     dData['date'] = time.strftime("%m/%d/%Y %T", time.localtime(dData['ELT']))
270 266
 
267
+    dTemp = dict(dData)
268
+    dTemp.pop('ELT')
269
+    dTemp.pop('UTC')
270
+    
271 271
     # Format the weather data as string using java script object notation.
272 272
     sData = '[{'
273
-    sData += "\"date\":\"%s\"," % dData['date']
274
-    sData += "\"CPM\":\"%d\"," % dData['CPM']
275
-    sData += "\"CPS\":\"%d\"," % dData['CPS']
276
-    sData += "\"uSvPerHr\":\"%.2f\"," % dData['uSvPerHr']
277
-    sData += "\"Mode\":\"%s\"," % dData['Mode']
278
-    sData += "\"status\":\"%s\"," % dData['status']
273
+    for key in dTemp:
274
+        sData += '\"%s\":\"%s\",' % (key, dData[key])
279 275
     sData = sData[:-1] + '}]\n'
280 276
 
281 277
     # Write the string to the output data file for use by html documents.
... ...
@@ -304,7 +300,7 @@ def writeInputDataFile(sData):
304 300
         fc.write(sData)
305 301
         fc.close()
306 302
     except Exception, exError:
307
-        print "%s writeOutputDataFile: %s" % (getTimeStamp(), exError)
303
+        print "%s writeInputDataFile: %s" % (getTimeStamp(), exError)
308 304
         return False
309 305
 
310 306
     return True
... ...
@@ -344,7 +340,7 @@ def updateDatabase(dData):
344 340
     global remoteDeviceReset
345 341
 
346 342
     # The RR database stores whole units, so convert uSv to Sv.
347
-    SvPerHr = dData['uSvPerHr'] * 1.0E-06 
343
+    SvPerHr = float(dData['uSvPerHr']) * 1.0E-06 
348 344
 
349 345
     # Create the rrdtool update command.
350 346
     strCmd = "rrdtool update %s %s:%s:%s" % \
Browse code

revisions 11-15-2018

fractalxaos authored on 11/16/2018 07:38:00
Showing 1 changed files
... ...
@@ -55,7 +55,7 @@ _USER = os.environ['USER']
55 55
 # ip address of radiation monitoring device
56 56
 _DEFAULT_RADIATION_MONITOR_URL = "{your radiation monitor url}"
57 57
 # url if this is a mirror server
58
-_PRIMARY_SERVER_URL = "{your mirror server url}"
58
+_PRIMARY_SERVER_URL = "{your primary server url}"
59 59
 
60 60
     ### FILE AND FOLDER LOCATIONS ###
61 61
 
Browse code

revisions 11-15-2018

fractalxaos authored on 11/16/2018 07:31:51
Showing 1 changed files
... ...
@@ -53,10 +53,9 @@ _USER = os.environ['USER']
53 53
    ### DEFAULT RADIATION MONITOR URL ###
54 54
 
55 55
 # ip address of radiation monitoring device
56
-_DEFAULT_RADIATION_MONITOR_URL = "http://192.168.1.24"
56
+_DEFAULT_RADIATION_MONITOR_URL = "{your radiation monitor url}"
57 57
 # url if this is a mirror server
58
-_PRIMARY_SERVER_URL = "http://73.157.139.23:7361" \
59
-                      "/~pi/radmon/dynamic/radmonInputData.dat"
58
+_PRIMARY_SERVER_URL = "{your mirror server url}"
60 59
 
61 60
     ### FILE AND FOLDER LOCATIONS ###
62 61
 
Browse code

revisions 11-15-2018

fractalxaos authored on 11/16/2018 07:25:46
Showing 1 changed files
... ...
@@ -15,7 +15,7 @@
15 15
 #     - write the processed weather data to a JSON file for use by html
16 16
 #       documents
17 17
 #
18
-# Copyright 2018 Jeff Owrey
18
+# Copyright 2015 Jeff Owrey
19 19
 #    This program is free software: you can redistribute it and/or modify
20 20
 #    it under the terms of the GNU General Public License as published by
21 21
 #    the Free Software Foundation, either version 3 of the License, or
... ...
@@ -34,9 +34,10 @@
34 34
 #   * v21 released 27 Nov 2017 by J L Owrey; bug fixes; updates
35 35
 #   * v22 released 03 Mar 2018 by J L Owrey; improved code readability;
36 36
 #         improved radmon device offline status handling
37
-#
37
+#   * v23 released 15 Nov 2018 by J L Owrey; improved system fault
38
+#         handling and radiation monitor offline handling
38 39
 
39
-_MIRROR_SERVER = False
40
+_MIRROR_SERVER = True
40 41
 
41 42
 import os
42 43
 import urllib2
... ...
@@ -52,9 +53,10 @@ _USER = os.environ['USER']
52 53
    ### DEFAULT RADIATION MONITOR URL ###
53 54
 
54 55
 # ip address of radiation monitoring device
55
-_DEFAULT_RADIATION_MONITOR_URL = "{your radiation monitor url}"
56
+_DEFAULT_RADIATION_MONITOR_URL = "http://192.168.1.24"
56 57
 # url if this is a mirror server
57
-_PRIMARY_SERVER_URL = "{your primary server url}"
58
+_PRIMARY_SERVER_URL = "http://73.157.139.23:7361" \
59
+                      "/~pi/radmon/dynamic/radmonInputData.dat"
58 60
 
59 61
     ### FILE AND FOLDER LOCATIONS ###
60 62
 
... ...
@@ -89,10 +91,10 @@ _CHART_HEIGHT = 150
89 91
 
90 92
 # turn on or off of verbose debugging information
91 93
 debugOption = False
92
-# online status of radiation monitor
93
-radiationMonitorOnline = True
94
-# number of unsuccessful http requests
95
-failedDataRequestCount = 0
94
+# used for detecting system faults and radiation monitor
95
+# online or offline status
96
+failedUpdateCount = 0
97
+stationOnline = True
96 98
 # status of reset command to radiation monitor
97 99
 remoteDeviceReset = False
98 100
 # ip address of radiation monitor
... ...
@@ -111,33 +113,28 @@ def getTimeStamp():
111 113
     return time.strftime( "%m/%d/%Y %T", time.localtime() )
112 114
 ##end def
113 115
 
114
-def setOfflineStatus(dData):
116
+def setStatusToOffline():
115 117
     """Set the status of the the upstream device to "offline" and sends
116 118
        blank data to the downstream clients.
117 119
        Parameters:
118 120
            dData - dictionary object containing weather data
119 121
        Returns nothing.
120 122
     """
121
-    global radiationMonitorOnline, failedDataRequestCount
123
+    global stationOnline
122 124
 
123 125
     if os.path.exists(_INPUT_DATA_FILE):
124 126
         os.remove(_INPUT_DATA_FILE)
125
-
126
-    if failedDataRequestCount < _MAX_FAILED_DATA_REQUESTS - 1:
127
-        failedDataRequestCount += 1
128
-        return
127
+    if os.path.exists(_OUTPUT_DATA_FILE):
128
+       os.remove(_OUTPUT_DATA_FILE)
129 129
 
130 130
     # If the radiation monitor was previously online, then send a message
131 131
     # that we are now offline.
132
-    if radiationMonitorOnline:
133
-        print "%s: radiation monitor offline" % getTimeStamp()
134
-        radiationMonitorOnline = False
135
-        if os.path.exists(_OUTPUT_DATA_FILE):
136
-            os.remove(_OUTPUT_DATA_FILE)
137
-    return
132
+    if stationOnline:
133
+        print '%s radiation monitor offline' % getTimeStamp()
134
+    stationOnline = False
138 135
 ##end def
139 136
 
140
-def signal_term_handler(signal, frame):
137
+def terminateAgentProcess(signal, frame):
141 138
     """Send message to log when process killed
142 139
        Parameters: signal, frame - sigint parameters
143 140
        Returns: nothing
... ...
@@ -164,8 +161,7 @@ def getRadiationData():
164 161
     Returns a string containing the radiation data, or None if
165 162
     not successful.
166 163
     """
167
-    global radiationMonitorOnline, failedDataRequestCount, \
168
-           remoteDeviceReset
164
+    global remoteDeviceReset
169 165
 
170 166
     if _MIRROR_SERVER:
171 167
         sUrl = _PRIMARY_SERVER_URL
... ...
@@ -193,13 +189,6 @@ def getRadiationData():
193 189
             print "http error: %s" % exError
194 190
         return None
195 191
 
196
-    # If the radiation monitor was previously offline, then send a message
197
-    # that we are now online.
198
-    if not radiationMonitorOnline:
199
-        print "%s radiation monitor online" % getTimeStamp()
200
-        radiationMonitorOnline = True
201
-
202
-    failedDataRequestCount = 0
203 192
     return content
204 193
 ##end def
205 194
 
... ...
@@ -322,6 +311,28 @@ def writeInputDataFile(sData):
322 311
     return True
323 312
 ##end def
324 313
 
314
+def setStationStatus(updateSuccess):
315
+    global failedUpdateCount, stationOnline
316
+
317
+    if updateSuccess:
318
+        failedUpdateCount = 0
319
+        # Set status and send a message to the log if the station was
320
+        # previously offline and is now online.
321
+        if not stationOnline:
322
+            print '%s radiation monitor online' % getTimeStamp()
323
+            stationOnline = True
324
+        if debugOption:
325
+            print 'radiation update successful'
326
+    else:
327
+        failedUpdateCount += 1
328
+        if debugOption:
329
+           print 'radiation update failed'
330
+
331
+    if failedUpdateCount >= _MAX_FAILED_DATA_REQUESTS:
332
+        setStatusToOffline()
333
+##end def
334
+
335
+
325 336
 def updateDatabase(dData):
326 337
     """
327 338
     Updates the rrdtool database by executing an rrdtool system command.
... ...
@@ -486,7 +497,7 @@ def main():
486 497
        Parameters: none
487 498
        Returns nothing.
488 499
     """
489
-    signal.signal(signal.SIGTERM, signal_term_handler)
500
+    signal.signal(signal.SIGTERM, terminateAgentProcess)
490 501
 
491 502
     print '%s starting up radmon agent process' % \
492 503
                   (getTimeStamp())
... ...
@@ -523,7 +534,6 @@ def main():
523 534
             # Get the data string from the device.
524 535
             sData = getRadiationData()
525 536
             if sData == None:
526
-                setOfflineStatus(dData)
527 537
                 result = False
528 538
 
529 539
             # If successful parse the data.
... ...
@@ -538,15 +548,18 @@ def main():
538 548
             if result:
539 549
                 writeInputDataFile(sData)
540 550
                 writeOutputDataFile(dData)
541
-                if debugOption:
542
-                    print "http request successful"
543 551
 
544 552
                 # At the rrdtool database update interval, update the database.
545 553
                 if currentTime - lastDatabaseUpdateTime > \
546 554
                         _DATABASE_UPDATE_INTERVAL:   
547 555
                     lastDatabaseUpdateTime = currentTime
548 556
                     ## Update the round robin database with the parsed data.
549
-                    result = updateDatabase(dData)
557
+                    updateDatabase(dData)
558
+
559
+            # Set the station status to online or offline depending on the
560
+            # success or failure of the above operations.
561
+            setStationStatus(result)
562
+
550 563
 
551 564
         # At the chart generation interval, generate charts.
552 565
         if currentTime - lastChartUpdateTime > _CHART_UPDATE_INTERVAL:
... ...
@@ -559,7 +572,8 @@ def main():
559 572
 
560 573
         elapsedTime = time.time() - currentTime
561 574
         if debugOption:
562
-            print "processing time: %6f sec\n" % elapsedTime
575
+            print
576
+            #print "processing time: %6f sec\n" % elapsedTime
563 577
         remainingTime = dataRequestInterval - elapsedTime
564 578
         if remainingTime > 0.0:
565 579
             time.sleep(remainingTime)
... ...
@@ -571,9 +585,5 @@ if __name__ == '__main__':
571 585
     try:
572 586
         main()
573 587
     except KeyboardInterrupt:
574
-        print '\n%s terminating radmon agent process' % \
575
-              (getTimeStamp())
576
-        if os.path.exists(_OUTPUT_DATA_FILE):
577
-            os.remove(_OUTPUT_DATA_FILE)
578
-        if os.path.exists(_INPUT_DATA_FILE):
579
-            os.remove(_INPUT_DATA_FILE)
588
+        print '\n',
589
+        terminateAgentProcess('KeyboardInterrupt','Module')
Browse code

sync 2018-07-27

fractalxaos authored on 07/27/2018 19:27:41
Showing 1 changed files
... ...
@@ -15,7 +15,7 @@
15 15
 #     - write the processed weather data to a JSON file for use by html
16 16
 #       documents
17 17
 #
18
-# Copyright 2015 Jeff Owrey
18
+# Copyright 2018 Jeff Owrey
19 19
 #    This program is free software: you can redistribute it and/or modify
20 20
 #    it under the terms of the GNU General Public License as published by
21 21
 #    the Free Software Foundation, either version 3 of the License, or
... ...
@@ -52,9 +52,9 @@ _USER = os.environ['USER']
52 52
    ### DEFAULT RADIATION MONITOR URL ###
53 53
 
54 54
 # ip address of radiation monitoring device
55
-_DEFAULT_RADIATION_MONITOR_URL = "{your radiation device url}"
55
+_DEFAULT_RADIATION_MONITOR_URL = "{your radiation monitor url}"
56 56
 # url if this is a mirror server
57
-_PRIMARY_SERVER_URL = "{your primary server radiation data url}"
57
+_PRIMARY_SERVER_URL = "{your primary server url}"
58 58
 
59 59
     ### FILE AND FOLDER LOCATIONS ###
60 60
 
Browse code

revisions 2018-07-23

fractalxaos authored on 07/23/2018 21:17:50
Showing 1 changed files
1 1
old mode 100644
2 2
new mode 100755
... ...
@@ -40,7 +40,8 @@ _MIRROR_SERVER = False
40 40
 
41 41
 import os
42 42
 import urllib2
43
-import sys   
43
+import sys
44
+import signal
44 45
 import subprocess
45 46
 import multiprocessing
46 47
 import time
... ...
@@ -69,6 +70,8 @@ _OUTPUT_DATA_FILE = _DOCROOT_PATH + "dynamic/radmonOutputData.js"
69 70
 _RRD_FILE = "/home/%s/database/radmonData.rrd" % _USER
70 71
 
71 72
     ### GLOBAL CONSTANTS ###
73
+# max number of failed data requests allowed
74
+_MAX_FAILED_DATA_REQUESTS = 2
72 75
 # interval in seconds between data requests to radiation monitor
73 76
 _DEFAULT_DATA_REQUEST_INTERVAL = 5
74 77
 # defines how often the charts get updated in seconds
... ...
@@ -77,10 +80,9 @@ _CHART_UPDATE_INTERVAL = 300
77 80
 _DATABASE_UPDATE_INTERVAL = 30
78 81
 # number seconds to wait for a response to HTTP request
79 82
 _HTTP_REQUEST_TIMEOUT = 3
80
-# max number of failed data requests allowed
81
-_MAX_FAILED_DATA_REQUESTS = 2
82
-# radmon chart dimensions
83
+# standard chart width in pixels
83 84
 _CHART_WIDTH = 600
85
+# standard chart height in pixels
84 86
 _CHART_HEIGHT = 150
85 87
 
86 88
    ### GLOBAL VARIABLES ###
... ...
@@ -135,6 +137,20 @@ def setOfflineStatus(dData):
135 137
     return
136 138
 ##end def
137 139
 
140
+def signal_term_handler(signal, frame):
141
+    """Send message to log when process killed
142
+       Parameters: signal, frame - sigint parameters
143
+       Returns: nothing
144
+    """
145
+    print '%s terminating radmon agent process' % \
146
+              (getTimeStamp())
147
+    if os.path.exists(_OUTPUT_DATA_FILE):
148
+        os.remove(_OUTPUT_DATA_FILE)
149
+    if os.path.exists(_INPUT_DATA_FILE):
150
+        os.remove(_INPUT_DATA_FILE)
151
+    sys.exit(0)
152
+##end def
153
+
138 154
   ###  PUBLIC METHODS  ###
139 155
 
140 156
 def getRadiationData():
... ...
@@ -267,9 +283,9 @@ def writeOutputDataFile(dData):
267 283
     # Format the weather data as string using java script object notation.
268 284
     sData = '[{'
269 285
     sData += "\"date\":\"%s\"," % dData['date']
270
-    sData += "\"CPM\":\"%s\"," % dData['CPM']
271
-    sData += "\"CPS\":\"%s\"," % dData['CPS']
272
-    sData += "\"uSvPerHr\":\"%s\"," % dData['uSvPerHr']
286
+    sData += "\"CPM\":\"%d\"," % dData['CPM']
287
+    sData += "\"CPS\":\"%d\"," % dData['CPS']
288
+    sData += "\"uSvPerHr\":\"%.2f\"," % dData['uSvPerHr']
273 289
     sData += "\"Mode\":\"%s\"," % dData['Mode']
274 290
     sData += "\"status\":\"%s\"," % dData['status']
275 291
     sData = sData[:-1] + '}]\n'
... ...
@@ -470,6 +486,10 @@ def main():
470 486
        Parameters: none
471 487
        Returns nothing.
472 488
     """
489
+    signal.signal(signal.SIGTERM, signal_term_handler)
490
+
491
+    print '%s starting up radmon agent process' % \
492
+                  (getTimeStamp())
473 493
 
474 494
     # last time output JSON file updated
475 495
     lastDataRequestTime = -1
... ...
@@ -483,8 +503,9 @@ def main():
483 503
 
484 504
     ## Exit with error if rrdtool database does not exist.
485 505
     if not os.path.exists(_RRD_FILE):
486
-        print "cannot find rrdtool database\nuse createWeatherRrd script to" \
487
-              " create rrdtool database\n"
506
+        print 'rrdtool database does not exist\n' \
507
+              'use createWeatherRrd script to ' \
508
+              'create rrdtool database\n'
488 509
         exit(1)
489 510
  
490 511
     ## main loop
... ...
@@ -550,7 +571,8 @@ if __name__ == '__main__':
550 571
     try:
551 572
         main()
552 573
     except KeyboardInterrupt:
553
-        print '\nInterrupted'
574
+        print '\n%s terminating radmon agent process' % \
575
+              (getTimeStamp())
554 576
         if os.path.exists(_OUTPUT_DATA_FILE):
555 577
             os.remove(_OUTPUT_DATA_FILE)
556 578
         if os.path.exists(_INPUT_DATA_FILE):
Browse code

minor revision 20180306

fractalxaos authored on 03/07/2018 23:54:56
Showing 1 changed files
1 1
old mode 100755
2 2
new mode 100644
... ...
@@ -1,6 +1,6 @@
1 1
 #!/usr/bin/python -u
2
-## The -u option above turns off block buffering of python output. This 
3
-## assures that each error message gets individually printed to the log file.
2
+# The -u option above turns off block buffering of python output. This 
3
+# assures that each error message gets individually printed to the log file.
4 4
 #
5 5
 # Module: radmonAgent.py
6 6
 #
... ...
@@ -48,12 +48,12 @@ import calendar
48 48
 
49 49
 _USER = os.environ['USER']
50 50
 
51
-   ### DEFAULT WEATHER STATION URL ###
51
+   ### DEFAULT RADIATION MONITOR URL ###
52 52
 
53 53
 # ip address of radiation monitoring device
54
-_DEFAULT_RADIATION_MONITOR_URL = '{your radmon device url}'
54
+_DEFAULT_RADIATION_MONITOR_URL = "{your radiation device url}"
55 55
 # url if this is a mirror server
56
-_PRIMARY_SERVER_URL = '{your primary server weather data url}'
56
+_PRIMARY_SERVER_URL = "{your primary server radiation data url}"
57 57
 
58 58
     ### FILE AND FOLDER LOCATIONS ###
59 59
 
... ...
@@ -61,12 +61,12 @@ _PRIMARY_SERVER_URL = '{your primary server weather data url}'
61 61
 _DOCROOT_PATH = "/home/%s/public_html/radmon/" % _USER
62 62
 # folder for charts and output data file
63 63
 _CHARTS_DIRECTORY = _DOCROOT_PATH + "dynamic/"
64
- # database that stores weather data
65
-_RRD_FILE = "/home/%s/database/radmonData.rrd" % _USER
66 64
 # location of data input file
67 65
 _INPUT_DATA_FILE = _DOCROOT_PATH + "dynamic/radmonInputData.dat"
68 66
 # location of data output file
69 67
 _OUTPUT_DATA_FILE = _DOCROOT_PATH + "dynamic/radmonOutputData.js"
68
+# database that stores weather data
69
+_RRD_FILE = "/home/%s/database/radmonData.rrd" % _USER
70 70
 
71 71
     ### GLOBAL CONSTANTS ###
72 72
 # interval in seconds between data requests to radiation monitor
... ...
@@ -153,10 +153,12 @@ def getRadiationData():
153 153
 
154 154
     if _MIRROR_SERVER:
155 155
         sUrl = _PRIMARY_SERVER_URL
156
-    elif remoteDeviceReset:
157
-        sUrl = radiationMonitorUrl + "/reset"
158 156
     else:
159
-        sUrl = radiationMonitorUrl + "/rdata"
157
+        sUrl = radiationMonitorUrl
158
+        if remoteDeviceReset:
159
+            sUrl += "/reset"
160
+        else:
161
+            sUrl += "/rdata"
160 162
 
161 163
     try:
162 164
         conn = urllib2.urlopen(sUrl, timeout=_HTTP_REQUEST_TIMEOUT)
... ...
@@ -207,8 +209,7 @@ def parseDataString(sData, dData):
207 209
     dData['status'] = 'online'
208 210
 
209 211
     if len(dData) != 6:
210
-        print "%s parse failed: corrupted data string: %s" % \
211
-               (getTimeStamp(), sData)
212
+        print "%s parse failed: corrupted data string" % getTimeStamp()
212 213
         return False;
213 214
 
214 215
     return True
... ...
@@ -224,19 +225,18 @@ def convertData(dData):
224 225
     result = True
225 226
  
226 227
     try:
227
-
228
-        # Uncomment below to use timestamp from radiation monitoring device
229
-        # otherwise the requesting server (this) will generate the
230
-        # timestamp. Allowing the server to generate the timestamp
231
-        # prevents timestamp errors due to the radiation monitoring device
232
-        # failing to synchronize with a NTP time server.
233
-
234
-        #dData['UTC'] = time.time()
235
-
236
-        ## Convert UTC from radiation monitoring device to local time.
228
+        # Convert the UTC timestamp provided by the radiation monitoring
229
+        # device to epoch local time in seconds.
237 230
         ts_utc = time.strptime(dData['UTC'], "%H:%M:%S %m/%d/%Y")
238
-        local_sec = calendar.timegm(ts_utc)
239
-        dData['UTC'] = local_sec
231
+        epoch_local_sec = calendar.timegm(ts_utc)
232
+        dData['ELT'] = epoch_local_sec
233
+
234
+        # Uncomment the code line below to use a timestamp generated by the
235
+        # requesting server (this) instead of the timestamp provided by the
236
+        # radiation monitoring device.  Using the server generated timestamp
237
+        # prevents errors that occur when the radiation monitoring device
238
+        # fails to synchronize with a valid NTP time server.
239
+        #dData['ELT'] = time.time()
240 240
         
241 241
         dData['Mode'] = dData['Mode'].lower()
242 242
  
... ...
@@ -247,7 +247,7 @@ def convertData(dData):
247 247
         dData['CPS'] = int(dData.pop('CPS'))
248 248
 
249 249
     except Exception, exError:
250
-        print "%s convertData: %s" % (getTimeStamp(), exError)
250
+        print "%s convert data failed: %s" % (getTimeStamp(), exError)
251 251
         result = False
252 252
 
253 253
     return result
... ...
@@ -262,13 +262,17 @@ def writeOutputDataFile(dData):
262 262
        Returns true if successful, false otherwise.
263 263
     """
264 264
     # Set date to current time and data
265
-    dData['date'] = getTimeStamp()
265
+    dData['date'] = time.strftime("%m/%d/%Y %T", time.localtime(dData['ELT']))
266 266
 
267 267
     # Format the weather data as string using java script object notation.
268 268
     sData = '[{'
269
-    for key in dData:
270
-        sData += "\"%s\":\"%s\"," % (key, dData[key])
271
-    sData = sData[:-1] + '}]'
269
+    sData += "\"date\":\"%s\"," % dData['date']
270
+    sData += "\"CPM\":\"%s\"," % dData['CPM']
271
+    sData += "\"CPS\":\"%s\"," % dData['CPS']
272
+    sData += "\"uSvPerHr\":\"%s\"," % dData['uSvPerHr']
273
+    sData += "\"Mode\":\"%s\"," % dData['Mode']
274
+    sData += "\"status\":\"%s\"," % dData['status']
275
+    sData = sData[:-1] + '}]\n'
272 276
 
273 277
     # Write the string to the output data file for use by html documents.
274 278
     try:
... ...
@@ -318,7 +322,7 @@ def updateDatabase(dData):
318 322
 
319 323
     # Create the rrdtool update command.
320 324
     strCmd = "rrdtool update %s %s:%s:%s" % \
321
-                       (_RRD_FILE, dData['UTC'], dData['CPM'], SvPerHr)
325
+                       (_RRD_FILE, dData['ELT'], dData['CPM'], SvPerHr)
322 326
     if debugOption and False:
323 327
         print "%s" % strCmd # DEBUG
324 328
 
Browse code

minor revision 20180303

fractalxaos authored on 03/04/2018 02:03:05
Showing 1 changed files
... ...
@@ -32,7 +32,8 @@
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
+#   * v22 released 03 Mar 2018 by J L Owrey; improved code readability;
36
+#         improved radmon device offline status handling
36 37
 #
37 38
 
38 39
 _MIRROR_SERVER = False
... ...
@@ -50,9 +51,9 @@ _USER = os.environ['USER']
50 51
    ### DEFAULT WEATHER STATION URL ###
51 52
 
52 53
 # ip address of radiation monitoring device
53
-_DEFAULT_RADIATION_MONITOR_URL = '{your radiation monitor device url}'
54
+_DEFAULT_RADIATION_MONITOR_URL = '{your radmon device url}'
54 55
 # url if this is a mirror server
55
-_PRIMARY_SERVER_URL = '{your primary server radmon data url}'
56
+_PRIMARY_SERVER_URL = '{your primary server weather data url}'
56 57
 
57 58
     ### FILE AND FOLDER LOCATIONS ###
58 59
 
... ...
@@ -129,15 +130,8 @@ def setOfflineStatus(dData):
129 130
     if radiationMonitorOnline:
130 131
         print "%s: radiation monitor offline" % getTimeStamp()
131 132
         radiationMonitorOnline = False
132
-
133
-        dData['UTC'] = ''
134
-        dData['Mode'] = ''
135
-        dData['uSvPerHr'] = ''
136
-        dData['CPM'] = ''
137
-        dData['CPS'] = ''
138
-        dData['status'] = 'offline'
139
-
140
-        writeOutputDataFile(dData)
133
+        if os.path.exists(_OUTPUT_DATA_FILE):
134
+            os.remove(_OUTPUT_DATA_FILE)
141 135
     return
142 136
 ##end def
143 137
 
Browse code

minor revisions 20180303

fractalxaos authored on 03/04/2018 01:14:48
Showing 1 changed files
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)
Browse code

2017-12-2 minor update

fractalxaos authored on 12/02/2017 23:59:08
Showing 1 changed files
1 1
old mode 100755
2 2
new mode 100644
... ...
@@ -34,7 +34,7 @@
34 34
 #   * v21 released 27 Nov 2017 by J L Owrey; bug fixes; updates
35 35
 #
36 36
 
37
-_MIRROR_SERVER = True
37
+_MIRROR_SERVER = False
38 38
 
39 39
 import os
40 40
 import urllib2
... ...
@@ -48,24 +48,24 @@ _USER = os.environ['USER']
48 48
 
49 49
    ### DEFAULT WEATHER STATION URL ###
50 50
 
51
-if _MIRROR_SERVER:
52
-    _DEFAULT_RADIATION_MONITOR_URL = \
53
-        "http://73.157.139.23:7361/~pi/radmon/dynamic/rad.dat"
54
-else:
55
-    _DEFAULT_RADIATION_MONITOR_URL = "http://192.168.1.8"
51
+# ip address of radiation monitoring device
52
+_RADIATION_MONITOR_URL = "http://YOUR_RADIATION_MONITOR_IP_ADDRESS"
53
+# url if this is a mirror server
54
+_PRIMARY_SERVER_URL = \
55
+    "http://YOUR_PRIMARY_SERVER/USER/radmon/dynamic/radmonInputData.dat"
56 56
 
57 57
     ### FILE AND FOLDER LOCATIONS ###
58 58
 
59 59
 # folder for containing dynamic data objects
60
-_DYNAMIC_FOLDER_PATH = "/home/%s/public_html/radmon/dynamic/" % _USER
60
+_DOCROOT_PATH = "/home/%s/public_html/radmon/" % _USER
61 61
 # folder for charts and output data file
62
-_CHARTS_DIRECTORY = _DYNAMIC_FOLDER_PATH
62
+_CHARTS_DIRECTORY = _DOCROOT_PATH + "dynamic/"
63 63
  # database that stores weather data
64 64
 _RRD_FILE = "/home/%s/database/radmonData.rrd" % _USER
65
+# location of data input file
66
+INPUT_DATA_FILE = _DOCROOT_PATH + "dynamic/radmonInputData.dat"
65 67
 # location of data output file
66
-_OUTPUT_DATA_FILE = _DYNAMIC_FOLDER_PATH + "radmonData.js"
67
-# location of data forwarding file
68
-_DATA_FORWARDING_FILE = _DYNAMIC_FOLDER_PATH + "rad.dat"
68
+_OUTPUT_DATA_FILE = _DOCROOT_PATH + "dynamic/radmonOutputData.js"
69 69
 
70 70
     ### GLOBAL CONSTANTS ###
71 71
 # interval in seconds between data requests to radiation monitor
... ...
@@ -92,11 +92,10 @@ radiationMonitorOnline = True
92 92
 radiationMonitorOfflineCount = 0
93 93
 # status of reset command to radiation monitor
94 94
 remoteDeviceReset = False
95
+# ip address of radiation monitor
96
+radiationMonitorUrl = _RADIATION_MONITOR_URL
95 97
 # web update frequency
96 98
 dataRequestInterval = _DEFAULT_DATA_REQUEST_INTERVAL
97
-# radiation monitor network address
98
-radiationMonitorUrl = _DEFAULT_RADIATION_MONITOR_URL
99
-
100 99
 
101 100
   ###  PRIVATE METHODS  ###
102 101
 
... ...
@@ -127,8 +126,8 @@ def setOfflineStatus(dData):
127 126
     # that we are now offline.
128 127
     if radiationMonitorOnline:
129 128
         print "%s: radiation monitor offline" % getTimeStamp()
130
-        if os.path.exists(_DATA_FORWARDING_FILE):
131
-            os.remove(_DATA_FORWARDING_FILE)
129
+        if os.path.exists(INPUT_DATA_FILE):
130
+            os.remove(INPUT_DATA_FILE)
132 131
         radiationMonitorOnline = False
133 132
 
134 133
     for key in dData:
... ...
@@ -156,14 +155,12 @@ def getRadiationData():
156 155
     global radiationMonitorOnline, radiationMonitorOfflineCount, \
157 156
            remoteDeviceReset
158 157
 
159
-    sUrl = radiationMonitorUrl
160
-
161
-    if not _MIRROR_SERVER:
162
-        if remoteDeviceReset:
163
-            sUrl += "/reset"
164
-            remoteDeviceReset = False
165
-        else:
166
-            sUrl += "/rdata"
158
+    if _MIRROR_SERVER:
159
+        sUrl = _PRIMARY_SERVER_URL
160
+    elif remoteDeviceReset:
161
+        sUrl = radiationMonitorUrl + "/reset"
162
+    else:
163
+        sUrl = radiationMonitorUrl + "/rdata"
167 164
 
168 165
     try:
169 166
         conn = urllib2.urlopen(sUrl, timeout=_HTTP_REQUEST_TIMEOUT)
... ...
@@ -190,10 +187,7 @@ def getRadiationData():
190 187
         print "%s radiation monitor online" % getTimeStamp()
191 188
         radiationMonitorOnline = True
192 189
 
193
-    if debugOption:
194
-        #print content
195
-        pass
196
-
190
+    #print content
197 191
     return content
198 192
 ##end def
199 193
 
... ...
@@ -218,6 +212,11 @@ def parseDataString(sData, dData):
218 212
             dData[item.split('=')[0]] = item.split('=')[1]
219 213
     dData['status'] = 'online'
220 214
 
215
+    if len(dData) != 6:
216
+        print "%s parse failed: corrupted data string: %s" % \
217
+               (getTimeStamp(), sData)
218
+        return False;
219
+
221 220
     return True
222 221
 ##end def
223 222
 
... ...
@@ -289,10 +288,11 @@ def writeOutputDataFile(dData):
289 288
     return True
290 289
 ## end def
291 290
 
292
-def writeForwardingFile(sData):
291
+def writeInputDataFile(sData):
293 292
     # Write the string to the output data file for use by html documents.
293
+    sData += "\n"
294 294
     try:
295
-        fc = open(_DATA_FORWARDING_FILE, "w")
295
+        fc = open(INPUT_DATA_FILE, "w")
296 296
         fc.write(sData)
297 297
         fc.close()
298 298
     except Exception, exError:
... ...
@@ -515,7 +515,7 @@ def main():
515 515
 
516 516
             # If conversion successful, write data to output file.
517 517
             if result:
518
-                writeForwardingFile(sData)
518
+                writeInputDataFile(sData)
519 519
                 writeOutputDataFile(dData)
520 520
                 if debugOption:
521 521
                     print "http request successful"
Browse code

2017-11-27 update

fractalxaos authored on 11/28/2017 01:24:55
Showing 1 changed files
... ...
@@ -1,18 +1,19 @@
1 1
 #!/usr/bin/python -u
2
-## The -u option above turns off block buffering of python output. This assures
3
-## that each error message gets individually printed to the log file.
2
+## The -u option above turns off block buffering of python output. This 
3
+## assures that each error message gets individually printed to the log file.
4 4
 #
5 5
 # Module: radmonAgent.py
6 6
 #
7
-# Description: This module acts as an agent between the radiation monitoring device
8
-# and the Internet web server.  The agent periodically sends an http request to the
9
-# radiation monitoring device and processes the response from the device and performs
10
-# a number of operations:
7
+# Description: This module acts as an agent between the radiation monitoring
8
+# device and the Internet web server.  The agent periodically sends an http
9
+# request to the radiation monitoring device and processes the response from
10
+# the device and performs a number of operations:
11 11
 #     - conversion of data items
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
15
-#     - write the processed weather data to a JSON file for use by html documents
15
+#     - write the processed weather data to a JSON file for use by html
16
+#       documents
16 17
 #
17 18
 # Copyright 2015 Jeff Owrey
18 19
 #    This program is free software: you can redistribute it and/or modify
... ...
@@ -29,9 +30,12 @@
29 30
 #    along with this program.  If not, see http://www.gnu.org/license.
30 31
 #
31 32
 # Revision History
32
-#   * v20 released 15 Sep 2015 by J L Owrey
33
+#   * v20 released 15 Sep 2015 by J L Owrey; first release
34
+#   * v21 released 27 Nov 2017 by J L Owrey; bug fixes; updates
33 35
 #
34 36
 
37
+_MIRROR_SERVER = True
38
+
35 39
 import os
36 40
 import urllib2
37 41
 import sys   
... ...
@@ -44,32 +48,54 @@ _USER = os.environ['USER']
44 48
 
45 49
    ### DEFAULT WEATHER STATION URL ###
46 50
 
47
-_DEFAULT_RADIATION_MONITOR_URL = "{your weather station url}"
48
-_DATA_FORWARDING_FILE = "/home/%s/public_html/radmon/dynamic/rad.dat" % _USER
51
+if _MIRROR_SERVER:
52
+    _DEFAULT_RADIATION_MONITOR_URL = \
53
+        "http://73.157.139.23:7361/~pi/radmon/dynamic/rad.dat"
54
+else:
55
+    _DEFAULT_RADIATION_MONITOR_URL = "http://192.168.1.8"
49 56
 
50 57
     ### FILE AND FOLDER LOCATIONS ###
51 58
 
52
-_TMP_DIRECTORY = "/tmp/radmon" # folder for charts and output data file
53
-_RRD_FILE = "/home/%s/database/radmonData.rrd" % _USER # database that stores the data
54
-_OUTPUT_DATA_FILE = "/tmp/radmon/radmonData.js" # output file used by HTML docs
59
+# folder for containing dynamic data objects
60
+_DYNAMIC_FOLDER_PATH = "/home/%s/public_html/radmon/dynamic/" % _USER
61
+# folder for charts and output data file
62
+_CHARTS_DIRECTORY = _DYNAMIC_FOLDER_PATH
63
+ # database that stores weather data
64
+_RRD_FILE = "/home/%s/database/radmonData.rrd" % _USER
65
+# location of data output file
66
+_OUTPUT_DATA_FILE = _DYNAMIC_FOLDER_PATH + "radmonData.js"
67
+# location of data forwarding file
68
+_DATA_FORWARDING_FILE = _DYNAMIC_FOLDER_PATH + "rad.dat"
55 69
 
56 70
     ### GLOBAL CONSTANTS ###
57
-
58
-_DEFAULT_DATA_REQUEST_INTERVAL = 10 # interval between data requests to radiation monitor
59
-_CHART_UPDATE_INTERVAL = 300 # defines how often the charts get updated in seconds
60
-_DATABASE_UPDATE_INTERVAL = 30 # defines how often the database gets updated
61
-_HTTP_REQUEST_TIMEOUT = 3 # number seconds to wait for a response to HTTP request
62
-_MAX_RADIATION_MONITOR_OFFLINE_COUNT = 2 # max number of failed data requests allowed
71
+# interval in seconds between data requests to radiation monitor
72
+_DEFAULT_DATA_REQUEST_INTERVAL = 5
73
+# defines how often the charts get updated in seconds
74
+_CHART_UPDATE_INTERVAL = 300
75
+# defines how often the database gets updated
76
+_DATABASE_UPDATE_INTERVAL = 30
77
+# number seconds to wait for a response to HTTP request
78
+_HTTP_REQUEST_TIMEOUT = 3
79
+# max number of failed data requests allowed
80
+_MAX_RADIATION_MONITOR_OFFLINE_COUNT = 2
81
+# radmon chart dimensions
63 82
 _CHART_WIDTH = 600
64 83
 _CHART_HEIGHT = 150
65 84
 
66 85
    ### GLOBAL VARIABLES ###
67 86
 
87
+# turn on or off of verbose debugging information
68 88
 debugOption = False
89
+# online status of radiation monitor
69 90
 radiationMonitorOnline = True
91
+# number of unsuccessful http requests
70 92
 radiationMonitorOfflineCount = 0
71
-dataRequestInterval = _DEFAULT_DATA_REQUEST_INTERVAL  # web update frequency
72
-radiationMonitorUrl = _DEFAULT_RADIATION_MONITOR_URL  # radiation monitor network address
93
+# status of reset command to radiation monitor
94
+remoteDeviceReset = False
95
+# web update frequency
96
+dataRequestInterval = _DEFAULT_DATA_REQUEST_INTERVAL
97
+# radiation monitor network address
98
+radiationMonitorUrl = _DEFAULT_RADIATION_MONITOR_URL
73 99
 
74 100
 
75 101
   ###  PRIVATE METHODS  ###
... ...
@@ -101,14 +127,13 @@ def setOfflineStatus(dData):
101 127
     # that we are now offline.
102 128
     if radiationMonitorOnline:
103 129
         print "%s: radiation monitor offline" % getTimeStamp()
130
+        if os.path.exists(_DATA_FORWARDING_FILE):
131
+            os.remove(_DATA_FORWARDING_FILE)
104 132
         radiationMonitorOnline = False
105 133
 
106
-    # Set data items to blank.
107
-    dData['UTC'] = ''
108
-    dData['CPM'] = ''
109
-    dData['CPS'] = ''
110
-    dData['uSvPerHr'] = ''
111
-    dData['Mode'] = ''
134
+    for key in dData:
135
+        dData[key] = ''
136
+
112 137
     dData['status'] = 'offline'
113 138
 
114 139
     writeOutputDataFile(dData)
... ...
@@ -128,11 +153,20 @@ def getRadiationData():
128 153
     Returns a string containing the radiation data, or None if
129 154
     not successful.
130 155
     """
131
-    global radiationMonitorOnline, radiationMonitorOfflineCount
156
+    global radiationMonitorOnline, radiationMonitorOfflineCount, \
157
+           remoteDeviceReset
158
+
159
+    sUrl = radiationMonitorUrl
160
+
161
+    if not _MIRROR_SERVER:
162
+        if remoteDeviceReset:
163
+            sUrl += "/reset"
164
+            remoteDeviceReset = False
165
+        else:
166
+            sUrl += "/rdata"
132 167
 
133 168
     try:
134
-        conn = urllib2.urlopen(radiationMonitorUrl,
135
-                               timeout=_HTTP_REQUEST_TIMEOUT)
169
+        conn = urllib2.urlopen(sUrl, timeout=_HTTP_REQUEST_TIMEOUT)
136 170
 
137 171
         # Format received data into a single string.
138 172
         content = ""
... ...
@@ -197,13 +231,26 @@ def convertData(dData):
197 231
     result = True
198 232
  
199 233
     try:
200
-        # Convert UTC from radiation monitoring device to local time.
234
+
235
+        # Uncomment below to use timestamp from radiation monitoring device
236
+        # otherwise the requesting server (this) will generate the
237
+        # timestamp. Allowing the server to generate the timestamp
238
+        # prevents timestamp errors due to the radiation monitoring device
239
+        # failing to synchronize with a NTP time server.
240
+
241
+        #dData['UTC'] = time.time()
242
+
243
+        ## Convert UTC from radiation monitoring device to local time.
201 244
         ts_utc = time.strptime(dData['UTC'], "%H:%M:%S %m/%d/%Y")
202 245
         local_sec = calendar.timegm(ts_utc)
203 246
         dData['UTC'] = local_sec
204
-
247
+        
205 248
         dData['Mode'] = dData['Mode'].lower()
206
-        dData['uSvPerHr'] = dData.pop('uSv/hr')
249
+ 
250
+        dData['uSvPerHr'] = float(dData.pop('uSv/hr'))
251
+        
252
+        dData['CPM'] = int(dData.pop('CPM'))
253
+
207 254
     except Exception, exError:
208 255
         print "%s convertData: %s" % (getTimeStamp(), exError)
209 256
         result = False
... ...
@@ -243,9 +290,6 @@ def writeOutputDataFile(dData):
243 290
 ## end def
244 291
 
245 292
 def writeForwardingFile(sData):
246
-    """Write weather station response string to a forwarding file for use
247
-       by down stream servers that mirror this site.
248
-    """
249 293
     # Write the string to the output data file for use by html documents.
250 294
     try:
251 295
         fc = open(_DATA_FORWARDING_FILE, "w")
... ...
@@ -267,8 +311,11 @@ def updateDatabase(dData):
267 311
                         written to the rr database file
268 312
     Returns true if successful, false otherwise.
269 313
     """
270
-    # The RR database stores whole units, so convert uSv to Sv.   
271
-    Svvalue = float(dData['uSvPerHr']) * 1.0E-06 # convert micro-Sieverts to Sieverts
314
+    global remoteDeviceReset
315
+
316
+    # 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 
272 319
 
273 320
     # Create the rrdtool update command.
274 321
     strCmd = "rrdtool update %s %s:%s:%s" % \
... ...
@@ -283,6 +330,11 @@ def updateDatabase(dData):
283 330
     except subprocess.CalledProcessError, exError:
284 331
         print "%s: rrdtool update failed: %s" % \
285 332
                                  (getTimeStamp(), exError.output)
333
+        if exError.output.find("illegal attempt to update using time") > -1:
334
+            remoteDeviceReset = True
335
+            print "%s: rebooting radiation monitor" % \
336
+                                 (getTimeStamp())
337
+
286 338
         return False
287 339
 
288 340
     return True
... ...
@@ -306,7 +358,7 @@ def createGraph(fileName, dataItem, gLabel, gTitle, gStart,
306 358
                lower and upper parameters to set vertical axis scale
307 359
        Returns true if successful, false otherwise.
308 360
     """
309
-    gPath = _TMP_DIRECTORY + '/' + fileName + ".png"
361
+    gPath = _CHARTS_DIRECTORY + fileName + ".png"
310 362
     trendWindow = { 'end-1day': 7200,
311 363
                     'end-4weeks': 172800,
312 364
                     'end-12months': 604800 }
... ...
@@ -410,24 +462,25 @@ def getCLarguments():
410 462
 ##end def
411 463
 
412 464
 def main():
413
-    """Handles timing of events and acts as executive routine managing all other
414
-       functions.
465
+    """Handles timing of events and acts as executive routine managing
466
+       all other functions.
415 467
        Parameters: none
416 468
        Returns nothing.
417 469
     """
418 470
 
419
-    lastDataRequestTime = -1 # last time output JSON file updated
420
-    lastChartUpdateTime = - 1 # last time charts generated
421
-    lastDatabaseUpdateTime = -1 # last time the rrdtool database updated
422
-    dData = {}  # dictionary object for temporary data storage
471
+    # last time output JSON file updated
472
+    lastDataRequestTime = -1
473
+    # last time charts generated
474
+    lastChartUpdateTime = - 1
475
+    # last time the rrdtool database updated
476
+    lastDatabaseUpdateTime = -1
477
+
478
+    # define empty dictionary object for radmon data
479
+    dData = {}
423 480
 
424 481
     ## Get command line arguments.
425 482
     getCLarguments()
426 483
 
427
-    ## Create www data folder if it does not already exist.
428
-    if not os.path.isdir(_TMP_DIRECTORY):
429
-        os.makedirs(_TMP_DIRECTORY)
430
-
431 484
     ## Exit with error if rrdtool database does not exist.
432 485
     if not os.path.exists(_RRD_FILE):
433 486
         print "cannot find rrdtool database\nuse createWeatherRrd script to" \
... ...
@@ -453,6 +506,7 @@ def main():
453 506
 
454 507
             # If successful parse the data.
455 508
             if result:
509
+                dData = {}
456 510
                 result = parseDataString(sData, dData)
457 511
 
458 512
             # If parsing successful, convert the data.
... ...
@@ -467,7 +521,8 @@ def main():
467 521
                     print "http request successful"
468 522
 
469 523
                 # At the rrdtool database update interval, update the database.
470
-                if currentTime - lastDatabaseUpdateTime > _DATABASE_UPDATE_INTERVAL:   
524
+                if currentTime - lastDatabaseUpdateTime > \
525
+                        _DATABASE_UPDATE_INTERVAL:   
471 526
                     lastDatabaseUpdateTime = currentTime
472 527
                     ## Update the round robin database with the parsed data.
473 528
                     result = updateDatabase(dData)
Browse code

revised 2016-12-23

fractalxaos authored on 12/23/2016 18:34:36
Showing 1 changed files
... ...
@@ -31,18 +31,24 @@
31 31
 # Revision History
32 32
 #   * v20 released 15 Sep 2015 by J L Owrey
33 33
 #
34
+
35
+import os
34 36
 import urllib2
35
-import time
36
-import calendar
37
+import sys   
37 38
 import subprocess
38
-import sys
39
-import os
40
-import json
41 39
 import multiprocessing
40
+import time
41
+import calendar
42
+
43
+_USER = os.environ['USER']
44
+
45
+   ### DEFAULT WEATHER STATION URL ###
46
+
47
+_DEFAULT_RADIATION_MONITOR_URL = "{your weather station url}"
48
+_DATA_FORWARDING_FILE = "/home/%s/public_html/radmon/dynamic/rad.dat" % _USER
42 49
 
43 50
     ### FILE AND FOLDER LOCATIONS ###
44 51
 
45
-_USER = os.environ['USER']
46 52
 _TMP_DIRECTORY = "/tmp/radmon" # folder for charts and output data file
47 53
 _RRD_FILE = "/home/%s/database/radmonData.rrd" % _USER # database that stores the data
48 54
 _OUTPUT_DATA_FILE = "/tmp/radmon/radmonData.js" # output file used by HTML docs
... ...
@@ -50,13 +56,12 @@ _OUTPUT_DATA_FILE = "/tmp/radmon/radmonData.js" # output file used by HTML docs
50 56
     ### GLOBAL CONSTANTS ###
51 57
 
52 58
 _DEFAULT_DATA_REQUEST_INTERVAL = 10 # interval between data requests to radiation monitor
53
-_CHART_UPDATE_INTERVAL = 300 # defines how often the charts get updated
59
+_CHART_UPDATE_INTERVAL = 300 # defines how often the charts get updated in seconds
54 60
 _DATABASE_UPDATE_INTERVAL = 30 # defines how often the database gets updated
55
-_HTTP_REQUEST_TIMEOUT = 5 # number seconds to wait for a response to HTTP request
56
-_MAX_RADIATION_MONITOR_OFFLINE_COUNT = 3 # max number of failed data requests allowed
61
+_HTTP_REQUEST_TIMEOUT = 3 # number seconds to wait for a response to HTTP request
62
+_MAX_RADIATION_MONITOR_OFFLINE_COUNT = 2 # max number of failed data requests allowed
57 63
 _CHART_WIDTH = 600
58 64
 _CHART_HEIGHT = 150
59
-_DEFAULT_RADIATION_MONITOR_URL = "{your radiation monitor url}"
60 65
 
61 66
    ### GLOBAL VARIABLES ###
62 67
 
... ...
@@ -126,7 +131,7 @@ def getRadiationData():
126 131
     global radiationMonitorOnline, radiationMonitorOfflineCount
127 132
 
128 133
     try:
129
-        conn = urllib2.urlopen(radiationMonitorUrl + "/rdata",
134
+        conn = urllib2.urlopen(radiationMonitorUrl,
130 135
                                timeout=_HTTP_REQUEST_TIMEOUT)
131 136
 
132 137
         # Format received data into a single string.
... ...
@@ -152,8 +157,9 @@ def getRadiationData():
152 157
         radiationMonitorOnline = True
153 158
 
154 159
     if debugOption:
155
-        print "http request successful"
156 160
         #print content
161
+        pass
162
+
157 163
     return content
158 164
 ##end def
159 165
 
... ...
@@ -236,6 +242,22 @@ def writeOutputDataFile(dData):
236 242
     return True
237 243
 ## end def
238 244
 
245
+def writeForwardingFile(sData):
246
+    """Write weather station response string to a forwarding file for use
247
+       by down stream servers that mirror this site.
248
+    """
249
+    # Write the string to the output data file for use by html documents.
250
+    try:
251
+        fc = open(_DATA_FORWARDING_FILE, "w")
252
+        fc.write(sData)
253
+        fc.close()
254
+    except Exception, exError:
255
+        print "%s writeOutputDataFile: %s" % (getTimeStamp(), exError)
256
+        return False
257
+
258
+    return True
259
+##end def
260
+
239 261
 def updateDatabase(dData):
240 262
     """
241 263
     Updates the rrdtool database by executing an rrdtool system command.
... ...
@@ -343,17 +365,17 @@ def generateGraphs():
343 365
     """
344 366
     autoScale = False
345 367
 
346
-    createGraph('radGraph1', 'CPM', 'counts\ per\ minute', 
368
+    createGraph('24hr_cpm', 'CPM', 'counts\ per\ minute', 
347 369
                 'CPM\ -\ Last\ 24\ Hours', 'end-1day', 0, 0, 2, autoScale)
348
-    createGraph('radGraph2', 'SvperHr', 'Sv\ per\ hour',
370
+    createGraph('24hr_svperhr', 'SvperHr', 'Sv\ per\ hour',
349 371
                 'Sv/Hr\ -\ Last\ 24\ Hours', 'end-1day', 0, 0, 2, autoScale)
350
-    createGraph('radGraph3', 'CPM', 'counts\ per\ minute',
372
+    createGraph('4wk_cpm', 'CPM', 'counts\ per\ minute',
351 373
                 'CPM\ -\ Last\ 4\ Weeks', 'end-4weeks', 0, 0, 2, autoScale)
352
-    createGraph('radGraph4', 'SvperHr', 'Sv\ per\ hour',
374
+    createGraph('4wk_svperhr', 'SvperHr', 'Sv\ per\ hour',
353 375
                 'Sv/Hr\ -\ Last\ 4\ Weeks', 'end-4weeks', 0, 0, 2, autoScale)
354
-    createGraph('radGraph5', 'CPM', 'counts\ per\ minute',
376
+    createGraph('12m_cpm', 'CPM', 'counts\ per\ minute',
355 377
                 'CPM\ -\ Past\ Year', 'end-12months', 0, 0, 2, autoScale)
356
-    createGraph('radGraph6', 'SvperHr', 'Sv\ per\ hour',
378
+    createGraph('12m_svperhr', 'SvperHr', 'Sv\ per\ hour',
357 379
                 'Sv/Hr\ -\ Past\ Year', 'end-12months', 0, 0, 2, autoScale)
358 380
 ##end def
359 381
 
... ...
@@ -439,7 +461,10 @@ def main():
439 461
 
440 462
             # If conversion successful, write data to output file.
441 463
             if result:
464
+                writeForwardingFile(sData)
442 465
                 writeOutputDataFile(dData)
466
+                if debugOption:
467
+                    print "http request successful"
443 468
 
444 469
                 # At the rrdtool database update interval, update the database.
445 470
                 if currentTime - lastDatabaseUpdateTime > _DATABASE_UPDATE_INTERVAL:   
Browse code

revised 2016.10.03

Fractal authored on 10/03/2016 22:52:07
Showing 1 changed files
1 1
old mode 100644
2 2
new mode 100755
... ...
@@ -49,19 +49,22 @@ _OUTPUT_DATA_FILE = "/tmp/radmon/radmonData.js" # output file used by HTML docs
49 49
 
50 50
     ### GLOBAL CONSTANTS ###
51 51
 
52
-_DEFAULT_WEB_DATA_UPDATE_INTERVAL = 10
52
+_DEFAULT_DATA_REQUEST_INTERVAL = 10 # interval between data requests to radiation monitor
53 53
 _CHART_UPDATE_INTERVAL = 300 # defines how often the charts get updated
54 54
 _DATABASE_UPDATE_INTERVAL = 30 # defines how often the database gets updated
55 55
 _HTTP_REQUEST_TIMEOUT = 5 # number seconds to wait for a response to HTTP request
56
+_MAX_RADIATION_MONITOR_OFFLINE_COUNT = 3 # max number of failed data requests allowed
56 57
 _CHART_WIDTH = 600
57 58
 _CHART_HEIGHT = 150
59
+_DEFAULT_RADIATION_MONITOR_URL = "{your radiation monitor url}"
58 60
 
59 61
    ### GLOBAL VARIABLES ###
60 62
 
61
-webUpdateInterval = _DEFAULT_WEB_DATA_UPDATE_INTERVAL  # web update frequency
62
-deviceUrl = "{your device url}"  # radiation monitor network address
63
-deviceOnline = True
64 63
 debugOption = False
64
+radiationMonitorOnline = True
65
+radiationMonitorOfflineCount = 0
66
+dataRequestInterval = _DEFAULT_DATA_REQUEST_INTERVAL  # web update frequency
67
+radiationMonitorUrl = _DEFAULT_RADIATION_MONITOR_URL  # radiation monitor network address
65 68
 
66 69
 
67 70
   ###  PRIVATE METHODS  ###
... ...
@@ -72,7 +75,7 @@ def getTimeStamp():
72 75
     Parameters: none
73 76
     Returns string containing the time stamp.
74 77
     """
75
-    return time.strftime( "%Y/%m/%d %T", time.localtime() )
78
+    return time.strftime( "%m/%d/%Y %T", time.localtime() )
76 79
 ##end def
77 80
 
78 81
 def setOfflineStatus(dData):
... ...
@@ -82,13 +85,18 @@ def setOfflineStatus(dData):
82 85
            dData - dictionary object containing weather data
83 86
        Returns nothing.
84 87
     """
85
-    global deviceOnline
88
+    global radiationMonitorOnline, radiationMonitorOfflineCount
89
+
90
+    radiationMonitorOfflineCount += 1
91
+
92
+    if radiationMonitorOfflineCount < _MAX_RADIATION_MONITOR_OFFLINE_COUNT:
93
+        return
86 94
 
87 95
     # If the radiation monitor was previously online, then send a message
88 96
     # that we are now offline.
89
-    if deviceOnline:
90
-        print "%s: radmon offline" % getTimeStamp()
91
-        deviceOnline = False
97
+    if radiationMonitorOnline:
98
+        print "%s: radiation monitor offline" % getTimeStamp()
99
+        radiationMonitorOnline = False
92 100
 
93 101
     # Set data items to blank.
94 102
     dData['UTC'] = ''
... ...
@@ -104,40 +112,48 @@ def setOfflineStatus(dData):
104 112
 
105 113
   ###  PUBLIC METHODS  ###
106 114
 
107
-def getRadmonData(deviceUrl, HttpRequestTimeout):
115
+def getRadiationData():
108 116
     """Send http request to radiation monitoring device.  The response
109 117
        from the device contains the radiation data.  The data is formatted
110 118
        as an html document.
111 119
     Parameters: 
112
-        deviceUrl - url of radiation monitoring device
120
+        radiationMonitorUrl - url of radiation monitoring device
113 121
         HttpRequesttimeout - how long to wait for device
114 122
                              to respond to http request
115 123
     Returns a string containing the radiation data, or None if
116 124
     not successful.
117 125
     """
118
-    global deviceOnline
126
+    global radiationMonitorOnline, radiationMonitorOfflineCount
119 127
 
120 128
     try:
121
-        conn = urllib2.urlopen(deviceUrl + "/rdata", timeout=HttpRequestTimeout)
129
+        conn = urllib2.urlopen(radiationMonitorUrl + "/rdata",
130
+                               timeout=_HTTP_REQUEST_TIMEOUT)
131
+
132
+        # Format received data into a single string.
133
+        content = ""
134
+        for line in conn:
135
+            content += line.strip()
136
+        del conn
137
+
122 138
     except Exception, exError:
123 139
         # If no response is received from the device, then assume that
124 140
         # the device is down or unavailable over the network.  In
125 141
         # that case set the status of the device to offline.
126 142
         if debugOption:
127
-            print "getRadmonData: %s\n" % exError
143
+            print "http error: %s" % exError
128 144
         return None
129 145
 
146
+    radiationMonitorOfflineCount = 0
147
+
130 148
     # If the radiation monitor was previously offline, then send a message
131 149
     # that we are now online.
132
-    if not deviceOnline:
133
-        print "%s radmon online" % getTimeStamp()
134
-        deviceOnline = True
135
-
136
-    # Format received data into a single string.
137
-    content = ""
138
-    for line in conn:
139
-         content += line.strip()
140
-    del conn
150
+    if not radiationMonitorOnline:
151
+        print "%s radiation monitor online" % getTimeStamp()
152
+        radiationMonitorOnline = True
153
+
154
+    if debugOption:
155
+        print "http request successful"
156
+        #print content
141 157
     return content
142 158
 ##end def
143 159
 
... ...
@@ -236,7 +252,7 @@ def updateDatabase(dData):
236 252
     strCmd = "rrdtool update %s %s:%s:%s" % \
237 253
                        (_RRD_FILE, dData['UTC'], dData['CPM'], Svvalue)
238 254
     if debugOption:
239
-        print "%s\n" % strCmd # DEBUG
255
+        print "%s" % strCmd # DEBUG
240 256
 
241 257
     # Run the command as a subprocess.
242 258
     try:
... ...
@@ -250,13 +266,22 @@ def updateDatabase(dData):
250 266
     return True
251 267
 ##end def
252 268
 
253
-def createGraph(fileName, dataItem, gLabel, gTitle, gStart, lower, upper, addTrend):
269
+def createGraph(fileName, dataItem, gLabel, gTitle, gStart,
270
+                lower, upper, addTrend, autoScale):
254 271
     """Uses rrdtool to create a graph of specified weather data item.
255 272
        Parameters:
256 273
            fileName - name of graph image file
257 274
            dataItem - data item to be graphed
258
-           gTitle - a title for the graph
259
-           gStart - beginning time of the data to be graphed
275
+           gLabel - string containing a graph label for the data item
276
+           gTitle - string containing a title for the graph
277
+           lower - lower bound for graph ordinate #NOT USED
278
+           upper - upper bound for graph ordinate #NOT USED
279
+           addTrend - 0, show only graph data
280
+                      1, show only a trend line
281
+                      2, show a trend line and the graph data
282
+           autoScale - if True, then use vertical axis auto scaling
283
+               (lower and upper parameters are ignored), otherwise use
284
+               lower and upper parameters to set vertical axis scale
260 285
        Returns true if successful, false otherwise.
261 286
     """
262 287
     gPath = _TMP_DIRECTORY + '/' + fileName + ".png"
... ...
@@ -270,26 +295,29 @@ def createGraph(fileName, dataItem, gLabel, gTitle, gStart, lower, upper, addTre
270 295
     strCmd = "rrdtool graph %s -a PNG -s %s -e now -w %s -h %s " \
271 296
              % (gPath, gStart, _CHART_WIDTH, _CHART_HEIGHT)
272 297
    
273
-    # Set the range of the chart ordinate dataum.
298
+    # Set the range and scaling of the chart y-axis.
274 299
     if lower < upper:
275
-        strCmd  +=  "-l %s -u %s " % (lower, upper)
276
-    else:
277
-        #strCmd += "-A -Y "
278
-        strCmd += "-Y "
300
+        strCmd  +=  "-l %s -u %s -r " % (lower, upper)
301
+    elif autoScale:
302
+        strCmd += "-A "
303
+    strCmd += "-Y "
279 304
 
280 305
     # Set the chart ordinate label and chart title. 
281 306
     strCmd += "-v %s -t %s " % (gLabel, gTitle)
282
-
307
+ 
283 308
     # Show the data, or a moving average trend line over
284 309
     # the data, or both.
285
-    strCmd += "DEF:%s=%s:%s:LAST " % (dataItem, _RRD_FILE, dataItem)
286
-
287
-    if addTrend == 0 or addTrend == 2:
288
-        strCmd += "LINE1:%s\#0400ff " % (dataItem)
289
-    if addTrend == 1 or addTrend == 2:
290
-        strCmd += "CDEF:smoothed=%s,%s,TREND LINE1:smoothed#ff0000" \
291
-                  % (dataItem, trendWindow[gStart])
292
-       
310
+    strCmd += "DEF:dSeries=%s:%s:LAST " % (_RRD_FILE, dataItem)
311
+    if addTrend == 0:
312
+        strCmd += "LINE1:dSeries#0400ff "
313
+    elif addTrend == 1:
314
+        strCmd += "CDEF:smoothed=dSeries,%s,TREND LINE3:smoothed#ff0000 " \
315
+                  % trendWindow[gStart]
316
+    elif addTrend == 2:
317
+        strCmd += "LINE1:dSeries#0400ff "
318
+        strCmd += "CDEF:smoothed=dSeries,%s,TREND LINE3:smoothed#ff0000 " \
319
+                  % trendWindow[gStart]
320
+     
293 321
     if debugOption:
294 322
         print "%s\n" % strCmd # DEBUG
295 323
     
... ...
@@ -308,6 +336,27 @@ def createGraph(fileName, dataItem, gLabel, gTitle, gStart, lower, upper, addTre
308 336
 
309 337
 ##end def
310 338
 
339
+def generateGraphs():
340
+    """Generate graphs for display in html documents.
341
+       Parameters: none
342
+       Returns nothing.
343
+    """
344
+    autoScale = False
345
+
346
+    createGraph('radGraph1', 'CPM', 'counts\ per\ minute', 
347
+                'CPM\ -\ Last\ 24\ Hours', 'end-1day', 0, 0, 2, autoScale)
348
+    createGraph('radGraph2', 'SvperHr', 'Sv\ per\ hour',
349
+                'Sv/Hr\ -\ Last\ 24\ Hours', 'end-1day', 0, 0, 2, autoScale)
350
+    createGraph('radGraph3', 'CPM', 'counts\ per\ minute',
351
+                'CPM\ -\ Last\ 4\ Weeks', 'end-4weeks', 0, 0, 2, autoScale)
352
+    createGraph('radGraph4', 'SvperHr', 'Sv\ per\ hour',
353
+                'Sv/Hr\ -\ Last\ 4\ Weeks', 'end-4weeks', 0, 0, 2, autoScale)
354
+    createGraph('radGraph5', 'CPM', 'counts\ per\ minute',
355
+                'CPM\ -\ Past\ Year', 'end-12months', 0, 0, 2, autoScale)
356
+    createGraph('radGraph6', 'SvperHr', 'Sv\ per\ hour',
357
+                'Sv/Hr\ -\ Past\ Year', 'end-12months', 0, 0, 2, autoScale)
358
+##end def
359
+
311 360
 def getCLarguments():
312 361
     """Get command line arguments.  There are three possible arguments
313 362
           -d turns on debug mode
... ...
@@ -315,7 +364,7 @@ def getCLarguments():
315 364
           -u sets the url of the radiation monitoring device
316 365
        Returns nothing.
317 366
     """
318
-    global debugOption, webUpdateInterval, deviceUrl
367
+    global debugOption, dataRequestInterval, radiationMonitorUrl
319 368
 
320 369
     index = 1
321 370
     while index < len(sys.argv):
... ...
@@ -323,34 +372,21 @@ def getCLarguments():
323 372
             debugOption = True
324 373
         elif sys.argv[index] == '-t':
325 374
             try:
326
-                webUpdateInterval = abs(int(sys.argv[index + 1]))
375
+                dataRequestInterval = abs(int(sys.argv[index + 1]))
327 376
             except:
328 377
                 print "invalid polling period"
329 378
                 exit(-1)
330 379
             index += 1
331 380
         elif sys.argv[index] == '-u':
332
-            deviceUrl = sys.argv[index + 1]
381
+            radiationMonitorUrl = sys.argv[index + 1]
333 382
             index += 1
334 383
         else:
335 384
             cmd_name = sys.argv[0].split('/')
336
-            print "Usage: %s {-v} {-d}" % cmd_name[-1]
385
+            print "Usage: %s [-d] [-t seconds] [-u url}" % cmd_name[-1]
337 386
             exit(-1)
338 387
         index += 1
339 388
 ##end def
340 389
 
341
-def generateGraphs():
342
-    """Generate graphs for display in html documents.
343
-       Parameters: none
344
-       Returns nothing.
345
-    """
346
-    createGraph('radGraph1', 'CPM', "'counts per minute'", "'CPM - Last 24 Hours'", 'end-1day', 0, 0, 2)
347
-    createGraph('radGraph2', 'SvperHr', "'Sv per hour'", "'Sv/Hr - Last 24 Hours'", 'end-1day', 0, 0, 2)
348
-    createGraph('radGraph3', 'CPM', "'counts per minute'", "'CPM - Last 4 Weeks'", 'end-4weeks', 0, 0, 2)
349
-    createGraph('radGraph4', 'SvperHr', "'Sv per hour'", "'Sv/Hr - Last 4 Weeks'", 'end-4weeks', 0, 0, 2)
350
-    createGraph('radGraph5', 'CPM', "'counts per minute'", "'CPM - Past Year'", 'end-12months', 0, 0, 2)
351
-    createGraph('radGraph6', 'SvperHr', "'Sv per hour'", "'Sv/Hr - Past Year'", 'end-12months', 0, 0, 2)
352
-##end def
353
-
354 390
 def main():
355 391
     """Handles timing of events and acts as executive routine managing all other
356 392
        functions.
... ...
@@ -358,11 +394,10 @@ def main():
358 394
        Returns nothing.
359 395
     """
360 396
 
397
+    lastDataRequestTime = -1 # last time output JSON file updated
361 398
     lastChartUpdateTime = - 1 # last time charts generated
362 399
     lastDatabaseUpdateTime = -1 # last time the rrdtool database updated
363
-    lastWebUpdateTime = -1 # last time output JSON file updated
364 400
     dData = {}  # dictionary object for temporary data storage
365
-    lsData = [] # list object for temporary data storage
366 401
 
367 402
     ## Get command line arguments.
368 403
     getCLarguments()
... ...
@@ -371,24 +406,25 @@ def main():
371 406
     if not os.path.isdir(_TMP_DIRECTORY):
372 407
         os.makedirs(_TMP_DIRECTORY)
373 408
 
374
-    ## Exit with error if cannot find the rrdtool database file.
409
+    ## Exit with error if rrdtool database does not exist.
375 410
     if not os.path.exists(_RRD_FILE):
376
-        print "cannot find rrdtool database file: terminating"
411
+        print "cannot find rrdtool database\nuse createWeatherRrd script to" \
412
+              " create rrdtool database\n"
377 413
         exit(1)
378 414
  
379 415
     ## main loop
380 416
     while True:
381 417
 
382
-        currentTime = time.time()
418
+        currentTime = time.time() # get current time in seconds
383 419
 
384
-        # At the radiation device query interval request and process
385
-        # the data from the device.
386
-        if currentTime - lastWebUpdateTime > webUpdateInterval:
387
-            lastWebUpdateTime = currentTime
420
+        # Every web update interval request data from the radiation
421
+        # monitor and process the received data.
422
+        if currentTime - lastDataRequestTime > dataRequestInterval:
423
+            lastDataRequestTime = currentTime
388 424
             result = True
389 425
 
390 426
             # Get the data string from the device.
391
-            sData = getRadmonData(deviceUrl, _HTTP_REQUEST_TIMEOUT)
427
+            sData = getRadiationData()
392 428
             if sData == None:
393 429
                 setOfflineStatus(dData)
394 430
                 result = False
... ...
@@ -422,12 +458,12 @@ def main():
422 458
 
423 459
         elapsedTime = time.time() - currentTime
424 460
         if debugOption:
425
-            print "web update: %6f sec\n" % elapsedTime
426
-        remainingTime = webUpdateInterval - elapsedTime
427
-        if remainingTime > 0:
461
+            print "processing time: %6f sec\n" % elapsedTime
462
+        remainingTime = dataRequestInterval - elapsedTime
463
+        if remainingTime > 0.0:
428 464
             time.sleep(remainingTime)
429
-             
430 465
     ## end while
466
+    return
431 467
 ## end def
432 468
 
433 469
 if __name__ == '__main__':
Browse code

revisions

Jeff Owrey authored on 02/15/2016 01:07:28
Showing 1 changed files
... ...
@@ -59,7 +59,7 @@ _CHART_HEIGHT = 150
59 59
    ### GLOBAL VARIABLES ###
60 60
 
61 61
 webUpdateInterval = _DEFAULT_WEB_DATA_UPDATE_INTERVAL  # web update frequency
62
-deviceUrl = "http://73.157.139.23:4371"  # radiation monitor network address
62
+deviceUrl = "{your device url}"  # radiation monitor network address
63 63
 deviceOnline = True
64 64
 debugOption = False
65 65
 
Browse code

revisions

Jeff Owrey authored on 02/15/2016 01:01:24
Showing 1 changed files
... ...
@@ -50,7 +50,7 @@ _OUTPUT_DATA_FILE = "/tmp/radmon/radmonData.js" # output file used by HTML docs
50 50
     ### GLOBAL CONSTANTS ###
51 51
 
52 52
 _DEFAULT_WEB_DATA_UPDATE_INTERVAL = 10
53
-_CHART_UPDATE_INTERVAL = 60 # defines how often the charts get updated
53
+_CHART_UPDATE_INTERVAL = 300 # defines how often the charts get updated
54 54
 _DATABASE_UPDATE_INTERVAL = 30 # defines how often the database gets updated
55 55
 _HTTP_REQUEST_TIMEOUT = 5 # number seconds to wait for a response to HTTP request
56 56
 _CHART_WIDTH = 600
... ...
@@ -59,7 +59,7 @@ _CHART_HEIGHT = 150
59 59
    ### GLOBAL VARIABLES ###
60 60
 
61 61
 webUpdateInterval = _DEFAULT_WEB_DATA_UPDATE_INTERVAL  # web update frequency
62
-deviceUrl = {your device URL}  # radiation monitor network address
62
+deviceUrl = "http://73.157.139.23:4371"  # radiation monitor network address
63 63
 deviceOnline = True
64 64
 debugOption = False
65 65
 
... ...
@@ -405,12 +405,11 @@ def main():
405 405
             if result:
406 406
                 writeOutputDataFile(dData)
407 407
 
408
-        # At the rrdtool database update interval, update the database.
409
-        if currentTime - lastDatabaseUpdateTime > _DATABASE_UPDATE_INTERVAL:   
410
-            lastDatabaseUpdateTime = currentTime
411
-            if result:
412
-                ## Update the round robin database with the parsed data.
413
-                result = updateDatabase(dData)
408
+                # At the rrdtool database update interval, update the database.
409
+                if currentTime - lastDatabaseUpdateTime > _DATABASE_UPDATE_INTERVAL:   
410
+                    lastDatabaseUpdateTime = currentTime
411
+                    ## Update the round robin database with the parsed data.
412
+                    result = updateDatabase(dData)
414 413
 
415 414
         # At the chart generation interval, generate charts.
416 415
         if currentTime - lastChartUpdateTime > _CHART_UPDATE_INTERVAL:
Browse code

revisions

Jeff Owrey authored on 02/09/2016 05:20:52
Showing 1 changed files
... ...
@@ -282,7 +282,7 @@ def createGraph(fileName, dataItem, gLabel, gTitle, gStart, lower, upper, addTre
282 282
 
283 283
     # Show the data, or a moving average trend line over
284 284
     # the data, or both.
285
-    strCmd += "DEF:%s=%s:%s:AVERAGE " % (dataItem, _RRD_FILE, dataItem)
285
+    strCmd += "DEF:%s=%s:%s:LAST " % (dataItem, _RRD_FILE, dataItem)
286 286
 
287 287
     if addTrend == 0 or addTrend == 2:
288 288
         strCmd += "LINE1:%s\#0400ff " % (dataItem)
Browse code

revisions

Jeff Owrey authored on 02/09/2016 05:04:32
Showing 1 changed files
... ...
@@ -59,7 +59,7 @@ _CHART_HEIGHT = 150
59 59
    ### GLOBAL VARIABLES ###
60 60
 
61 61
 webUpdateInterval = _DEFAULT_WEB_DATA_UPDATE_INTERVAL  # web update frequency
62
-deviceUrl = "http://73.157.139.23:4371"  # radiation monitor network address
62
+deviceUrl = {your device URL}  # radiation monitor network address
63 63
 deviceOnline = True
64 64
 debugOption = False
65 65
 
Browse code

revisions

Jeff Owrey authored on 02/07/2016 04:35:37
Showing 1 changed files
... ...
@@ -42,8 +42,9 @@ import multiprocessing
42 42
 
43 43
     ### FILE AND FOLDER LOCATIONS ###
44 44
 
45
+_USER = os.environ['USER']
45 46
 _TMP_DIRECTORY = "/tmp/radmon" # folder for charts and output data file
46
-_RRD_FILE = "/home/{your user id}/database/radmonData.rrd"  # database that stores the data
47
+_RRD_FILE = "/home/%s/database/radmonData.rrd" % _USER # database that stores the data
47 48
 _OUTPUT_DATA_FILE = "/tmp/radmon/radmonData.js" # output file used by HTML docs
48 49
 
49 50
     ### GLOBAL CONSTANTS ###
... ...
@@ -52,11 +53,13 @@ _DEFAULT_WEB_DATA_UPDATE_INTERVAL = 10
52 53
 _CHART_UPDATE_INTERVAL = 60 # defines how often the charts get updated
53 54
 _DATABASE_UPDATE_INTERVAL = 30 # defines how often the database gets updated
54 55
 _HTTP_REQUEST_TIMEOUT = 5 # number seconds to wait for a response to HTTP request
56
+_CHART_WIDTH = 600
57
+_CHART_HEIGHT = 150
55 58
 
56 59
    ### GLOBAL VARIABLES ###
57 60
 
58 61
 webUpdateInterval = _DEFAULT_WEB_DATA_UPDATE_INTERVAL  # web update frequency
59
-deviceUrl = "{your URL}"  # radiation monitor network address
62
+deviceUrl = "http://73.157.139.23:4371"  # radiation monitor network address
60 63
 deviceOnline = True
61 64
 debugOption = False
62 65
 
... ...
@@ -211,7 +214,7 @@ def writeOutputDataFile(dData):
211 214
         print "%s writeOutputDataFile: %s" % (getTimeStamp(), exError)
212 215
         return False
213 216
 
214
-    if debugOption:
217
+    if debugOption and 0:
215 218
         print sData
216 219
 
217 220
     return True
... ...
@@ -247,7 +250,7 @@ def updateDatabase(dData):
247 250
     return True
248 251
 ##end def
249 252
 
250
-def createGraph(fileName, dataItem, gTitle, gStart):
253
+def createGraph(fileName, dataItem, gLabel, gTitle, gStart, lower, upper, addTrend):
251 254
     """Uses rrdtool to create a graph of specified weather data item.
252 255
        Parameters:
253 256
            fileName - name of graph image file
... ...
@@ -257,30 +260,52 @@ def createGraph(fileName, dataItem, gTitle, gStart):
257 260
        Returns true if successful, false otherwise.
258 261
     """
259 262
     gPath = _TMP_DIRECTORY + '/' + fileName + ".png"
260
-
261
-    # Create the rrdtool graph command.
262
-    strFmt = ("rrdtool graph %s -a PNG -s %s -w 600 -h 150 "
263
-                           ##  "-l 50 -u 110 -r "
264
-                               "-v %s -t %s "
265
-                               "DEF:%s=%s:%s:AVERAGE "
266
-                               "LINE2:%s\#0400ff:")         
267
-    strCmd = strFmt % (gPath, gStart, dataItem, gTitle, dataItem, \
268
-                       _RRD_FILE, dataItem, dataItem)
263
+    trendWindow = { 'end-1day': 7200,
264
+                    'end-4weeks': 172800,
265
+                    'end-12months': 604800 }
266
+ 
267
+    # Format the rrdtool graph command.
268
+
269
+    # Set chart start time, height, and width.
270
+    strCmd = "rrdtool graph %s -a PNG -s %s -e now -w %s -h %s " \
271
+             % (gPath, gStart, _CHART_WIDTH, _CHART_HEIGHT)
272
+   
273
+    # Set the range of the chart ordinate dataum.
274
+    if lower < upper:
275
+        strCmd  +=  "-l %s -u %s " % (lower, upper)
276
+    else:
277
+        #strCmd += "-A -Y "
278
+        strCmd += "-Y "
279
+
280
+    # Set the chart ordinate label and chart title. 
281
+    strCmd += "-v %s -t %s " % (gLabel, gTitle)
282
+
283
+    # Show the data, or a moving average trend line over
284
+    # the data, or both.
285
+    strCmd += "DEF:%s=%s:%s:AVERAGE " % (dataItem, _RRD_FILE, dataItem)
286
+
287
+    if addTrend == 0 or addTrend == 2:
288
+        strCmd += "LINE1:%s\#0400ff " % (dataItem)
289
+    if addTrend == 1 or addTrend == 2:
290
+        strCmd += "CDEF:smoothed=%s,%s,TREND LINE1:smoothed#ff0000" \
291
+                  % (dataItem, trendWindow[gStart])
292
+       
269 293
     if debugOption:
270 294
         print "%s\n" % strCmd # DEBUG
271 295
     
272
-    # Run the command as a subprocess.
296
+    # Run the formatted rrdtool command as a subprocess.
273 297
     try:
274
-        result = subprocess.check_output(strCmd, stderr=subprocess.STDOUT, \
298
+        result = subprocess.check_output(strCmd, \
299
+                     stderr=subprocess.STDOUT,   \
275 300
                      shell=True)
276 301
     except subprocess.CalledProcessError, exError:
277
-        print "rdtool graph failed: %s" % (exError.output)
302
+        print "rrdtool graph failed: %s" % (exError.output)
278 303
         return False
279 304
 
280 305
     if debugOption:
281 306
         print "rrdtool graph: %s" % result
282
-
283 307
     return True
308
+
284 309
 ##end def
285 310
 
286 311
 def getCLarguments():
... ...
@@ -318,12 +343,12 @@ def generateGraphs():
318 343
        Parameters: none
319 344
        Returns nothing.
320 345
     """
321
-    createGraph('radGraph1', 'CPM', "'CPM - Last 24 Hours'", 'end-1day')
322
-    createGraph('radGraph2', 'SvperHr', "'Sv/Hr - Last 24 Hours'", 'end-1day')
323
-    createGraph('radGraph3', 'CPM', "'CPM - Last 4 Weeks'", 'end-4weeks')
324
-    createGraph('radGraph4', 'SvperHr', "'Sv/Hr - Last 4 Weeks'", 'end-4weeks')
325
-    createGraph('radGraph5', 'CPM', "'CPM - Past Year'", 'end-12months')
326
-    createGraph('radGraph6', 'SvperHr', "'Sv/Hr - Past Year'", 'end-12months')
346
+    createGraph('radGraph1', 'CPM', "'counts per minute'", "'CPM - Last 24 Hours'", 'end-1day', 0, 0, 2)
347
+    createGraph('radGraph2', 'SvperHr', "'Sv per hour'", "'Sv/Hr - Last 24 Hours'", 'end-1day', 0, 0, 2)
348
+    createGraph('radGraph3', 'CPM', "'counts per minute'", "'CPM - Last 4 Weeks'", 'end-4weeks', 0, 0, 2)
349
+    createGraph('radGraph4', 'SvperHr', "'Sv per hour'", "'Sv/Hr - Last 4 Weeks'", 'end-4weeks', 0, 0, 2)
350
+    createGraph('radGraph5', 'CPM', "'counts per minute'", "'CPM - Past Year'", 'end-12months', 0, 0, 2)
351
+    createGraph('radGraph6', 'SvperHr', "'Sv per hour'", "'Sv/Hr - Past Year'", 'end-12months', 0, 0, 2)
327 352
 ##end def
328 353
 
329 354
 def main():
Browse code

revisions 20151230

Jeff Owrey authored on 12/30/2015 20:43:27
Showing 1 changed files
... ...
@@ -43,7 +43,7 @@ import multiprocessing
43 43
     ### FILE AND FOLDER LOCATIONS ###
44 44
 
45 45
 _TMP_DIRECTORY = "/tmp/radmon" # folder for charts and output data file
46
-_RRD_FILE = "/home/{user}/database/radmonData.rrd"  # database that stores the data
46
+_RRD_FILE = "/home/{your user id}/database/radmonData.rrd"  # database that stores the data
47 47
 _OUTPUT_DATA_FILE = "/tmp/radmon/radmonData.js" # output file used by HTML docs
48 48
 
49 49
     ### GLOBAL CONSTANTS ###
... ...
@@ -177,8 +177,8 @@ def convertData(dData):
177 177
         local_sec = calendar.timegm(ts_utc)
178 178
         dData['UTC'] = local_sec
179 179
 
180
+        dData['Mode'] = dData['Mode'].lower()
180 181
         dData['uSvPerHr'] = dData.pop('uSv/hr')
181
-        dData['Mode'] = dData.pop('Mode').lower()
182 182
     except Exception, exError:
183 183
         print "%s convertData: %s" % (getTimeStamp(), exError)
184 184
         result = False
... ...
@@ -200,7 +200,7 @@ def writeOutputDataFile(dData):
200 200
     sData = '[{'
201 201
     for key in dData:
202 202
         sData += "\"%s\":\"%s\"," % (key, dData[key])
203
-    sData = sData[:-1] + '}]\n'
203
+    sData = sData[:-1] + '}]'
204 204
 
205 205
     # Write the string to the output data file for use by html documents.
206 206
     try:
Browse code

revisions 20151229

Jeff Owrey authored on 12/30/2015 04:41:17
Showing 1 changed files
... ...
@@ -56,9 +56,11 @@ _HTTP_REQUEST_TIMEOUT = 5 # number seconds to wait for a response to HTTP reques
56 56
    ### GLOBAL VARIABLES ###
57 57
 
58 58
 webUpdateInterval = _DEFAULT_WEB_DATA_UPDATE_INTERVAL  # web update frequency
59
-deviceUrl = "http://192.168.1.8"  # radiation monitor network address
59
+deviceUrl = "{your URL}"  # radiation monitor network address
60
+deviceOnline = True
60 61
 debugOption = False
61 62
 
63
+
62 64
   ###  PRIVATE METHODS  ###
63 65
 
64 66
 def getTimeStamp():
... ...
@@ -70,89 +72,97 @@ def getTimeStamp():
70 72
     return time.strftime( "%Y/%m/%d %T", time.localtime() )
71 73
 ##end def
72 74
 
73
-def sendOffLineStatusMessage():
74
-    """Sets the status of the the upstream device to "offline" and sends
75
+def setOfflineStatus(dData):
76
+    """Set the status of the the upstream device to "offline" and sends
75 77
        blank data to the downstream clients.
76
-       Parameters: none
78
+       Parameters:
79
+           dData - dictionary object containing weather data
77 80
        Returns nothing.
78 81
     """
79
-    sTmp = "\"date\":\"\",\"CPS\":\"\",\"CPM\":\"\"," \
80
-           "\"uSvPerHr\":\"\",\"Mode\":\"\",\"status\":\"offline\""
81
-
82
-    lsTmp = sTmp.split(',')
83
-    lsTmp[0] = "\"date\":\"%s\"" % getTimeStamp()
84
-    writeOutputDataFile(lsTmp)
82
+    global deviceOnline
83
+
84
+    # If the radiation monitor was previously online, then send a message
85
+    # that we are now offline.
86
+    if deviceOnline:
87
+        print "%s: radmon offline" % getTimeStamp()
88
+        deviceOnline = False
89
+
90
+    # Set data items to blank.
91
+    dData['UTC'] = ''
92
+    dData['CPM'] = ''
93
+    dData['CPS'] = ''
94
+    dData['uSvPerHr'] = ''
95
+    dData['Mode'] = ''
96
+    dData['status'] = 'offline'
97
+
98
+    writeOutputDataFile(dData)
85 99
     return
86 100
 ##end def
87 101
 
88 102
   ###  PUBLIC METHODS  ###
89 103
 
90 104
 def getRadmonData(deviceUrl, HttpRequestTimeout):
91
-        """Send http request to radiation monitoring device.  The response
92
-           from the device contains the radiation data.  The data is formatted
93
-           as an html document.
94
-        Parameters: 
95
-            deviceUrl - url of radiation monitoring device
96
-            HttpRequesttimeout - how long to wait for device
97
-                                 to respond to http request
98
-        Returns a string containing the radiation data, or None if
99
-        not successful.
100
-        """
101
-        content = ""
102
-        try:
103
-            conn = urllib2.urlopen(deviceUrl + "/jsdata", timeout=HttpRequestTimeout)
104
-        except Exception, exError:
105
-            # If no response is received from the device, then assume that
106
-            # the device is down or unavailable over the network.  In
107
-            # that case set the status of the device to offline.
108
-            print "%s: device offline: %s" % \
109
-                                (getTimeStamp(), exError)
110
-            return None
111
-        else:
112
-            for line in conn:
113
-                content += line.strip()
114
-            if len(content) == 0:
115
-                print "%s: HTTP download failed: null content" % \
116
-                    (getTimeStamp())
117
-                return None
118
-            del conn
119
-            return content
105
+    """Send http request to radiation monitoring device.  The response
106
+       from the device contains the radiation data.  The data is formatted
107
+       as an html document.
108
+    Parameters: 
109
+        deviceUrl - url of radiation monitoring device
110
+        HttpRequesttimeout - how long to wait for device
111
+                             to respond to http request
112
+    Returns a string containing the radiation data, or None if
113
+    not successful.
114
+    """
115
+    global deviceOnline
116
+
117
+    try:
118
+        conn = urllib2.urlopen(deviceUrl + "/rdata", timeout=HttpRequestTimeout)
119
+    except Exception, exError:
120
+        # If no response is received from the device, then assume that
121
+        # the device is down or unavailable over the network.  In
122
+        # that case set the status of the device to offline.
123
+        if debugOption:
124
+            print "getRadmonData: %s\n" % exError
125
+        return None
126
+
127
+    # If the radiation monitor was previously offline, then send a message
128
+    # that we are now online.
129
+    if not deviceOnline:
130
+        print "%s radmon online" % getTimeStamp()
131
+        deviceOnline = True
132
+
133
+    # Format received data into a single string.
134
+    content = ""
135
+    for line in conn:
136
+         content += line.strip()
137
+    del conn
138
+    return content
120 139
 ##end def
121 140
 
122
-def parseDataString(sData, lsData, dData):
141
+def parseDataString(sData, dData):
123 142
     """Parse the radiation data JSON string from the radiation 
124 143
        monitoring device into its component parts.  
125 144
        Parameters:
126 145
            sData - the string containing the data to be parsed
127
-          lsData - a list object to contain the parsed data items
128 146
            dData - a dictionary object to contain the parsed data items
129 147
        Returns true if successful, false otherwise.
130 148
     """
131
-    # Clear data array in preparation for loading reformatted data.
132
-    while len(lsData) > 0:
133
-        elmt = lsData.pop(0)
134
-
135 149
     try:
136
-        dTmp = json.loads(sData[1:-1])
137
-        sTmp = dTmp['radmon'].encode('ascii', 'ignore')
150
+        sTmp = sData[2:-2]
138 151
         lsTmp = sTmp.split(',')
139
-        lsData.extend(lsTmp)
140 152
     except Exception, exError:
141
-        print "%s parse failed: %s" % (getTimeStamp(), exError)
153
+        print "%s parseDataString: %s" % (getTimeStamp(), exError)
142 154
         return False
143 155
 
144
-    # Since the device responded, set the status to online.
145
-    lsData.insert(-2, "status=online")
146
-
147 156
     # Load the parsed data into a dictionary for easy access.
148
-    for item in lsData:
157
+    for item in lsTmp:
149 158
         if "=" in item:
150 159
             dData[item.split('=')[0]] = item.split('=')[1]
160
+    dData['status'] = 'online'
151 161
 
152 162
     return True
153 163
 ##end def
154 164
 
155
-def convertData(lsData, dData):
165
+def convertData(dData):
156 166
     """Convert individual radiation data items as necessary.
157 167
        Parameters:
158 168
            lsData - a list object containing the radiation data
... ...
@@ -166,47 +176,44 @@ def convertData(lsData, dData):
166 176
         ts_utc = time.strptime(dData['UTC'], "%H:%M:%S %m/%d/%Y")
167 177
         local_sec = calendar.timegm(ts_utc)
168 178
         dData['UTC'] = local_sec
169
-    except:
170
-        print "%s invalid time: %s" % (getTimeStamp(), utc)
171
-        result = False
172
-
173
-    # Clear data array in preparation for loading reformatted data.
174
-    while len(lsData) > 0:
175
-        elmt = lsData.pop(0)
176 179
 
177
-    lsData.append("\"UTC\":\"%s\"" % dData['UTC'])
178
-    lsData.append("\"CPS\":\"%s\"" % dData['CPS'])
179
-    lsData.append("\"CPM\":\"%s\"" % dData['CPM'])
180
-    lsData.append("\"uSvPerHr\":\"%s\"" % dData['uSv/hr'])
181
-    lsData.append("\"Mode\":\"%s\"" % dData['Mode'].lower())
182
-    lsData.append("\"status\":\"%s\"" % dData['status'])
180
+        dData['uSvPerHr'] = dData.pop('uSv/hr')
181
+        dData['Mode'] = dData.pop('Mode').lower()
182
+    except Exception, exError:
183
+        print "%s convertData: %s" % (getTimeStamp(), exError)
184
+        result = False
183 185
 
184 186
     return result
185 187
 ##end def
186 188
 
187
-def writeOutputDataFile(lsData):
189
+def writeOutputDataFile(dData):
188 190
     """Convert individual weather string data items as necessary.
189 191
        Parameters:
190 192
            lsData - a list object containing the data to be written
191 193
                     to the JSON file
192 194
        Returns true if successful, false otherwise.
193 195
     """
194
-    # Convert the list object to a string.
195
-    sTmp = ','.join(lsData)
196
+    # Set date to current time and data
197
+    dData['date'] = getTimeStamp()
196 198
 
197
-    # Apply JSON formatting to the string and write it to a
198
-    # file for use by html documents.
199
-    sData = "[{%s}]\n" % (sTmp)
199
+    # Format the weather data as string using java script object notation.
200
+    sData = '[{'
201
+    for key in dData:
202
+        sData += "\"%s\":\"%s\"," % (key, dData[key])
203
+    sData = sData[:-1] + '}]\n'
200 204
 
205
+    # Write the string to the output data file for use by html documents.
201 206
     try:
202 207
         fc = open(_OUTPUT_DATA_FILE, "w")
203 208
         fc.write(sData)
204 209
         fc.close()
205 210
     except Exception, exError:
206
-        print "%s: write to JSON file failed: %s" % \
207
-                             (getTimeStamp(), exError)
211
+        print "%s writeOutputDataFile: %s" % (getTimeStamp(), exError)
208 212
         return False
209 213
 
214
+    if debugOption:
215
+        print sData
216
+
210 217
     return True
211 218
 ## end def
212 219
 
... ...
@@ -220,7 +227,7 @@ def updateDatabase(dData):
220 227
     Returns true if successful, false otherwise.
221 228
     """
222 229
     # The RR database stores whole units, so convert uSv to Sv.   
223
-    Svvalue = float(dData['uSv/hr']) * 1.0E-06 # convert micro-Sieverts to Sieverts
230
+    Svvalue = float(dData['uSvPerHr']) * 1.0E-06 # convert micro-Sieverts to Sieverts
224 231
 
225 232
     # Create the rrdtool update command.
226 233
     strCmd = "rrdtool update %s %s:%s:%s" % \
... ...
@@ -328,7 +335,7 @@ def main():
328 335
 
329 336
     lastChartUpdateTime = - 1 # last time charts generated
330 337
     lastDatabaseUpdateTime = -1 # last time the rrdtool database updated
331
-    lastWebDataUpdateTime = -1 # last time output JSON file updated
338
+    lastWebUpdateTime = -1 # last time output JSON file updated
332 339
     dData = {}  # dictionary object for temporary data storage
333 340
     lsData = [] # list object for temporary data storage
334 341
 
... ...
@@ -351,28 +358,27 @@ def main():
351 358
 
352 359
         # At the radiation device query interval request and process
353 360
         # the data from the device.
354
-        if currentTime - lastWebDataUpdateTime > webUpdateInterval:
355
-            llastWebDataUpdateTime = currentTime
361
+        if currentTime - lastWebUpdateTime > webUpdateInterval:
362
+            lastWebUpdateTime = currentTime
356 363
             result = True
357 364
 
358 365
             # Get the data string from the device.
359 366
             sData = getRadmonData(deviceUrl, _HTTP_REQUEST_TIMEOUT)
360 367
             if sData == None:
361
-                sendOffLineStatusMessage()
368
+                setOfflineStatus(dData)
362 369
                 result = False
363 370
 
364 371
             # If successful parse the data.
365 372
             if result:
366
-                result = parseDataString(sData, lsData, dData)
373
+                result = parseDataString(sData, dData)
367 374
 
368 375
             # If parsing successful, convert the data.
369 376
             if result:
370
-                result = convertData(lsData, dData)
377
+                result = convertData(dData)
371 378
 
372 379
             # If conversion successful, write data to output file.
373 380
             if result:
374
-                lsData[0] = "\"date\":\"%s\"" % getTimeStamp()
375
-                writeOutputDataFile(lsData)
381
+                writeOutputDataFile(dData)
376 382
 
377 383
         # At the rrdtool database update interval, update the database.
378 384
         if currentTime - lastDatabaseUpdateTime > _DATABASE_UPDATE_INTERVAL:   
Browse code

bug fix 20151227

Jeff Owrey authored on 12/28/2015 02:58:35
Showing 1 changed files
... ...
@@ -40,19 +40,23 @@ import os
40 40
 import json
41 41
 import multiprocessing
42 42
 
43
-## Define constants
44
-_TMP_DIRECTORY = "/tmp/radmon"
45
-_RRD_FILE = "/home/pi/database/radmonData.rrd"  # the file that stores the data
46
-_OUTPUT_DATA_FILE = "/tmp/radmon/radmonData.js"
47
-
48
-_WEB_DATA_UPDATE_INTERVAL = 1
49
-_CHART_UPDATE_INTERVAL = 60
50
-_DATABASE_UPDATE_INTERVAL = 30
43
+    ### FILE AND FOLDER LOCATIONS ###
44
+
45
+_TMP_DIRECTORY = "/tmp/radmon" # folder for charts and output data file
46
+_RRD_FILE = "/home/{user}/database/radmonData.rrd"  # database that stores the data
47
+_OUTPUT_DATA_FILE = "/tmp/radmon/radmonData.js" # output file used by HTML docs
48
+
49
+    ### GLOBAL CONSTANTS ###
50
+
51
+_DEFAULT_WEB_DATA_UPDATE_INTERVAL = 10
52
+_CHART_UPDATE_INTERVAL = 60 # defines how often the charts get updated
53
+_DATABASE_UPDATE_INTERVAL = 30 # defines how often the database gets updated
51 54
 _HTTP_REQUEST_TIMEOUT = 5 # number seconds to wait for a response to HTTP request
52 55
 
53
-## Define run time options
54
-deviceQueryInterval = 5.0  # period defines how often the RR database gets updated
55
-deviceUrl = "http://{IP address}:{port}/jsdata"  # radiation monitor network address
56
+   ### GLOBAL VARIABLES ###
57
+
58
+webUpdateInterval = _DEFAULT_WEB_DATA_UPDATE_INTERVAL  # web update frequency
59
+deviceUrl = "http://192.168.1.8"  # radiation monitor network address
56 60
 debugOption = False
57 61
 
58 62
   ###  PRIVATE METHODS  ###
... ...
@@ -66,23 +70,24 @@ def getTimeStamp():
66 70
     return time.strftime( "%Y/%m/%d %T", time.localtime() )
67 71
 ##end def
68 72
 
69
-def sendOffLineStatusMsg():
73
+def sendOffLineStatusMessage():
70 74
     """Sets the status of the the upstream device to "offline" and sends
71 75
        blank data to the downstream clients.
72 76
        Parameters: none
73 77
        Returns nothing.
74 78
     """
75
-    sTmp = "\"time\":\"\",\"CPS\":\"\",\"CPM\":\"\"," \
79
+    sTmp = "\"date\":\"\",\"CPS\":\"\",\"CPM\":\"\"," \
76 80
            "\"uSvPerHr\":\"\",\"Mode\":\"\",\"status\":\"offline\""
77 81
 
78 82
     lsTmp = sTmp.split(',')
79
-    writeJSONfile(lsTmp)
83
+    lsTmp[0] = "\"date\":\"%s\"" % getTimeStamp()
84
+    writeOutputDataFile(lsTmp)
80 85
     return
81 86
 ##end def
82 87
 
83 88
   ###  PUBLIC METHODS  ###
84 89
 
85
-def getDataString(deviceUrl, HttpRequestTimeout):
90
+def getRadmonData(deviceUrl, HttpRequestTimeout):
86 91
         """Send http request to radiation monitoring device.  The response
87 92
            from the device contains the radiation data.  The data is formatted
88 93
            as an html document.
... ...
@@ -95,14 +100,13 @@ def getDataString(deviceUrl, HttpRequestTimeout):
95 100
         """
96 101
         content = ""
97 102
         try:
98
-            conn = urllib2.urlopen(deviceUrl, timeout=HttpRequestTimeout)
103
+            conn = urllib2.urlopen(deviceUrl + "/jsdata", timeout=HttpRequestTimeout)
99 104
         except Exception, exError:
100 105
             # If no response is received from the device, then assume that
101 106
             # the device is down or unavailable over the network.  In
102 107
             # that case set the status of the device to offline.
103 108
             print "%s: device offline: %s" % \
104 109
                                 (getTimeStamp(), exError)
105
-            sendOffLineStatusMsg()
106 110
             return None
107 111
         else:
108 112
             for line in conn:
... ...
@@ -112,9 +116,6 @@ def getDataString(deviceUrl, HttpRequestTimeout):
112 116
                     (getTimeStamp())
113 117
                 return None
114 118
             del conn
115
-            
116
-            if debugOption:
117
-                print "%s\n" % content # DEBUG
118 119
             return content
119 120
 ##end def
120 121
 
... ...
@@ -148,9 +149,6 @@ def parseDataString(sData, lsData, dData):
148 149
         if "=" in item:
149 150
             dData[item.split('=')[0]] = item.split('=')[1]
150 151
 
151
-    if debugOption and 0:
152
-        print lsData
153
-        print dData
154 152
     return True
155 153
 ##end def
156 154
 
... ...
@@ -186,7 +184,7 @@ def convertData(lsData, dData):
186 184
     return result
187 185
 ##end def
188 186
 
189
-def writeJSONfile(lsData):
187
+def writeOutputDataFile(lsData):
190 188
     """Convert individual weather string data items as necessary.
191 189
        Parameters:
192 190
            lsData - a list object containing the data to be written
... ...
@@ -198,16 +196,17 @@ def writeJSONfile(lsData):
198 196
 
199 197
     # Apply JSON formatting to the string and write it to a
200 198
     # file for use by html documents.
201
-    strJSON = "[{%s}]\n" % (sTmp)
199
+    sData = "[{%s}]\n" % (sTmp)
202 200
 
203 201
     try:
204 202
         fc = open(_OUTPUT_DATA_FILE, "w")
205
-        fc.write(strJSON)
203
+        fc.write(sData)
206 204
         fc.close()
207 205
     except Exception, exError:
208 206
         print "%s: write to JSON file failed: %s" % \
209 207
                              (getTimeStamp(), exError)
210 208
         return False
209
+
211 210
     return True
212 211
 ## end def
213 212
 
... ...
@@ -284,7 +283,7 @@ def getCLarguments():
284 283
           -u sets the url of the radiation monitoring device
285 284
        Returns nothing.
286 285
     """
287
-    global debugOption, deviceQueryInterval, deviceURL
286
+    global debugOption, webUpdateInterval, deviceUrl
288 287
 
289 288
     index = 1
290 289
     while index < len(sys.argv):
... ...
@@ -292,13 +291,13 @@ def getCLarguments():
292 291
             debugOption = True
293 292
         elif sys.argv[index] == '-t':
294 293
             try:
295
-                deviceQueryInterval = abs(int(sys.argv[index + 1]))
294
+                webUpdateInterval = abs(int(sys.argv[index + 1]))
296 295
             except:
297 296
                 print "invalid polling period"
298 297
                 exit(-1)
299 298
             index += 1
300 299
         elif sys.argv[index] == '-u':
301
-            deviceURL = sys.argv[index + 1]
300
+            deviceUrl = sys.argv[index + 1]
302 301
             index += 1
303 302
         else:
304 303
             cmd_name = sys.argv[0].split('/')
... ...
@@ -326,10 +325,10 @@ def main():
326 325
        Parameters: none
327 326
        Returns nothing.
328 327
     """
328
+
329 329
     lastChartUpdateTime = - 1 # last time charts generated
330 330
     lastDatabaseUpdateTime = -1 # last time the rrdtool database updated
331 331
     lastWebDataUpdateTime = -1 # last time output JSON file updated
332
-    lastDeviceQueryTime = -1 # last time radiation device queried for data
333 332
     dData = {}  # dictionary object for temporary data storage
334 333
     lsData = [] # list object for temporary data storage
335 334
 
... ...
@@ -352,13 +351,14 @@ def main():
352 351
 
353 352
         # At the radiation device query interval request and process
354 353
         # the data from the device.
355
-        if currentTime - lastDeviceQueryTime > deviceQueryInterval:
356
-            lastDeviceQueryTime = currentTime
354
+        if currentTime - lastWebDataUpdateTime > webUpdateInterval:
355
+            llastWebDataUpdateTime = currentTime
357 356
             result = True
358 357
 
359 358
             # Get the data string from the device.
360
-            sData = getDataString(deviceUrl, _HTTP_REQUEST_TIMEOUT)
359
+            sData = getRadmonData(deviceUrl, _HTTP_REQUEST_TIMEOUT)
361 360
             if sData == None:
361
+                sendOffLineStatusMessage()
362 362
                 result = False
363 363
 
364 364
             # If successful parse the data.
... ...
@@ -368,19 +368,16 @@ def main():
368 368
             # If parsing successful, convert the data.
369 369
             if result:
370 370
                 result = convertData(lsData, dData)
371
-                
372
-        # At the web update interval, update the JSON file used to pass
373
-        # radiation data to html documents.
374
-        if currentTime - lastWebDataUpdateTime > _WEB_DATA_UPDATE_INTERVAL:
375
-            lastWebDataUpdateTime = currentTime
371
+
372
+            # If conversion successful, write data to output file.
376 373
             if result:
377
-                lsData[0] = "\"time\":\"%s\"" % getTimeStamp()
378
-                writeJSONfile(lsData)
374
+                lsData[0] = "\"date\":\"%s\"" % getTimeStamp()
375
+                writeOutputDataFile(lsData)
379 376
 
380 377
         # At the rrdtool database update interval, update the database.
381 378
         if currentTime - lastDatabaseUpdateTime > _DATABASE_UPDATE_INTERVAL:   
379
+            lastDatabaseUpdateTime = currentTime
382 380
             if result:
383
-                lastDatabaseUpdateTime = currentTime
384 381
                 ## Update the round robin database with the parsed data.
385 382
                 result = updateDatabase(dData)
386 383
 
... ...
@@ -395,8 +392,8 @@ def main():
395 392
 
396 393
         elapsedTime = time.time() - currentTime
397 394
         if debugOption:
398
-            print "processing time: %s\n" % elapsedTime
399
-        remainingTime = _WEB_DATA_UPDATE_INTERVAL - elapsedTime
395
+            print "web update: %6f sec\n" % elapsedTime
396
+        remainingTime = webUpdateInterval - elapsedTime
400 397
         if remainingTime > 0:
401 398
             time.sleep(remainingTime)
402 399
              
Browse code

minor revision

Jeff Owrey authored on 10/21/2015 02:54:46
Showing 1 changed files
... ...
@@ -52,7 +52,7 @@ _HTTP_REQUEST_TIMEOUT = 5 # number seconds to wait for a response to HTTP reques
52 52
 
53 53
 ## Define run time options
54 54
 deviceQueryInterval = 5.0  # period defines how often the RR database gets updated
55
-deviceUrl = "http://73.157.139.23:4371/jsdata"  # radiation monitor network address
55
+deviceUrl = "http://{IP address}:{port}/jsdata"  # radiation monitor network address
56 56
 debugOption = False
57 57
 
58 58
   ###  PRIVATE METHODS  ###
Browse code

first release

Jeff Owrey authored on 09/25/2015 03:44:17
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,408 @@
1
+#!/usr/bin/python -u
2
+## The -u option above turns off block buffering of python output. This assures
3
+## that each error message gets individually printed to the log file.
4
+#
5
+# Module: radmonAgent.py
6
+#
7
+# Description: This module acts as an agent between the radiation monitoring device
8
+# and the Internet web server.  The agent periodically sends an http request to the
9
+# radiation monitoring device and processes the response from the device and performs
10
+# a number of operations:
11
+#     - conversion of data items
12
+#     - update a round robin (rrdtool) database with the radiation data
13
+#     - periodically generate graphic charts for display in html documents
14
+#     - forward the radiation data to other services
15
+#     - write the processed weather data to a JSON file for use by html documents
16
+#
17
+# Copyright 2015 Jeff Owrey
18
+#    This program is free software: you can redistribute it and/or modify
19
+#    it under the terms of the GNU General Public License as published by
20
+#    the Free Software Foundation, either version 3 of the License, or
21
+#    (at your option) any later version.
22
+#
23
+#    This program is distributed in the hope that it will be useful,
24
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
25
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26
+#    GNU General Public License for more details.
27
+#
28
+#    You should have received a copy of the GNU General Public License
29
+#    along with this program.  If not, see http://www.gnu.org/license.
30
+#
31
+# Revision History
32
+#   * v20 released 15 Sep 2015 by J L Owrey
33
+#
34
+import urllib2
35
+import time
36
+import calendar
37
+import subprocess
38
+import sys
39
+import os
40
+import json
41
+import multiprocessing
42
+
43
+## Define constants
44
+_TMP_DIRECTORY = "/tmp/radmon"
45
+_RRD_FILE = "/home/pi/database/radmonData.rrd"  # the file that stores the data
46
+_OUTPUT_DATA_FILE = "/tmp/radmon/radmonData.js"
47
+
48
+_WEB_DATA_UPDATE_INTERVAL = 1
49
+_CHART_UPDATE_INTERVAL = 60
50
+_DATABASE_UPDATE_INTERVAL = 30
51
+_HTTP_REQUEST_TIMEOUT = 5 # number seconds to wait for a response to HTTP request
52
+
53
+## Define run time options
54
+deviceQueryInterval = 5.0  # period defines how often the RR database gets updated
55
+deviceUrl = "http://73.157.139.23:4371/jsdata"  # radiation monitor network address
56
+debugOption = False
57
+
58
+  ###  PRIVATE METHODS  ###
59
+
60
+def getTimeStamp():
61
+    """
62
+    Sets the error message time stamp to the local system time.
63
+    Parameters: none
64
+    Returns string containing the time stamp.
65
+    """
66
+    return time.strftime( "%Y/%m/%d %T", time.localtime() )
67
+##end def
68
+
69
+def sendOffLineStatusMsg():
70
+    """Sets the status of the the upstream device to "offline" and sends
71
+       blank data to the downstream clients.
72
+       Parameters: none
73
+       Returns nothing.
74
+    """
75
+    sTmp = "\"time\":\"\",\"CPS\":\"\",\"CPM\":\"\"," \
76
+           "\"uSvPerHr\":\"\",\"Mode\":\"\",\"status\":\"offline\""
77
+
78
+    lsTmp = sTmp.split(',')
79
+    writeJSONfile(lsTmp)
80
+    return
81
+##end def
82
+
83
+  ###  PUBLIC METHODS  ###
84
+
85
+def getDataString(deviceUrl, HttpRequestTimeout):
86
+        """Send http request to radiation monitoring device.  The response
87
+           from the device contains the radiation data.  The data is formatted
88
+           as an html document.
89
+        Parameters: 
90
+            deviceUrl - url of radiation monitoring device
91
+            HttpRequesttimeout - how long to wait for device
92
+                                 to respond to http request
93
+        Returns a string containing the radiation data, or None if
94
+        not successful.
95
+        """
96
+        content = ""
97
+        try:
98
+            conn = urllib2.urlopen(deviceUrl, timeout=HttpRequestTimeout)
99
+        except Exception, exError:
100
+            # If no response is received from the device, then assume that
101
+            # the device is down or unavailable over the network.  In
102
+            # that case set the status of the device to offline.
103
+            print "%s: device offline: %s" % \
104
+                                (getTimeStamp(), exError)
105
+            sendOffLineStatusMsg()
106
+            return None
107
+        else:
108
+            for line in conn:
109
+                content += line.strip()
110
+            if len(content) == 0:
111
+                print "%s: HTTP download failed: null content" % \
112
+                    (getTimeStamp())
113
+                return None
114
+            del conn
115
+            
116
+            if debugOption:
117
+                print "%s\n" % content # DEBUG
118
+            return content
119
+##end def
120
+
121
+def parseDataString(sData, lsData, dData):
122
+    """Parse the radiation data JSON string from the radiation 
123
+       monitoring device into its component parts.  
124
+       Parameters:
125
+           sData - the string containing the data to be parsed
126
+          lsData - a list object to contain the parsed data items
127
+           dData - a dictionary object to contain the parsed data items
128
+       Returns true if successful, false otherwise.
129
+    """
130
+    # Clear data array in preparation for loading reformatted data.
131
+    while len(lsData) > 0:
132
+        elmt = lsData.pop(0)
133
+
134
+    try:
135
+        dTmp = json.loads(sData[1:-1])
136
+        sTmp = dTmp['radmon'].encode('ascii', 'ignore')
137
+        lsTmp = sTmp.split(',')
138
+        lsData.extend(lsTmp)
139
+    except Exception, exError:
140
+        print "%s parse failed: %s" % (getTimeStamp(), exError)
141
+        return False
142
+
143
+    # Since the device responded, set the status to online.
144
+    lsData.insert(-2, "status=online")
145
+
146
+    # Load the parsed data into a dictionary for easy access.
147
+    for item in lsData:
148
+        if "=" in item:
149
+            dData[item.split('=')[0]] = item.split('=')[1]
150
+
151
+    if debugOption and 0:
152
+        print lsData
153
+        print dData
154
+    return True
155
+##end def
156
+
157
+def convertData(lsData, dData):
158
+    """Convert individual radiation data items as necessary.
159
+       Parameters:
160
+           lsData - a list object containing the radiation data
161
+           dData - a dictionary object containing the radiation data
162
+       Returns true if successful, false otherwise.
163
+    """
164
+    result = True
165
+ 
166
+    try:
167
+        # Convert UTC from radiation monitoring device to local time.
168
+        ts_utc = time.strptime(dData['UTC'], "%H:%M:%S %m/%d/%Y")
169
+        local_sec = calendar.timegm(ts_utc)
170
+        dData['UTC'] = local_sec
171
+    except:
172
+        print "%s invalid time: %s" % (getTimeStamp(), utc)
173
+        result = False
174
+
175
+    # Clear data array in preparation for loading reformatted data.
176
+    while len(lsData) > 0:
177
+        elmt = lsData.pop(0)
178
+
179
+    lsData.append("\"UTC\":\"%s\"" % dData['UTC'])
180
+    lsData.append("\"CPS\":\"%s\"" % dData['CPS'])
181
+    lsData.append("\"CPM\":\"%s\"" % dData['CPM'])
182
+    lsData.append("\"uSvPerHr\":\"%s\"" % dData['uSv/hr'])
183
+    lsData.append("\"Mode\":\"%s\"" % dData['Mode'].lower())
184
+    lsData.append("\"status\":\"%s\"" % dData['status'])
185
+
186
+    return result
187
+##end def
188
+
189
+def writeJSONfile(lsData):
190
+    """Convert individual weather string data items as necessary.
191
+       Parameters:
192
+           lsData - a list object containing the data to be written
193
+                    to the JSON file
194
+       Returns true if successful, false otherwise.
195
+    """
196
+    # Convert the list object to a string.
197
+    sTmp = ','.join(lsData)
198
+
199
+    # Apply JSON formatting to the string and write it to a
200
+    # file for use by html documents.
201
+    strJSON = "[{%s}]\n" % (sTmp)
202
+
203
+    try:
204
+        fc = open(_OUTPUT_DATA_FILE, "w")
205
+        fc.write(strJSON)
206
+        fc.close()
207
+    except Exception, exError:
208
+        print "%s: write to JSON file failed: %s" % \
209
+                             (getTimeStamp(), exError)
210
+        return False
211
+    return True
212
+## end def
213
+
214
+def updateDatabase(dData):
215
+    """
216
+    Updates the rrdtool database by executing an rrdtool system command.
217
+    Formats the command using the data extracted from the radiation
218
+    monitor response.   
219
+    Parameters: dData - dictionary object containing data items to be
220
+                        written to the rr database file
221
+    Returns true if successful, false otherwise.
222
+    """
223
+    # The RR database stores whole units, so convert uSv to Sv.   
224
+    Svvalue = float(dData['uSv/hr']) * 1.0E-06 # convert micro-Sieverts to Sieverts
225
+
226
+    # Create the rrdtool update command.
227
+    strCmd = "rrdtool update %s %s:%s:%s" % \
228
+                       (_RRD_FILE, dData['UTC'], dData['CPM'], Svvalue)
229
+    if debugOption:
230
+        print "%s\n" % strCmd # DEBUG
231
+
232
+    # Run the command as a subprocess.
233
+    try:
234
+        subprocess.check_output(strCmd, shell=True,  \
235
+                             stderr=subprocess.STDOUT)
236
+    except subprocess.CalledProcessError, exError:
237
+        print "%s: rrdtool update failed: %s" % \
238
+                                 (getTimeStamp(), exError.output)
239
+        return False
240
+
241
+    return True
242
+##end def
243
+
244
+def createGraph(fileName, dataItem, gTitle, gStart):
245
+    """Uses rrdtool to create a graph of specified weather data item.
246
+       Parameters:
247
+           fileName - name of graph image file
248
+           dataItem - data item to be graphed
249
+           gTitle - a title for the graph
250
+           gStart - beginning time of the data to be graphed
251
+       Returns true if successful, false otherwise.
252
+    """
253
+    gPath = _TMP_DIRECTORY + '/' + fileName + ".png"
254
+
255
+    # Create the rrdtool graph command.
256
+    strFmt = ("rrdtool graph %s -a PNG -s %s -w 600 -h 150 "
257
+                           ##  "-l 50 -u 110 -r "
258
+                               "-v %s -t %s "
259
+                               "DEF:%s=%s:%s:AVERAGE "
260
+                               "LINE2:%s\#0400ff:")         
261
+    strCmd = strFmt % (gPath, gStart, dataItem, gTitle, dataItem, \
262
+                       _RRD_FILE, dataItem, dataItem)
263
+    if debugOption:
264
+        print "%s\n" % strCmd # DEBUG
265
+    
266
+    # Run the command as a subprocess.
267
+    try:
268
+        result = subprocess.check_output(strCmd, stderr=subprocess.STDOUT, \
269
+                     shell=True)
270
+    except subprocess.CalledProcessError, exError:
271
+        print "rdtool graph failed: %s" % (exError.output)
272
+        return False
273
+
274
+    if debugOption:
275
+        print "rrdtool graph: %s" % result
276
+
277
+    return True
278
+##end def
279
+
280
+def getCLarguments():
281
+    """Get command line arguments.  There are three possible arguments
282
+          -d turns on debug mode
283
+          -t sets the radiation device query interval
284
+          -u sets the url of the radiation monitoring device
285
+       Returns nothing.
286
+    """
287
+    global debugOption, deviceQueryInterval, deviceURL
288
+
289
+    index = 1
290
+    while index < len(sys.argv):
291
+        if sys.argv[index] == '-d':
292
+            debugOption = True
293
+        elif sys.argv[index] == '-t':
294
+            try:
295
+                deviceQueryInterval = abs(int(sys.argv[index + 1]))
296
+            except:
297
+                print "invalid polling period"
298
+                exit(-1)
299
+            index += 1
300
+        elif sys.argv[index] == '-u':
301
+            deviceURL = sys.argv[index + 1]
302
+            index += 1
303
+        else:
304
+            cmd_name = sys.argv[0].split('/')
305
+            print "Usage: %s {-v} {-d}" % cmd_name[-1]
306
+            exit(-1)
307
+        index += 1
308
+##end def
309
+
310
+def generateGraphs():
311
+    """Generate graphs for display in html documents.
312
+       Parameters: none
313
+       Returns nothing.
314
+    """
315
+    createGraph('radGraph1', 'CPM', "'CPM - Last 24 Hours'", 'end-1day')
316
+    createGraph('radGraph2', 'SvperHr', "'Sv/Hr - Last 24 Hours'", 'end-1day')
317
+    createGraph('radGraph3', 'CPM', "'CPM - Last 4 Weeks'", 'end-4weeks')
318
+    createGraph('radGraph4', 'SvperHr', "'Sv/Hr - Last 4 Weeks'", 'end-4weeks')
319
+    createGraph('radGraph5', 'CPM', "'CPM - Past Year'", 'end-12months')
320
+    createGraph('radGraph6', 'SvperHr', "'Sv/Hr - Past Year'", 'end-12months')
321
+##end def
322
+
323
+def main():
324
+    """Handles timing of events and acts as executive routine managing all other
325
+       functions.
326
+       Parameters: none
327
+       Returns nothing.
328
+    """
329
+    lastChartUpdateTime = - 1 # last time charts generated
330
+    lastDatabaseUpdateTime = -1 # last time the rrdtool database updated
331
+    lastWebDataUpdateTime = -1 # last time output JSON file updated
332
+    lastDeviceQueryTime = -1 # last time radiation device queried for data
333
+    dData = {}  # dictionary object for temporary data storage
334
+    lsData = [] # list object for temporary data storage
335
+
336
+    ## Get command line arguments.
337
+    getCLarguments()
338
+
339
+    ## Create www data folder if it does not already exist.
340
+    if not os.path.isdir(_TMP_DIRECTORY):
341
+        os.makedirs(_TMP_DIRECTORY)
342
+
343
+    ## Exit with error if cannot find the rrdtool database file.
344
+    if not os.path.exists(_RRD_FILE):
345
+        print "cannot find rrdtool database file: terminating"
346
+        exit(1)
347
+ 
348
+    ## main loop
349
+    while True:
350
+
351
+        currentTime = time.time()
352
+
353
+        # At the radiation device query interval request and process
354
+        # the data from the device.
355
+        if currentTime - lastDeviceQueryTime > deviceQueryInterval:
356
+            lastDeviceQueryTime = currentTime
357
+            result = True
358
+
359
+            # Get the data string from the device.
360
+            sData = getDataString(deviceUrl, _HTTP_REQUEST_TIMEOUT)
361
+            if sData == None:
362
+                result = False
363
+
364
+            # If successful parse the data.
365
+            if result:
366
+                result = parseDataString(sData, lsData, dData)
367
+
368
+            # If parsing successful, convert the data.
369
+            if result:
370
+                result = convertData(lsData, dData)
371
+                
372
+        # At the web update interval, update the JSON file used to pass
373
+        # radiation data to html documents.
374
+        if currentTime - lastWebDataUpdateTime > _WEB_DATA_UPDATE_INTERVAL:
375
+            lastWebDataUpdateTime = currentTime
376
+            if result:
377
+                lsData[0] = "\"time\":\"%s\"" % getTimeStamp()
378
+                writeJSONfile(lsData)
379
+
380
+        # At the rrdtool database update interval, update the database.
381
+        if currentTime - lastDatabaseUpdateTime > _DATABASE_UPDATE_INTERVAL:   
382
+            if result:
383
+                lastDatabaseUpdateTime = currentTime
384
+                ## Update the round robin database with the parsed data.
385
+                result = updateDatabase(dData)
386
+
387
+        # At the chart generation interval, generate charts.
388
+        if currentTime - lastChartUpdateTime > _CHART_UPDATE_INTERVAL:
389
+            lastChartUpdateTime = currentTime
390
+            p = multiprocessing.Process(target=generateGraphs, args=())
391
+            p.start()
392
+
393
+        # Relinquish processing back to the operating system until
394
+        # the next update interval.
395
+
396
+        elapsedTime = time.time() - currentTime
397
+        if debugOption:
398
+            print "processing time: %s\n" % elapsedTime
399
+        remainingTime = _WEB_DATA_UPDATE_INTERVAL - elapsedTime
400
+        if remainingTime > 0:
401
+            time.sleep(remainingTime)
402
+             
403
+    ## end while
404
+## end def
405
+
406
+if __name__ == '__main__':
407
+    main()
408
+