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)