Browse code

minor revisions

gandolf authored on 07/05/2022 17:58:34
Showing 4 changed files
... ...
@@ -46,11 +46,11 @@
46 46
 import os
47 47
 import sys
48 48
 import signal
49
-import subprocess
50 49
 import multiprocessing
51 50
 import time
52 51
 import json
53 52
 from urllib.request import urlopen
53
+import rrdbase
54 54
 
55 55
    ### ENVIRONMENT ###
56 56
 
... ...
@@ -59,10 +59,7 @@ _SERVER_MODE = "primary"
59 59
 
60 60
    ### DEFAULT AREDN NODE URL ###
61 61
 
62
-# set url of the aredn node
63
-
64
-_DEFAULT_AREDN_NODE_URL = \
65
-    "{your node url}"
62
+_DEFAULT_AREDN_NODE_URL = "http://localnode.local.mesh/cgi-bin/status"
66 63
 
67 64
     ### FILE AND FOLDER LOCATIONS ###
68 65
 
... ...
@@ -77,13 +74,19 @@ _RRD_FILE = "/home/%s/database/arednsigData.rrd" % _USER
77 74
 
78 75
     ### GLOBAL CONSTANTS ###
79 76
 
80
-# max number of failed data requests allowed
77
+# maximum number of failed data requests allowed
81 78
 _MAX_FAILED_DATA_REQUESTS = 2
82
-# AREDN node data request interval in seconds
79
+# maximum number of http request retries  allowed
80
+_MAX_HTTP_RETRIES = 3
81
+# delay time between http request retries
82
+_HTTP_RETRY_DELAY = 1.1199
83
+# interval in seconds between data requests
83 84
 _DEFAULT_DATA_REQUEST_INTERVAL = 60
84 85
 # number seconds to wait for a response to HTTP request
85
-_HTTP_REQUEST_TIMEOUT = 5
86
+_HTTP_REQUEST_TIMEOUT = 3
86 87
 
88
+# interval in seconds between database updates
89
+_DATABASE_UPDATE_INTERVAL = 60
87 90
 # chart update interval in seconds
88 91
 _CHART_UPDATE_INTERVAL = 600
89 92
 # standard chart width in pixels
... ...
@@ -97,13 +100,14 @@ _CHART_HEIGHT = 150
97 100
 # turn on or off of verbose debugging information
98 101
 verboseMode = False
99 102
 debugMode = False
103
+reportUpdateFails = False
100 104
 
101 105
 # The following two items are used for detecting system faults
102 106
 # and aredn node online or offline status.
103 107
 
104 108
 # count of failed attempts to get data from aredn node
105 109
 failedUpdateCount = 0
106
-# detected status of aredn node device
110
+httpRetries = 0
107 111
 nodeOnline = False
108 112
 
109 113
 # ip address of aredn node
... ...
@@ -113,6 +117,9 @@ dataRequestInterval = _DEFAULT_DATA_REQUEST_INTERVAL
113 117
 # chart update interval
114 118
 chartUpdateInterval = _CHART_UPDATE_INTERVAL
115 119
 
120
+# rrdtool database interface handler instance
121
+rrdb = None
122
+
116 123
   ###  PRIVATE METHODS  ###
117 124
 
118 125
 def getTimeStamp():
... ...
@@ -124,23 +131,6 @@ def getTimeStamp():
124 131
     return time.strftime( "%m/%d/%Y %T", time.localtime() )
125 132
 ##end def
126 133
 
127
-def getEpochSeconds(sTime):
128
-    """Convert the time stamp supplied in the supplied string
129
-       to seconds since 1/1/1970 00:00:00.
130
-       Parameters: 
131
-           sTime - the time stamp to be converted must be formatted
132
-                   as %m/%d/%Y %H:%M:%S
133
-       Returns: epoch seconds
134
-    """
135
-    try:
136
-        t_sTime = time.strptime(sTime, '%m/%d/%Y %H:%M:%S')
137
-    except Exception as exError:
138
-        print('%s getEpochSeconds: %s' % (getTimeStamp(), exError))
139
-        return None
140
-    tSeconds = int(time.mktime(t_sTime))
141
-    return tSeconds
142
-##end def
143
-
144 134
 def setStatusToOffline():
145 135
     """Set the detected status of the aredn node to
146 136
        "offline" and inform downstream clients by removing input
... ...
@@ -168,8 +158,10 @@ def terminateAgentProcess(signal, frame):
168 158
            signal, frame - dummy parameters
169 159
        Returns: nothing
170 160
     """
161
+    # Inform downstream clients by removing output data file.
162
+    if os.path.exists(_OUTPUT_DATA_FILE):
163
+       os.remove(_OUTPUT_DATA_FILE)
171 164
     print('%s terminating arednsig agent process' % getTimeStamp())
172
-    setStatusToOffline()
173 165
     sys.exit(0)
174 166
 ##end def
175 167
 
... ...
@@ -182,6 +174,8 @@ def getNodeData(dData):
182 174
        Returns: True if successful,
183 175
                 or False if not successful
184 176
     """
177
+    global httpRetries
178
+
185 179
     try:
186 180
         currentTime = time.time()
187 181
         response = urlopen(arednNodeUrl, timeout=_HTTP_REQUEST_TIMEOUT)
... ...
@@ -197,15 +191,29 @@ def getNodeData(dData):
197 191
         # If no response is received from the device, then assume that
198 192
         # the device is down or unavailable over the network.  In
199 193
         # that case return None to the calling function.
200
-        print("%s getNodeData: %s" % (getTimeStamp(), exError))
201
-        return False
202
-    ##end try
194
+        httpRetries += 1
195
+
196
+        if reportUpdateFails:
197
+            print("%s " % getTimeStamp(), end='')
198
+        if reportUpdateFails or verboseMode:
199
+            print("http request failed (%d): %s" % \
200
+                (httpRetries, exError))
201
+
202
+        if httpRetries > _MAX_HTTP_RETRIES:
203
+            httpRetries = 0
204
+            return False
205
+        else:
206
+            time.sleep(_HTTP_RETRY_DELAY)
207
+            return getNodeData(dData)
208
+    ## end try
203 209
 
204 210
     if debugMode:
205 211
         print(content)
206 212
     if verboseMode:
207
-        print("http request successful: %.4f sec" % requestTime)
213
+        print("http request successful: "\
214
+              "%.4f seconds" % requestTime)
208 215
 
216
+    httpRetries = 0
209 217
     dData['content'] = content
210 218
     return True
211 219
 ##end def
... ...
@@ -306,125 +314,21 @@ def setNodeStatus(updateSuccess):
306 314
         # Set status and send a message to the log if the device
307 315
         # previously offline and is now online.
308 316
         if not nodeOnline:
309
-            print('%s node online' % getTimeStamp())
317
+            print('%s aredn node online' % getTimeStamp())
310 318
             nodeOnline = True
311 319
         return
312
-    elif failedUpdateCount == _MAX_FAILED_DATA_REQUESTS - 1:
320
+    else:
321
+        # The last attempt failed, so update the failed attempts
322
+        # count.
323
+        failedUpdateCount += 1
324
+
325
+    if failedUpdateCount == _MAX_FAILED_DATA_REQUESTS:
313 326
         # Max number of failed data requests, so set
314 327
         # device status to offline.
315 328
         setStatusToOffline()
316
-    ## end if
317
-    failedUpdateCount += 1
318 329
 ##end def
319 330
 
320
-    ### DATABASE FUNCTIONS ###
321
-
322
-def updateDatabase(dData):
323
-    """
324
-    Update the rrdtool database by executing an rrdtool system command.
325
-    Format the command using the data extracted from the aredn node
326
-    response.   
327
-    Parameters: dData - dictionary object containing data items to be
328
-                        written to the rr database file
329
-    Returns: True if successful, False otherwise
330
-    """
331
-    
332
-    time = getEpochSeconds(dData['date'])
333
-
334
-    # Format the rrdtool update command.
335
-    strFmt = "rrdtool update %s %s:%s:%s:%s:%s:%s:%s:%s"
336
-    strCmd = strFmt % (_RRD_FILE, time, dData['signal'], \
337
-             dData['noise'], dData['snr'], '0', \
338
-             '0', '0', '0')
339
-
340
-    if debugMode:
341
-        print("%s" % strCmd) # DEBUG
342
-
343
-    # Run the command as a subprocess.
344
-    try:
345
-        subprocess.check_output(strCmd, shell=True,  \
346
-                             stderr=subprocess.STDOUT)
347
-    except subprocess.CalledProcessError as exError:
348
-        print("%s: rrdtool update failed: %s" % \
349
-                    (getTimeStamp(), exError.output))
350
-        return False
351
-
352
-    if verboseMode and not debugMode:
353
-        print("database update successful")
354
-
355
-    return True
356
-##end def
357
-
358
-def createGraph(fileName, dataItem, gLabel, gTitle, gStart,
359
-                lower, upper, addTrend, autoScale):
360
-    """Uses rrdtool to create a graph of specified node data item.
361
-       Parameters:
362
-           fileName - name of file containing the graph
363
-           dataItem - data item to be graphed
364
-           gLabel - string containing a graph label for the data item
365
-           gTitle - string containing a title for the graph
366
-           gStart - beginning time of the graphed data
367
-           lower - lower bound for graph ordinate #NOT USED
368
-           upper - upper bound for graph ordinate #NOT USED
369
-           addTrend - 0, show only graph data
370
-                      1, show only a trend line
371
-                      2, show a trend line and the graph data
372
-           autoScale - if True, then use vertical axis auto scaling
373
-               (lower and upper parameters are ignored), otherwise use
374
-               lower and upper parameters to set vertical axis scale
375
-       Returns: True if successful, False otherwise
376
-    """
377
-    gPath = _CHARTS_DIRECTORY + fileName + ".png"
378
-    trendWindow = { 'end-1day': 7200,
379
-                    'end-4weeks': 172800,
380
-                    'end-12months': 604800 }
381
- 
382
-    # Format the rrdtool graph command.
383
-
384
-    # Set chart start time, height, and width.
385
-    strCmd = "rrdtool graph %s -a PNG -s %s -e now -w %s -h %s " \
386
-             % (gPath, gStart, _CHART_WIDTH, _CHART_HEIGHT)
387
-   
388
-    # Set the range and scaling of the chart y-axis.
389
-    if lower < upper:
390
-        strCmd  +=  "-l %s -u %s -r " % (lower, upper)
391
-    elif autoScale:
392
-        strCmd += "-A "
393
-    strCmd += "-Y "
394
-
395
-    # Set the chart ordinate label and chart title. 
396
-    strCmd += "-v %s -t %s " % (gLabel, gTitle)
397
- 
398
-    # Show the data, or a moving average trend line over
399
-    # the data, or both.
400
-    strCmd += "DEF:dSeries=%s:%s:LAST " % (_RRD_FILE, dataItem)
401
-    if addTrend == 0:
402
-        strCmd += "LINE1:dSeries#0400ff "
403
-    elif addTrend == 1:
404
-        strCmd += "CDEF:smoothed=dSeries,%s,TREND LINE2:smoothed#006600 " \
405
-                  % trendWindow[gStart]
406
-    elif addTrend == 2:
407
-        strCmd += "LINE1:dSeries#0400ff "
408
-        strCmd += "CDEF:smoothed=dSeries,%s,TREND LINE2:smoothed#006600 " \
409
-                  % trendWindow[gStart]
410
-     
411
-    if debugMode:
412
-        print("%s" % strCmd) # DEBUG
413
-    
414
-    # Run the formatted rrdtool command as a subprocess.
415
-    try:
416
-        result = subprocess.check_output(strCmd, \
417
-                     stderr=subprocess.STDOUT,   \
418
-                     shell=True)
419
-    except subprocess.CalledProcessError as exError:
420
-        print("rrdtool graph failed: %s" % (exError.output))
421
-        return False
422
-
423
-    if verboseMode:
424
-        print("rrdtool graph: %s\n" % result.decode('utf-8'), end='')
425
-    return True
426
-
427
-##end def
331
+    ### GRAPH FUNCTIONS ###
428 332
 
429 333
 def generateGraphs():
430 334
     """Generate graphs for display in html documents.
... ...
@@ -443,23 +347,27 @@ def generateGraphs():
443 347
 
444 348
     # 24 hour stock charts
445 349
 
446
-    createGraph('24hr_signal', 'S', 'dBm', 
350
+
351
+    #### REPLACE WITH createRadGraph ####
352
+
353
+
354
+    rrdb.createAutoGraph('24hr_signal', 'S', 'dBm', 
447 355
                 'RSSI\ -\ Last\ 24\ Hours', 'end-1day', 0, 0, 2, autoScale)
448
-    createGraph('24hr_snr', 'SNR', 'dB', 
356
+    rrdb.createAutoGraph('24hr_snr', 'SNR', 'dB', 
449 357
                 'SNR\ -\ Last\ 24\ Hours', 'end-1day', 0, 0, 2, autoScale)
450 358
 
451 359
     # 4 week stock charts
452 360
 
453
-    createGraph('4wk_signal', 'S', 'dBm', 
361
+    rrdb.createAutoGraph('4wk_signal', 'S', 'dBm', 
454 362
                 'RSSI\ -\ Last\ 4\ Weeks', 'end-4weeks', 0, 0, 2, autoScale)
455
-    createGraph('4wk_snr', 'SNR', 'dB', 
363
+    rrdb.createAutoGraph('4wk_snr', 'SNR', 'dB', 
456 364
                 'SNR\ -\ Last\ 4\ Weeks', 'end-4weeks', 0, 0, 2, autoScale)
457 365
 
458 366
     # 12 month stock charts
459 367
 
460
-    createGraph('12m_signal', 'S', 'dBm', 
368
+    rrdb.createAutoGraph('12m_signal', 'S', 'dBm', 
461 369
                 'RSSI\ -\ Past\ Year', 'end-12months', 0, 0, 2, autoScale)
462
-    createGraph('12m_snr', 'SNR', 'dB', 
370
+    rrdb.createAutoGraph('12m_snr', 'SNR', 'dB', 
463 371
                 'SNR\ -\ Past\ Year', 'end-12months', 0, 0, 2, autoScale)
464 372
 
465 373
     if verboseMode:
... ...
@@ -471,12 +379,12 @@ def getCLarguments():
471 379
     """Get command line arguments.  There are four possible arguments
472 380
           -d turns on debug mode
473 381
           -v turns on verbose debug mode
474
-          -t sets the aredn node query interval
382
+          -p sets the aredn node query interval
475 383
           -u sets the url of the aredn nodeing device
476 384
        Returns: nothing
477 385
     """
478 386
     global verboseMode, debugMode, dataRequestInterval, \
479
-           arednNodeUrl
387
+           arednNodeUrl, reportUpdateFails
480 388
 
481 389
     index = 1
482 390
     while index < len(sys.argv):
... ...
@@ -485,9 +393,13 @@ def getCLarguments():
485 393
         elif sys.argv[index] == '-d':
486 394
             verboseMode = True
487 395
             debugMode = True
396
+        elif sys.argv[index] == '-r':
397
+            reportUpdateFails = True
398
+
399
+        # Update period and url options
488 400
         elif sys.argv[index] == '-p':
489 401
             try:
490
-                dataRequestInterval = abs(int(sys.argv[index + 1]))
402
+                dataRequestInterval = abs(float(sys.argv[index + 1]))
491 403
             except:
492 404
                 print("invalid polling period")
493 405
                 exit(-1)
... ...
@@ -499,42 +411,48 @@ def getCLarguments():
499 411
             index += 1
500 412
         else:
501 413
             cmd_name = sys.argv[0].split('/')
502
-            print("Usage: %s [-d] [-v] [-p seconds] [-u url]" % cmd_name[-1])
414
+            print("Usage: %s [-v|d] [-p seconds] [-u url]" % cmd_name[-1])
503 415
             exit(-1)
504 416
         index += 1
505 417
 ##end def
506 418
 
507
-def main():
419
+def setup():
508 420
     """Handles timing of events and acts as executive routine managing
509 421
        all other functions.
510 422
        Parameters: none
511 423
        Returns: nothing
512 424
     """
513
-    global dataRequestInterval
425
+    global rrdb
514 426
 
515
-    signal.signal(signal.SIGTERM, terminateAgentProcess)
516
-    signal.signal(signal.SIGINT, terminateAgentProcess)
427
+    ## Get command line arguments.
428
+    getCLarguments()
517 429
 
518
-    print('===================')
430
+    print('======================================================')
519 431
     print('%s starting up arednsig agent process' % \
520 432
                   (getTimeStamp()))
521 433
 
522
-    # last time output JSON file updated
523
-    lastDataRequestTime = -1
524
-    # last time charts generated
525
-    lastChartUpdateTime = - 1
526
-    # last time the rrdtool database updated
527
-    lastDatabaseUpdateTime = -1
528
-
529
-    ## Get command line arguments.
530
-    getCLarguments()
531
-
532 434
     ## Exit with error if rrdtool database does not exist.
533 435
     if not os.path.exists(_RRD_FILE):
534 436
         print('rrdtool database does not exist\n' \
535 437
               'use createArednsigRrd script to ' \
536 438
               'create rrdtool database\n')
537 439
         exit(1)
440
+
441
+    signal.signal(signal.SIGTERM, terminateAgentProcess)
442
+    signal.signal(signal.SIGINT, terminateAgentProcess)
443
+
444
+    # Define object for calling rrdtool database functions.
445
+    rrdb = rrdbase.rrdbase( _RRD_FILE, _CHARTS_DIRECTORY, _CHART_WIDTH, \
446
+                            _CHART_HEIGHT, verboseMode, debugMode )
447
+## end def
448
+
449
+def loop():
450
+    # last time output JSON file updated
451
+    lastDataRequestTime = -1
452
+    # last time charts generated
453
+    lastChartUpdateTime = -1
454
+    # last time the rrdtool database updated
455
+    lastDatabaseUpdateTime = -1
538 456
  
539 457
     ## main loop
540 458
     while True:
... ...
@@ -558,9 +476,15 @@ def main():
558 476
             if result:
559 477
                 writeOutputFile(dData)
560 478
 
561
-            # If write output file successful, update the database.
562
-            if result:
563
-                result = updateDatabase(dData)
479
+            # At the rrdtool database update interval, update the database.
480
+            if result and (currentTime - lastDatabaseUpdateTime > \
481
+                           _DATABASE_UPDATE_INTERVAL):   
482
+                lastDatabaseUpdateTime = currentTime
483
+                # If write output file successful, update the database.
484
+                if result:
485
+                    result = rrdb.updateDatabase(dData['date'], \
486
+                             dData['signal'], dData['noise'], dData['snr'], \
487
+                            '0', '0', '0', '0')
564 488
 
565 489
             # Set the node status to online or offline depending on the
566 490
             # success or failure of the above operations.
... ...
@@ -571,7 +495,7 @@ def main():
571 495
         if currentTime - lastChartUpdateTime > chartUpdateInterval:
572 496
             lastChartUpdateTime = currentTime
573 497
             p = multiprocessing.Process(target=generateGraphs, args=())
574
-            #p.start()
498
+            p.start()
575 499
 
576 500
         # Relinquish processing back to the operating system until
577 501
         # the next update interval.
... ...
@@ -586,9 +510,9 @@ def main():
586 510
         if remainingTime > 0.0:
587 511
             time.sleep(remainingTime)
588 512
     ## end while
589
-    return
590 513
 ## end def
591 514
 
592 515
 if __name__ == '__main__':
593
-    main()
516
+    setup()
517
+    loop()
594 518
 
595 519
old mode 100755
596 520
new mode 100644
597 521
old mode 100755
598 522
new mode 100644
... ...
@@ -176,11 +176,12 @@ function createChart($chartFile, $dataItem, $label, $title, $begin,
176 176
     if ($addTrend == 0) {
177 177
         $cmd .= "LINE1:dSeries#0400ff ";
178 178
     } elseif ($addTrend == 1) {
179
-        $cmdfmt = "CDEF:smoothed=dSeries,%s,TREND LINE2:smoothed#006600 ";
179
+        $cmdfmt = "CDEF:smoothed=dSeries,%s,TREND LINE3:smoothed#ff0000 ";
180 180
         $cmd .= sprintf($cmdfmt, $trendWindow);
181 181
     } elseif ($addTrend == 2) {
182 182
         $cmd .= "LINE1:dSeries#0400ff ";
183
-        $cmdfmt = "CDEF:smoothed=dSeries,%s,TREND LINE2:smoothed#006600 ";
183
+        $cmdfmt = "CDEF:smoothed=dSeries,%s,TREND LINE3:smoothed#ff0000 ";
184
+        #$cmdfmt = "CDEF:smoothed=dSeries,%s,XYZZY LINE3:smoothed#ff0000 ";
184 185
         $cmd .=  sprintf($cmdfmt, $trendWindow);
185 186
     }
186 187
      
... ...
@@ -1,6 +1,7 @@
1 1
 <!DOCTYPE html>
2 2
 <html>
3 3
 <head>
4
+  <meta charset="UTF-8">
4 5
   <meta http-equiv="refresh" content="0; url=./arednsig.html">
5 6
 </head>
6 7
 </html>