Browse code

minor revision

Gandolf authored on 07/03/2021 01:42:15
Showing 3 changed files
... ...
@@ -57,16 +57,24 @@ class ina260:
57 57
     # register.
58 58
     def __init__(self, sAddr=DEFAULT_BUS_ADDRESS,
59 59
                        sbus=DEFAULT_BUS_NUMBER,
60
-                       config=DEFAULT_CONFIG):
60
+                       config=DEFAULT_CONFIG,
61
+                       debug=False):
61 62
         # Instantiate a smbus object.
62 63
         self.sensorAddr = sAddr
63 64
         self.bus = smbus.SMBus(sbus)
65
+        self.debugMode = debug
66
+
64 67
         # Initialize INA260 sensor.  
65 68
         initData = [(config >> 8), (config & 0x00FF)]
66 69
         self.bus.write_i2c_block_data(self.sensorAddr, CONFIG_REG, initData)
70
+
71
+        if self.debugMode:
72
+            data = self.getInfo()
73
+            print("manufacturer ID: %s %s\n"\
74
+                  "configuration register: %s %s\n" % data)
67 75
     ## end def
68 76
 
69
-    def status(self):
77
+    def getInfo(self):
70 78
         # Read manufacture identification data.
71 79
         mfcid = self.bus.read_i2c_block_data(self.sensorAddr, ID_REG, 2)
72 80
         mfcidB1 = format(mfcid[0], "08b")
... ...
@@ -91,7 +99,7 @@ class ina260:
91 99
         # Get the current data from the sensor.
92 100
         # INA260 returns the data in two bytes formatted as follows
93 101
         #        -------------------------------------------------
94
-        #    bit | b7  | b6  | b5  | b4  | b3  | b2  | b1  | b0  |
102
+        #    bit |  7  |  6  |  5  |  4  |  3  |  2  |  1  |  0  |
95 103
         #        -------------------------------------------------
96 104
         # byte 1 | d15 | d14 | d13 | d12 | d11 | d10 | d9  | d8  |
97 105
         #        -------------------------------------------------
... ...
@@ -100,6 +108,12 @@ class ina260:
100 108
         # The current is returned in d15-d0, a two's complement,
101 109
         # 16 bit number.  This means that d15 is the sign bit.        
102 110
         data=self.bus.read_i2c_block_data(self.sensorAddr, CUR_REG, 2)
111
+
112
+        if self.debugMode:
113
+            dataB1 = format(data[0], "08b")
114
+            dataB2 = format(data[1], "08b")
115
+            print("current register: %s %s" % (dataB1, dataB2))
116
+
103 117
         # Format into a 16 bit word.
104 118
         bdata = data[0] << 8 | data[1]
105 119
         # Convert from two's complement to integer.
... ...
@@ -126,7 +140,7 @@ class ina260:
126 140
         # Get the voltage data from the sensor.
127 141
         # INA260 returns the data in two bytes formatted as follows
128 142
         #        -------------------------------------------------
129
-        #    bit | b7  | b6  | b5  | b4  | b3  | b2  | b1  | b0  |
143
+        #    bit |  7  |  6  |  5  |  4  |  3  |  2  |  1  |  0  |
130 144
         #        -------------------------------------------------
131 145
         # byte 1 | d15 | d14 | d13 | d12 | d11 | d10 | d9  | d8  |
132 146
         #        -------------------------------------------------
... ...
@@ -134,6 +148,12 @@ class ina260:
134 148
         #        -------------------------------------------------
135 149
         # The voltage is returned in d15-d0 as an unsigned integer.
136 150
         data=self.bus.read_i2c_block_data(self.sensorAddr, VOLT_REG, 2)
151
+
152
+        if self.debugMode:
153
+            dataB1 = format(data[0], "08b")
154
+            dataB2 = format(data[1], "08b")
155
+            print("voltage register: %s %s" % (dataB1, dataB2))
156
+
137 157
         # Convert data to volts.
138 158
         volts = (data[0] << 8 | data[1]) * 0.00125 # LSB is 1.25 mV
139 159
         return volts
... ...
@@ -143,7 +163,7 @@ class ina260:
143 163
         # Get the wattage data from the sensor.
144 164
         # INA260 returns the data in two bytes formatted as follows
145 165
         #        -------------------------------------------------
146
-        #    bit | b7  | b6  | b5  | b4  | b3  | b2  | b1  | b0  |
166
+        #    bit | 7   |  6  |  5  |  4  |  3  |  2  |  1  |  0  |
147 167
         #        -------------------------------------------------
148 168
         # byte 1 | d15 | d14 | d13 | d12 | d11 | d10 | d9  | d8  |
149 169
         #        -------------------------------------------------
... ...
@@ -151,6 +171,12 @@ class ina260:
151 171
         #        -------------------------------------------------
152 172
         # The wattage is returned in d15-d0 as an unsigned integer.
153 173
         data=self.bus.read_i2c_block_data(self.sensorAddr, PWR_REG, 2)
174
+
175
+        if self.debugMode:
176
+            dataB1 = format(data[0], "08b")
177
+            dataB2 = format(data[1], "08b")
178
+            print("power register: %s %s" % (dataB1, dataB2))
179
+
154 180
         # Convert data to milliWatts. 
155 181
         mW = (data[0] << 8 | data[1]) * 10.0  # LSB is 10.0 mW
156 182
         return mW
... ...
@@ -159,15 +185,10 @@ class ina260:
159 185
 
160 186
 def test():
161 187
     # Initialize the smbus and INA260 sensor.
162
-    pwr1 = ina260(0x40, 1)
163
-    # Read the INA260 configuration register and manufacturer's ID.
164
-    data = pwr1.status()
165
-    print("manufacturer ID: %s %s\nconfiguration register: %s %s\n" % data)
188
+    pwr1 = ina260(0x40, 1, debug=True)
166 189
     # Print out sensor values.
167 190
     while True:
168
-        print("current register: %s %s" % pwr1.getCurrentReg())
169 191
         print("%6.2f mA" % pwr1.getCurrent())
170
-        print("volt register: %s %s" % pwr1.getVoltageReg())
171 192
         print("%6.2f V" % pwr1.getVoltage())
172 193
         print("%6.2f mW\n" % pwr1.getPower())
173 194
         time.sleep(2)
... ...
@@ -29,6 +29,8 @@
29 29
 #
30 30
 # Revision History
31 31
 #   * v10 released 01 June 2021 by J L Owrey; first release
32
+#   * v11 released 02 July 2021 by J L Owrey; improved sensor fault
33
+#     handling; improved code readability
32 34
 #
33 35
 #2345678901234567890123456789012345678901234567890123456789012345678901234567890
34 36
 
... ...
@@ -55,7 +57,7 @@ _PWR_SENSOR_ADDR = 0X40
55 57
 _BAT_TMP_SENSOR_ADDR = 0x48
56 58
 _AMB_TMP_SENSOR_ADDR = 0x4B
57 59
 # Set bus selector.
58
-_BUS_SEL = 1
60
+_BUS_NUMBER = 1
59 61
 
60 62
     ### FILE AND FOLDER LOCATIONS ###
61 63
 
... ...
@@ -71,9 +73,12 @@ _RRD_FILE = "/home/%s/database/powerData.rrd" % _USER
71 73
     ### GLOBAL CONSTANTS ###
72 74
 
73 75
 # sensor data request interval in seconds
74
-_DEFAULT_DATA_REQUEST_INTERVAL = 2
76
+_DEFAULT_SENSOR_POLLING_INTERVAL = 2
75 77
 # rrdtool database update interval in seconds
76 78
 _DATABASE_UPDATE_INTERVAL = 30
79
+# max number of failed attempts to get sensor data
80
+_MAX_FAILED_DATA_REQUESTS = 2
81
+
77 82
 # chart update interval in seconds
78 83
 _CHART_UPDATE_INTERVAL = 600
79 84
 # standard chart width in pixels
... ...
@@ -85,19 +90,23 @@ _AVERAGE_LINE_COLOR = '#006600'
85 90
 
86 91
    ### GLOBAL VARIABLES ###
87 92
 
88
-# debug output options
93
+# turns on or off extensive debugging messages
89 94
 debugMode = False
90 95
 verboseMode = False
91 96
 
92 97
 # frequency of data requests to sensors
93
-dataRequestInterval = _DEFAULT_DATA_REQUEST_INTERVAL
98
+dataRequestInterval = _DEFAULT_SENSOR_POLLING_INTERVAL
94 99
 # how often charts get updated
95 100
 chartUpdateInterval = _CHART_UPDATE_INTERVAL
101
+# number of failed attempts to get sensor data
102
+failedUpdateCount = 0
103
+# sensor status
104
+deviceOnline = False
96 105
 
97
-# Define each sensor.  This also initialzes each sensor.
98
-pwr = ina260.ina260(_PWR_SENSOR_ADDR, _BUS_SEL)
99
-btmp = tmp102.tmp102(_BAT_TMP_SENSOR_ADDR, _BUS_SEL)
100
-atmp = tmp102.tmp102(_AMB_TMP_SENSOR_ADDR, _BUS_SEL)
106
+# Create sensor objects.  This also initialzes each sensor.
107
+power = ina260.ina260(_PWR_SENSOR_ADDR, _BUS_NUMBER)
108
+battemp = tmp102.tmp102(_BAT_TMP_SENSOR_ADDR, _BUS_NUMBER)
109
+ambtemp = tmp102.tmp102(_AMB_TMP_SENSOR_ADDR, _BUS_NUMBER)
101 110
 
102 111
   ###  PRIVATE METHODS  ###
103 112
 
... ...
@@ -127,21 +136,37 @@ def getEpochSeconds(sTime):
127 136
     return tSeconds
128 137
 ## end def
129 138
 
130
-def terminateAgentProcess(signal, frame):
131
-    """
132
-    Send a message to the log when the agent process gets killed
133
-    by the operating system.  Inform downstream clients
134
-    by removing output data files.
135
-    Parameters:
136
-        signal, frame - dummy parameters
137
-    Returns: nothing
139
+def setStatusToOffline():
140
+    """Set the detected status of the device to
141
+       "offline" and inform downstream clients by removing input
142
+       and output data files.
143
+       Parameters: none
144
+       Returns: nothing
138 145
     """
146
+    global deviceOnline
147
+
139 148
     # Inform downstream clients by removing output data file.
140 149
     if os.path.exists(_OUTPUT_DATA_FILE):
141 150
        os.remove(_OUTPUT_DATA_FILE)
142
-    print('%s terminating npw agent process' % getTimeStamp())
151
+    # If the sensor or  device was previously online, then send
152
+    # a message that we are now offline.
153
+    if deviceOnline:
154
+        print('%s device offline' % getTimeStamp())
155
+    deviceOnline = False
156
+##end def
157
+
158
+def terminateAgentProcess(signal, frame):
159
+    """Send a message to log when the agent process gets killed
160
+       by the operating system.  Inform downstream clients
161
+       by removing input and output data files.
162
+       Parameters:
163
+           signal, frame - dummy parameters
164
+       Returns: nothing
165
+    """
166
+    print('%s terminating agent process' % getTimeStamp())
167
+    setStatusToOffline()
143 168
     sys.exit(0)
144
-## end def
169
+##end def
145 170
 
146 171
   ###  PUBLIC METHODS  ###
147 172
 
... ...
@@ -156,11 +181,11 @@ def getSensorData(dData):
156 181
     dData["time"] = getTimeStamp()
157 182
  
158 183
     try:
159
-        dData["current"] = pwr.getCurrent()
160
-        dData["voltage"] = pwr.getVoltage()
161
-        dData["power"] = pwr.getPower()
162
-        dData["battemp"] = btmp.getTempF()
163
-        dData["ambtemp"] = atmp.getTempF()
184
+        dData["current"] = power.getCurrent()
185
+        dData["voltage"] = power.getVoltage()
186
+        dData["power"] = power.getPower()
187
+        dData["battemp"] = battemp.getTempF()
188
+        dData["ambtemp"] = ambtemp.getTempF()
164 189
     except Exception as exError:
165 190
         print("%s sensor error: %s" % (getTimeStamp(), exError))
166 191
         return False
... ...
@@ -212,6 +237,35 @@ def writeOutputFile(dData):
212 237
     return True
213 238
 ## end def
214 239
 
240
+def setStatus(updateSuccess):
241
+    """Detect if device is offline or not available on
242
+       the network. After a set number of attempts to get data
243
+       from the device set a flag that the device is offline.
244
+       Parameters:
245
+           updateSuccess - a boolean that is True if data request
246
+                           successful, False otherwise
247
+       Returns: nothing
248
+    """
249
+    global failedUpdateCount, deviceOnline
250
+
251
+    if updateSuccess:
252
+        failedUpdateCount = 0
253
+        # Set status and send a message to the log if the device
254
+        # previously offline and is now online.
255
+        if not deviceOnline:
256
+            print('%s device online' % getTimeStamp())
257
+            deviceOnline = True
258
+    else:
259
+        # The last attempt failed, so update the failed attempts
260
+        # count.
261
+        failedUpdateCount += 1
262
+
263
+    if failedUpdateCount >= _MAX_FAILED_DATA_REQUESTS:
264
+        # Max number of failed data requests, so set
265
+        # device status to offline.
266
+        setStatusToOffline()
267
+##end def
268
+
215 269
 def updateDatabase(dData):
216 270
     """
217 271
     Update the rrdtool database by executing an rrdtool system command.
... ...
@@ -439,12 +493,13 @@ def main():
439 493
     Parameters: none
440 494
     Returns: nothing
441 495
     """
442
-    global dataRequestInterval
443
-
444 496
     signal.signal(signal.SIGTERM, terminateAgentProcess)
445 497
     signal.signal(signal.SIGINT, terminateAgentProcess)
446 498
 
447
-    print('%s starting up node power agent process' % getTimeStamp())
499
+    # Log agent process startup time.
500
+    print '===================\n'\
501
+          '%s starting up node power agent process' % \
502
+                  (getTimeStamp())
448 503
 
449 504
     # last time output JSON file updated
450 505
     lastDataRequestTime = -1
... ...
@@ -488,6 +543,8 @@ def main():
488 543
                 ## Update the round robin database with the parsed data.
489 544
                 result = updateDatabase(dData)
490 545
 
546
+            setStatus(result)
547
+
491 548
         # At the chart generation interval, generate charts.
492 549
         if currentTime - lastChartUpdateTime > chartUpdateInterval:
493 550
             lastChartUpdateTime = currentTime
... ...
@@ -57,17 +57,25 @@ class tmp102:
57 57
     # register.
58 58
     def __init__(self, sAddr=DEFAULT_BUS_ADDRESS,
59 59
                  sbus=DEFAULT_BUS_NUMBER,
60
-                 config=DEFAULT_CONFIG): 
60
+                 config=DEFAULT_CONFIG,
61
+                 debug=False): 
61 62
         # Instantiate a smbus object
62 63
         self.sensorAddr = sAddr
63 64
         self.bus = smbus.SMBus(sbus)
65
+        self.debugMode = debug
66
+ 
64 67
         # Initialize TMP102 sensor.  
65 68
         initData = [(config >> 8), (config & 0x00FF)]
66 69
         self.bus.write_i2c_block_data(self.sensorAddr, CONFIG_REG, initData)
70
+
71
+        if self.debugMode:
72
+            # Read the TMP102 configuration register.
73
+            data = self.getInfo()
74
+            print("configuration register: %s %s\n" % data)
67 75
     ## end def
68 76
 
69 77
     # Reads the configuration register (two bytes).
70
-    def status(self):
78
+    def getInfo(self):
71 79
         # Read configuration data
72 80
         config = self.bus.read_i2c_block_data(self.sensorAddr, CONFIG_REG, 2)
73 81
         configB1 = format(config[0], "08b")
... ...
@@ -99,6 +107,12 @@ class tmp102:
99 107
         # The temperature is returned in d11-d0, a two's complement,
100 108
         # 12 bit number.  This means that d11 is the sign bit.
101 109
         data=self.bus.read_i2c_block_data(self.sensorAddr, TEMP_REG, 2)
110
+
111
+        if self.debugMode:
112
+            dataB1 = format(data[0], "08b")
113
+            dataB2 = format(data[1], "08b")
114
+            print("Temperature Reg: %s %s" % (dataB1, dataB2))
115
+
102 116
         # Format into a 12 bit word.
103 117
         bData = ( data[0] << 8 | data[1] ) >> 4
104 118
         # Convert from two's complement to integer.
... ...
@@ -121,24 +135,18 @@ class tmp102:
121 135
 
122 136
 def testclass():
123 137
     # Initialize the smbus and TMP102 sensor.
124
-    ts1 = tmp102(0x48, 1)
125
-    # Read the TMP102 configuration register.
126
-    data = ts1.status()
127
-    print "configuration register: %s %s\n" % data
138
+    ts1 = tmp102(0x48, 1, debug=True)
128 139
     # Print out sensor values.
129 140
     bAl = False
130 141
     while True:
131
-        regdata = ts1.getTempReg()
132 142
         tempC = ts1.getTempC()
133 143
         tempF = ts1.getTempF()
134 144
         if bAl:
135 145
             bAl = False
136
-            print("\033[42;30mTemperature Reg: %s %s\033[m" % regdata)
137
-            print("\033[42;30m%6.2f%sC  %6.2f%s                 \033[m" % \
146
+            print("\033[42;30m%6.2f%sC  %6.2f%sF                 \033[m" % \
138 147
                   (tempC, DEGSYM, tempF, DEGSYM))
139 148
         else:
140 149
             bAl = True
141
-            print("Temperature Reg: %s %s" % regdata)
142 150
             print("%6.2f%sC  %6.2f%sF" % \
143 151
                   (tempC, DEGSYM, tempF, DEGSYM))
144 152
         time.sleep(2)