... | ... |
@@ -7,7 +7,7 @@ |
7 | 7 |
# Description: Creates a rrdtool database for use by the power agent to |
8 | 8 |
# store the data from the power and temperature sensors. The agent uses |
9 | 9 |
# the data in the database to generate graphic charts for display in the |
10 |
-# weather station web page. |
|
10 |
+# node power web page. |
|
11 | 11 |
# |
12 | 12 |
# Copyright 2021 Jeff Owrey |
13 | 13 |
# This program is free software: you can redistribute it and/or modify |
... | ... |
@@ -38,32 +38,40 @@ CUR_REG = 0x1 |
38 | 38 |
VOLT_REG = 0x2 |
39 | 39 |
PWR_REG = 0x3 |
40 | 40 |
|
41 |
+# Define default sm bus address. |
|
42 |
+DEFAULT_BUS_ADDRESS = 0x40 |
|
43 |
+DEFAULT_BUS_NUMBER = 1 |
|
44 |
+ |
|
45 |
+# Define the default sensor configuration. See the INA260 data sheet |
|
46 |
+# for meaning of each bit. The following bytes are written to the |
|
47 |
+# configuration register |
|
48 |
+# byte 1: 11100000 |
|
49 |
+# byte 2: 00100111 |
|
50 |
+DEFAULT_CONFIG = 0xE027 |
|
51 |
+ |
|
41 | 52 |
class ina260: |
42 | 53 |
# Initialize the INA260 sensor at the supplied address (default |
43 | 54 |
# address is 0x40), and supplied bus (default is 1). Creates |
44 | 55 |
# a new SMBus object for each instance of this class. Writes |
45 | 56 |
# configuration data (two bytes) to the INA260 configuration |
46 | 57 |
# register. |
47 |
- def __init__(self, sAddr=0x40, sbus=1): |
|
48 |
- # Instantiate a smbus object |
|
58 |
+ def __init__(self, sAddr=DEFAULT_BUS_ADDRESS, |
|
59 |
+ sbus=DEFAULT_BUS_NUMBER, |
|
60 |
+ config=DEFAULT_CONFIG): |
|
61 |
+ # Instantiate a smbus object. |
|
49 | 62 |
self.sensorAddr = sAddr |
50 | 63 |
self.bus = smbus.SMBus(sbus) |
51 |
- # Initialize INA260 sensor. See the data sheet for meaning of |
|
52 |
- # each bit. The following bytes are written to the configuration |
|
53 |
- # register |
|
54 |
- # byte 1: 11100000 |
|
55 |
- # byte 2: 00100111 |
|
56 |
- initData = [0b11100000, 0b00100111] |
|
57 |
- #initData = [0x60, 0x27] |
|
64 |
+ # Initialize INA260 sensor. |
|
65 |
+ initData = [(config >> 8), (config & 0x00FF)] |
|
58 | 66 |
self.bus.write_i2c_block_data(self.sensorAddr, CONFIG_REG, initData) |
59 | 67 |
## end def |
60 | 68 |
|
61 | 69 |
def status(self): |
62 |
- # Read configuration data |
|
70 |
+ # Read manufacture identification data. |
|
63 | 71 |
mfcid = self.bus.read_i2c_block_data(self.sensorAddr, ID_REG, 2) |
64 | 72 |
mfcidB1 = format(mfcid[0], "08b") |
65 | 73 |
mfcidB2 = format(mfcid[1], "08b") |
66 |
- # Read configuration data |
|
74 |
+ # Read configuration data. |
|
67 | 75 |
config = self.bus.read_i2c_block_data(self.sensorAddr, CONFIG_REG, 2) |
68 | 76 |
configB1 = format(config[0], "08b") |
69 | 77 |
configB2 = format(config[1], "08b") |
... | ... |
@@ -97,9 +105,9 @@ class ina260: |
97 | 105 |
# Convert from two's complement to integer. |
98 | 106 |
# If d15 is 1, the the number is a negative two's complement |
99 | 107 |
# number. The absolute value is 2^16 - 1 minus the value |
100 |
- # of d14-d0 taken as a positive number. |
|
108 |
+ # of d15-d0 taken as a positive number. |
|
101 | 109 |
if bdata > 0x7FFF: |
102 |
- bdata = -(0xFFFF - bdata) # 0xFFFF is 2^16 - 1 |
|
110 |
+ bdata = -(0xFFFF - bdata) # 0xFFFF equals 2^16 - 1 |
|
103 | 111 |
# Convert integer data to mAmps. |
104 | 112 |
mAmps = bdata * 1.25 # LSB is 1.25 mA |
105 | 113 |
return mAmps |
... | ... |
@@ -154,14 +162,14 @@ def test(): |
154 | 162 |
pwr1 = ina260(0x40, 1) |
155 | 163 |
# Read the INA260 configuration register and manufacturer's ID. |
156 | 164 |
data = pwr1.status() |
157 |
- print "manufacturer ID: %s %s\nconfiguration register: %s %s\n" % data |
|
165 |
+ print("manufacturer ID: %s %s\nconfiguration register: %s %s\n" % data) |
|
158 | 166 |
# Print out sensor values. |
159 | 167 |
while True: |
160 |
- print "current register: %s %s" % pwr1.getCurrentReg() |
|
161 |
- print "%6.2f mA" % pwr1.getCurrent() |
|
162 |
- print "volt register: %s %s" % pwr1.getVoltageReg() |
|
163 |
- print "%6.2f V" % pwr1.getVoltage() |
|
164 |
- print "%6.2f mW\n" % pwr1.getPower() |
|
168 |
+ print("current register: %s %s" % pwr1.getCurrentReg()) |
|
169 |
+ print("%6.2f mA" % pwr1.getCurrent()) |
|
170 |
+ print("volt register: %s %s" % pwr1.getVoltageReg()) |
|
171 |
+ print("%6.2f V" % pwr1.getVoltage()) |
|
172 |
+ print("%6.2f mW\n" % pwr1.getPower()) |
|
165 | 173 |
time.sleep(2) |
166 | 174 |
## end def |
167 | 175 |
|
... | ... |
@@ -39,11 +39,15 @@ import signal |
39 | 39 |
import subprocess |
40 | 40 |
import multiprocessing |
41 | 41 |
import time |
42 |
+import json |
|
42 | 43 |
|
43 | 44 |
# Import sensor libraries. |
44 | 45 |
import ina260 # power sensor |
45 | 46 |
import tmp102 # temperature sensor |
46 | 47 |
|
48 |
+ ### ENVIRONMENT ### |
|
49 |
+_USER = os.environ['USER'] |
|
50 |
+ |
|
47 | 51 |
### SENSOR BUS ADDRESSES ### |
48 | 52 |
|
49 | 53 |
# Set bus addresses of sensors. |
... | ... |
@@ -55,8 +59,7 @@ _BUS_SEL = 1 |
55 | 59 |
|
56 | 60 |
### FILE AND FOLDER LOCATIONS ### |
57 | 61 |
|
58 |
-_USER = os.environ['USER'] |
|
59 |
-# folder to contain dynamic data objects |
|
62 |
+# folder to contain html |
|
60 | 63 |
_DOCROOT_PATH = "/home/%s/public_html/power/" % _USER |
61 | 64 |
# folder to contain charts and output data file |
62 | 65 |
_CHARTS_DIRECTORY = _DOCROOT_PATH + "dynamic/" |
... | ... |
@@ -67,10 +70,10 @@ _RRD_FILE = "/home/%s/database/powerData.rrd" % _USER |
67 | 70 |
|
68 | 71 |
### GLOBAL CONSTANTS ### |
69 | 72 |
|
70 |
-# rrdtool database update interval in seconds |
|
71 |
-_DATABASE_UPDATE_INTERVAL = 30 |
|
72 | 73 |
# sensor data request interval in seconds |
73 | 74 |
_DEFAULT_DATA_REQUEST_INTERVAL = 2 |
75 |
+# rrdtool database update interval in seconds |
|
76 |
+_DATABASE_UPDATE_INTERVAL = 30 |
|
74 | 77 |
# chart update interval in seconds |
75 | 78 |
_CHART_UPDATE_INTERVAL = 600 |
76 | 79 |
# standard chart width in pixels |
... | ... |
@@ -85,6 +88,7 @@ _AVERAGE_LINE_COLOR = '#006600' |
85 | 88 |
# debug output options |
86 | 89 |
debugOption = False |
87 | 90 |
verboseDebug = False |
91 |
+ |
|
88 | 92 |
# frequency of data requests to sensors |
89 | 93 |
dataRequestInterval = _DEFAULT_DATA_REQUEST_INTERVAL |
90 | 94 |
# how often charts get updated |
... | ... |
@@ -101,7 +105,7 @@ atmp = tmp102.tmp102(_AMB_TMP_SENSOR_ADDR, _BUS_SEL) |
101 | 105 |
|
102 | 106 |
def getTimeStamp(): |
103 | 107 |
""" |
104 |
- Set the error message time stamp to the local system time. |
|
108 |
+ Get the local time and format as a text string. |
|
105 | 109 |
Parameters: none |
106 | 110 |
Returns: string containing the time stamp |
107 | 111 |
""" |
... | ... |
@@ -109,57 +113,58 @@ def getTimeStamp(): |
109 | 113 |
## end def |
110 | 114 |
|
111 | 115 |
def getEpochSeconds(sTime): |
112 |
- """Convert the time stamp supplied in the weather data string |
|
113 |
- to seconds since 1/1/1970 00:00:00. |
|
114 |
- Parameters: |
|
115 |
- sTime - the time stamp to be converted must be formatted |
|
116 |
+ """ |
|
117 |
+ Convert the time stamp to seconds since 1/1/1970 00:00:00. |
|
118 |
+ Parameters: |
|
119 |
+ sTime - the time stamp to be converted must be formatted |
|
116 | 120 |
as %m/%d/%Y %H:%M:%S |
117 |
- Returns: epoch seconds |
|
121 |
+ Returns: epoch seconds |
|
118 | 122 |
""" |
119 | 123 |
try: |
120 | 124 |
t_sTime = time.strptime(sTime, '%m/%d/%Y %H:%M:%S') |
121 |
- except Exception, exError: |
|
122 |
- print '%s getEpochSeconds: %s' % (getTimeStamp(), exError) |
|
125 |
+ except Exception as exError: |
|
126 |
+ print('%s getEpochSeconds: %s' % (getTimeStamp(), exError)) |
|
123 | 127 |
return None |
124 | 128 |
tSeconds = int(time.mktime(t_sTime)) |
125 | 129 |
return tSeconds |
126 | 130 |
## end def |
127 | 131 |
|
128 | 132 |
def terminateAgentProcess(signal, frame): |
129 |
- """Send a message to the log when the agent process gets killed |
|
130 |
- by the operating system. Inform downstream clients |
|
131 |
- by removing output data files. |
|
132 |
- Parameters: |
|
133 |
- signal, frame - dummy parameters |
|
134 |
- Returns: nothing |
|
133 |
+ """ |
|
134 |
+ Send a message to the log when the agent process gets killed |
|
135 |
+ by the operating system. Inform downstream clients |
|
136 |
+ by removing output data files. |
|
137 |
+ Parameters: |
|
138 |
+ signal, frame - dummy parameters |
|
139 |
+ Returns: nothing |
|
135 | 140 |
""" |
136 | 141 |
# Inform downstream clients by removing output data file. |
137 | 142 |
if os.path.exists(_OUTPUT_DATA_FILE): |
138 | 143 |
os.remove(_OUTPUT_DATA_FILE) |
139 |
- print '%s terminating npw agent process' % \ |
|
140 |
- (getTimeStamp()) |
|
144 |
+ print('%s terminating npw agent process' % getTimeStamp()) |
|
141 | 145 |
sys.exit(0) |
142 | 146 |
## end def |
143 | 147 |
|
144 | 148 |
### PUBLIC METHODS ### |
145 | 149 |
|
146 | 150 |
def getSensorData(dData): |
147 |
- """Poll sensors for data. Store the data in a dictionary object for |
|
148 |
- use by other subroutines. The dictionary object passed in should |
|
149 |
- an empty dictionary, i.e., dData = { }. |
|
150 |
- Parameters: dData - a dictionary object to contain the sensor data |
|
151 |
- Returns: True if successful, False otherwise |
|
152 | 151 |
""" |
152 |
+ Poll sensors for data. Store the data in a dictionary object for |
|
153 |
+ use by other subroutines. The dictionary object passed in should |
|
154 |
+ an empty dictionary, i.e., dData = { }. |
|
155 |
+ Parameters: dData - a dictionary object to contain the sensor data |
|
156 |
+ Returns: True if successful, False otherwise |
|
157 |
+ """ |
|
158 |
+ dData["time"] = getTimeStamp() |
|
159 |
+ |
|
153 | 160 |
try: |
154 |
- dData["time"] = getTimeStamp() |
|
155 | 161 |
dData["current"] = pwr.getCurrent() |
156 | 162 |
dData["voltage"] = pwr.getVoltage() |
157 | 163 |
dData["power"] = pwr.getPower() |
158 | 164 |
dData["battemp"] = btmp.getTempF() |
159 | 165 |
dData["ambtemp"] = atmp.getTempF() |
160 |
- |
|
161 |
- except Exception, exError: |
|
162 |
- print "%s sensor error: %s" % (getTimeStamp(), exError) |
|
166 |
+ except Exception as exError: |
|
167 |
+ print("%s sensor error: %s" % (getTimeStamp(), exError)) |
|
163 | 168 |
return False |
164 | 169 |
|
165 | 170 |
return True |
... | ... |
@@ -182,29 +187,33 @@ def updateDatabase(dData): |
182 | 187 |
dData['voltage'], dData['power'], dData['battemp'], \ |
183 | 188 |
dData['ambtemp']) |
184 | 189 |
|
185 |
- if debugOption: |
|
186 |
- print "%s" % strCmd # DEBUG |
|
190 |
+ if verboseDebug: |
|
191 |
+ print("%s" % strCmd) # DEBUG |
|
187 | 192 |
|
188 | 193 |
# Run the command as a subprocess. |
189 | 194 |
try: |
190 |
- subprocess.check_output(strCmd, shell=True, \ |
|
191 |
- stderr=subprocess.STDOUT) |
|
192 |
- except subprocess.CalledProcessError, exError: |
|
193 |
- print "%s: rrdtool update failed: %s" % \ |
|
194 |
- (getTimeStamp(), exError.output) |
|
195 |
+ subprocess.check_output(strCmd, shell=True, \ |
|
196 |
+ stderr=subprocess.STDOUT) |
|
197 |
+ except subprocess.CalledProcessError as exError: |
|
198 |
+ print("%s: rrdtool update failed: %s" % \ |
|
199 |
+ (getTimeStamp(), exError.output)) |
|
195 | 200 |
return False |
196 | 201 |
|
202 |
+ if debugOption and not verboseDebug: |
|
203 |
+ print("database updated") |
|
204 |
+ |
|
197 | 205 |
return True |
198 | 206 |
## end def |
199 | 207 |
|
200 |
-def writeOutputDataFile(dData): |
|
201 |
- """Write node data items to the output data file, formatted as |
|
202 |
- a Javascript file. This file may then be requested and used by |
|
203 |
- by downstream clients, for instance, an HTML document. |
|
204 |
- Parameters: |
|
205 |
- dData - a dictionary containing the data to be written |
|
208 |
+def writeOutputFile(dData): |
|
209 |
+ """ |
|
210 |
+ Write sensor data items to the output data file, formatted as |
|
211 |
+ a Javascript file. This file may then be requested and used by |
|
212 |
+ by downstream clients, for instance, an HTML document. |
|
213 |
+ Parameters: |
|
214 |
+ dData - a dictionary containing the data to be written |
|
206 | 215 |
to the output data file |
207 |
- Returns: True if successful, False otherwise |
|
216 |
+ Returns: True if successful, False otherwise |
|
208 | 217 |
""" |
209 | 218 |
# Write a JSON formatted file for use by html clients. The following |
210 | 219 |
# data items are sent to the client file. |
... | ... |
@@ -213,54 +222,58 @@ def writeOutputDataFile(dData): |
213 | 222 |
# * The sensor values |
214 | 223 |
|
215 | 224 |
# Create a JSON formatted string from the sensor data. |
216 |
- sData = "[{\"period\":\"%s\", " % \ |
|
217 |
- (chartUpdateInterval) |
|
218 |
- for key in dData: |
|
219 |
- sData += '\"%s\":\"%s\", ' % (key, dData[key]) |
|
220 |
- sData = sData[:-2] + '}]\n' |
|
225 |
+ try: |
|
226 |
+ jsData = json.loads("{}") |
|
227 |
+ for key in dData: |
|
228 |
+ jsData.update({key:dData[key]}) |
|
229 |
+ jsData.update({"chartUpdateInterval": chartUpdateInterval}) |
|
230 |
+ sData = "[%s]" % json.dumps(jsData) |
|
231 |
+ except Exception as exError: |
|
232 |
+ print("%s writeOutputFile: %s" % (getTimeStamp(), exError)) |
|
233 |
+ return False |
|
234 |
+ |
|
235 |
+ if verboseDebug: |
|
236 |
+ print(sData) |
|
221 | 237 |
|
222 | 238 |
# Write the JSON formatted data to the output data file. |
239 |
+ |
|
223 | 240 |
try: |
224 | 241 |
fc = open(_OUTPUT_DATA_FILE, "w") |
225 | 242 |
fc.write(sData) |
226 | 243 |
fc.close() |
227 |
- except Exception, exError: |
|
228 |
- print "%s write output file failed: %s" % \ |
|
229 |
- (getTimeStamp(), exError) |
|
244 |
+ except Exception as exError: |
|
245 |
+ print("%s write output file failed: %s" % \ |
|
246 |
+ (getTimeStamp(), exError)) |
|
230 | 247 |
return False |
231 | 248 |
|
232 |
- if verboseDebug: |
|
233 |
- print sData[:-1] |
|
234 |
- if debugOption: |
|
235 |
- print "writing output data file: %d bytes" % len(sData) |
|
236 |
- |
|
237 | 249 |
return True |
238 | 250 |
## end def |
239 | 251 |
|
240 | 252 |
def createGraph(fileName, dataItem, gLabel, gTitle, gStart, |
241 | 253 |
lower=0, upper=0, trendLine=0, scaleFactor=1, |
242 | 254 |
autoScale=True, alertLine=""): |
243 |
- """Uses rrdtool to create a graph of specified node data item. |
|
244 |
- Parameters: |
|
245 |
- fileName - name of file containing the graph |
|
246 |
- dataItem - data item to be graphed |
|
247 |
- gLabel - string containing a graph label for the data item |
|
248 |
- gTitle - string containing a title for the graph |
|
249 |
- gStart - beginning time of the graphed data |
|
250 |
- lower - lower bound for graph ordinate #NOT USED |
|
251 |
- upper - upper bound for graph ordinate #NOT USED |
|
252 |
- trendLine |
|
253 |
- 0, show only graph data |
|
254 |
- 1, show only a trend line |
|
255 |
- 2, show a trend line and the graph data |
|
256 |
- scaleFactor - amount to pre-scale the data before charting |
|
257 |
- the data [default=1] |
|
258 |
- autoScale - if True, then use vertical axis auto scaling |
|
259 |
- (lower and upper parameters must be zero) |
|
260 |
- alertLine - value for which to print a critical |
|
261 |
- low voltage alert line on the chart. If not provided |
|
262 |
- alert line will not be printed. |
|
263 |
- Returns: True if successful, False otherwise |
|
255 |
+ """ |
|
256 |
+ Uses rrdtool to create a graph of specified sensor data item. |
|
257 |
+ Parameters: |
|
258 |
+ fileName - name of file containing the graph |
|
259 |
+ dataItem - data item to be graphed |
|
260 |
+ gLabel - string containing a graph label for the data item |
|
261 |
+ gTitle - string containing a title for the graph |
|
262 |
+ gStart - beginning time of the graphed data |
|
263 |
+ lower - lower bound for graph ordinate #NOT USED |
|
264 |
+ upper - upper bound for graph ordinate #NOT USED |
|
265 |
+ trendLine |
|
266 |
+ 0, show only graph data |
|
267 |
+ 1, show only a trend line |
|
268 |
+ 2, show a trend line and the graph data |
|
269 |
+ scaleFactor - amount to pre-scale the data before charting |
|
270 |
+ the data [default=1] |
|
271 |
+ autoScale - if True, then use vertical axis auto scaling |
|
272 |
+ (lower and upper parameters must be zero) |
|
273 |
+ alertLine - value for which to print a critical |
|
274 |
+ low voltage alert line on the chart. If not provided |
|
275 |
+ alert line will not be printed. |
|
276 |
+ Returns: True if successful, False otherwise |
|
264 | 277 |
""" |
265 | 278 |
gPath = _CHARTS_DIRECTORY + fileName + ".png" |
266 | 279 |
trendWindow = { 'end-1day': 7200, |
... | ... |
@@ -302,27 +315,28 @@ def createGraph(fileName, dataItem, gLabel, gTitle, gStart, |
302 | 315 |
strCmd += "HRULE:%s#FF0000:Critical\ Low\ Voltage " % (alertLine) |
303 | 316 |
|
304 | 317 |
if verboseDebug: |
305 |
- print "%s" % strCmd # DEBUG |
|
318 |
+ print("%s" % strCmd) # DEBUG |
|
306 | 319 |
|
307 | 320 |
# Run the formatted rrdtool command as a subprocess. |
308 | 321 |
try: |
309 | 322 |
result = subprocess.check_output(strCmd, \ |
310 | 323 |
stderr=subprocess.STDOUT, \ |
311 | 324 |
shell=True) |
312 |
- except subprocess.CalledProcessError, exError: |
|
313 |
- print "rrdtool graph failed: %s" % (exError.output) |
|
325 |
+ except subprocess.CalledProcessError as exError: |
|
326 |
+ print("rrdtool graph failed: %s" % (exError.output)) |
|
314 | 327 |
return False |
315 | 328 |
|
316 | 329 |
if debugOption: |
317 |
- print "rrdtool graph: %s\n" % result, |
|
330 |
+ print("rrdtool graph: %s" % result.decode('utf-8')) |
|
318 | 331 |
return True |
319 | 332 |
|
320 | 333 |
## end def |
321 | 334 |
|
322 | 335 |
def generateGraphs(): |
323 |
- """Generate graphs for display in html documents. |
|
324 |
- Parameters: none |
|
325 |
- Returns: nothing |
|
336 |
+ """ |
|
337 |
+ Generate graphs for display in html documents. |
|
338 |
+ Parameters: none |
|
339 |
+ Returns: nothing |
|
326 | 340 |
""" |
327 | 341 |
|
328 | 342 |
# 24 hour stock charts |
... | ... |
@@ -381,12 +395,13 @@ def generateGraphs(): |
381 | 395 |
## end def |
382 | 396 |
|
383 | 397 |
def getCLarguments(): |
384 |
- """Get command line arguments. There are three possible arguments |
|
385 |
- -d turns on debug mode |
|
386 |
- -v turns on verbose debug mode |
|
387 |
- -p sets the sensor query period |
|
388 |
- -c sets the chart update period |
|
389 |
- Returns: nothing |
|
398 |
+ """ |
|
399 |
+ Get command line arguments. There are three possible arguments |
|
400 |
+ -d turns on debug mode |
|
401 |
+ -v turns on verbose debug mode |
|
402 |
+ -p sets the sensor query period |
|
403 |
+ -c sets the chart update period |
|
404 |
+ Returns: nothing |
|
390 | 405 |
""" |
391 | 406 |
global debugOption, verboseDebug, dataRequestInterval, chartUpdateInterval |
392 | 407 |
|
... | ... |
@@ -401,36 +416,37 @@ def getCLarguments(): |
401 | 416 |
try: |
402 | 417 |
dataRequestInterval = abs(int(sys.argv[index + 1])) |
403 | 418 |
except: |
404 |
- print "invalid sensor query period" |
|
419 |
+ print("invalid sensor query period") |
|
405 | 420 |
exit(-1) |
406 | 421 |
index += 1 |
407 | 422 |
elif sys.argv[index] == '-c': |
408 | 423 |
try: |
409 | 424 |
chartUpdateInterval = abs(int(sys.argv[index + 1])) |
410 | 425 |
except: |
411 |
- print "invalid chart update period" |
|
426 |
+ print("invalid chart update period") |
|
412 | 427 |
exit(-1) |
413 | 428 |
index += 1 |
414 | 429 |
else: |
415 | 430 |
cmd_name = sys.argv[0].split('/') |
416 |
- print "Usage: %s [-d | v] [-p seconds] [-c seconds]" \ |
|
417 |
- % cmd_name[-1] |
|
431 |
+ print("Usage: %s [-d | v] [-p seconds] [-c seconds]" \ |
|
432 |
+ % cmd_name[-1]) |
|
418 | 433 |
exit(-1) |
419 | 434 |
index += 1 |
420 | 435 |
##end def |
421 | 436 |
|
422 | 437 |
def main(): |
423 |
- """Handles timing of events and acts as executive routine managing |
|
424 |
- all other functions. |
|
425 |
- Parameters: none |
|
426 |
- Returns: nothing |
|
438 |
+ """ |
|
439 |
+ Handles timing of events and acts as executive routine managing |
|
440 |
+ all other functions. |
|
441 |
+ Parameters: none |
|
442 |
+ Returns: nothing |
|
427 | 443 |
""" |
428 | 444 |
global dataRequestInterval |
429 | 445 |
|
430 | 446 |
signal.signal(signal.SIGTERM, terminateAgentProcess) |
447 |
+ signal.signal(signal.SIGINT, terminateAgentProcess) |
|
431 | 448 |
|
432 |
- print '%s starting up node power agent process' % \ |
|
433 |
- (getTimeStamp()) |
|
449 |
+ print('%s starting up node power agent process' % getTimeStamp()) |
|
434 | 450 |
|
435 | 451 |
# last time output JSON file updated |
436 | 452 |
lastDataRequestTime = -1 |
... | ... |
@@ -444,9 +460,9 @@ def main(): |
444 | 460 |
|
445 | 461 |
## Exit with error if rrdtool database does not exist. |
446 | 462 |
if not os.path.exists(_RRD_FILE): |
447 |
- print 'rrdtool database does not exist\n' \ |
|
448 |
- 'use createArednsigRrd script to ' \ |
|
449 |
- 'create rrdtool database\n' |
|
463 |
+ print('rrdtool database does not exist\n' \ |
|
464 |
+ 'use createPowerRrd script to ' \ |
|
465 |
+ 'create rrdtool database\n') |
|
450 | 466 |
exit(1) |
451 | 467 |
|
452 | 468 |
## main loop |
... | ... |
@@ -454,8 +470,8 @@ def main(): |
454 | 470 |
|
455 | 471 |
currentTime = time.time() # get current time in seconds |
456 | 472 |
|
457 |
- # Every web update interval request data from the aredn |
|
458 |
- # node and process the received data. |
|
473 |
+ # Every data request interval read the sensors and process the |
|
474 |
+ # data from the sensors. |
|
459 | 475 |
if currentTime - lastDataRequestTime > dataRequestInterval: |
460 | 476 |
lastDataRequestTime = currentTime |
461 | 477 |
dData = {} |
... | ... |
@@ -466,17 +482,14 @@ def main(): |
466 | 482 |
|
467 | 483 |
# If get data successful, write data to data files. |
468 | 484 |
if result: |
469 |
- result = writeOutputDataFile(dData) |
|
470 |
- pass |
|
485 |
+ result = writeOutputFile(dData) |
|
471 | 486 |
|
472 | 487 |
# At the rrdtool database update interval, update the database. |
473 |
- if currentTime - lastDatabaseUpdateTime > \ |
|
474 |
- _DATABASE_UPDATE_INTERVAL: |
|
488 |
+ if result and (currentTime - lastDatabaseUpdateTime > \ |
|
489 |
+ _DATABASE_UPDATE_INTERVAL): |
|
475 | 490 |
lastDatabaseUpdateTime = currentTime |
476 | 491 |
## Update the round robin database with the parsed data. |
477 |
- if result: |
|
478 |
- updateDatabase(dData) |
|
479 |
- pass |
|
492 |
+ result = updateDatabase(dData) |
|
480 | 493 |
|
481 | 494 |
# At the chart generation interval, generate charts. |
482 | 495 |
if currentTime - lastChartUpdateTime > chartUpdateInterval: |
... | ... |
@@ -490,10 +503,11 @@ def main(): |
490 | 503 |
elapsedTime = time.time() - currentTime |
491 | 504 |
if debugOption: |
492 | 505 |
if result: |
493 |
- print "%s update successful:" % getTimeStamp(), |
|
506 |
+ print("update successful: %6f sec\n" |
|
507 |
+ % elapsedTime) |
|
494 | 508 |
else: |
495 |
- print "%s update failed:" % getTimeStamp(), |
|
496 |
- print "%6f seconds processing time\n" % elapsedTime |
|
509 |
+ print("update failed: %6f sec\n" |
|
510 |
+ % elapsedTime) |
|
497 | 511 |
remainingTime = dataRequestInterval - elapsedTime |
498 | 512 |
if remainingTime > 0.0: |
499 | 513 |
time.sleep(remainingTime) |
... | ... |
@@ -502,8 +516,4 @@ def main(): |
502 | 516 |
## end def |
503 | 517 |
|
504 | 518 |
if __name__ == '__main__': |
505 |
- try: |
|
506 |
- main() |
|
507 |
- except KeyboardInterrupt: |
|
508 |
- print '\n', |
|
509 |
- terminateAgentProcess('KeyboardInterrupt','Module') |
|
519 |
+ main() |
... | ... |
@@ -1,4 +1,4 @@ |
1 |
-#!/usr/bin/python |
|
1 |
+#!/usr/bin/python2 |
|
2 | 2 |
# |
3 | 3 |
# Module: tmp102.py |
4 | 4 |
# |
... | ... |
@@ -31,12 +31,23 @@ import smbus |
31 | 31 |
import time |
32 | 32 |
|
33 | 33 |
# Define constants |
34 |
-DEGSYM = u'\xb0' |
|
34 |
+DEGSYM = u'\xB0' |
|
35 | 35 |
|
36 | 36 |
# Define TMP102 Device Registers |
37 | 37 |
CONFIG_REG = 0x1 |
38 | 38 |
TEMP_REG = 0x0 |
39 | 39 |
|
40 |
+# Define default sm bus address. |
|
41 |
+DEFAULT_BUS_ADDRESS = 0x48 |
|
42 |
+DEFAULT_BUS_NUMBER = 1 |
|
43 |
+ |
|
44 |
+# Define the default sensor configuration. See the TMP102 data sheet |
|
45 |
+# for meaning of each bit. The following bytes are written to the |
|
46 |
+# configuration register |
|
47 |
+# byte 1: 01100000 |
|
48 |
+# byte 2: 10100000 |
|
49 |
+DEFAULT_CONFIG = 0x60A0 |
|
50 |
+ |
|
40 | 51 |
class tmp102: |
41 | 52 |
|
42 | 53 |
# Initialize the TMP102 sensor at the supplied address (default |
... | ... |
@@ -44,16 +55,14 @@ class tmp102: |
44 | 55 |
# a new SMBus object for each instance of this class. Writes |
45 | 56 |
# configuration data (two bytes) to the TMP102 configuration |
46 | 57 |
# register. |
47 |
- def __init__(self, sAddr=0x48, sbus=1): |
|
58 |
+ def __init__(self, sAddr=DEFAULT_BUS_ADDRESS, |
|
59 |
+ sbus=DEFAULT_BUS_NUMBER, |
|
60 |
+ config=DEFAULT_CONFIG): |
|
48 | 61 |
# Instantiate a smbus object |
49 | 62 |
self.sensorAddr = sAddr |
50 | 63 |
self.bus = smbus.SMBus(sbus) |
51 |
- # Initialize TMP102 sensor. See the data sheet for meaning of |
|
52 |
- # each bit. The following bytes are written to the configuration |
|
53 |
- # register |
|
54 |
- # byte 1: 01100000 |
|
55 |
- # byte 2: 10100000 |
|
56 |
- initData = [0x60, 0xA0] |
|
64 |
+ # Initialize TMP102 sensor. |
|
65 |
+ initData = [(config >> 8), (config & 0x00FF)] |
|
57 | 66 |
self.bus.write_i2c_block_data(self.sensorAddr, CONFIG_REG, initData) |
58 | 67 |
## end def |
59 | 68 |
|
... | ... |
@@ -95,7 +104,7 @@ class tmp102: |
95 | 104 |
# Convert from two's complement to integer. |
96 | 105 |
# If d11 is 1, the the number is a negative two's complement |
97 | 106 |
# number. The absolute value is 2^12 - 1 minus the value |
98 |
- # of d10-d0 taken as a positive number. |
|
107 |
+ # of d11-d0 taken as a positive number. |
|
99 | 108 |
if bData > 0x7FF: # all greater values are negative numbers |
100 | 109 |
bData = -(0xFFF - bData) # 0xFFF is 2^12 - 1 |
101 | 110 |
# convert integer data to Celsius |
... | ... |
@@ -124,14 +133,14 @@ def testclass(): |
124 | 133 |
tempF = ts1.getTempF() |
125 | 134 |
if bAl: |
126 | 135 |
bAl = False |
127 |
- print "\033[42;30mTemperature Reg: %s %s\033[m" % regdata |
|
128 |
- print "\033[42;30m%6.2f%sC %6.2f%s \033[m" % \ |
|
129 |
- (tempC, DEGSYM, tempF, DEGSYM) |
|
136 |
+ print("\033[42;30mTemperature Reg: %s %s\033[m" % regdata) |
|
137 |
+ print("\033[42;30m%6.2f%sC %6.2f%s \033[m" % \ |
|
138 |
+ (tempC, DEGSYM, tempF, DEGSYM)) |
|
130 | 139 |
else: |
131 | 140 |
bAl = True |
132 |
- print "Temperature Reg: %s %s" % regdata |
|
133 |
- print "%6.2f%sC %6.2f%sF" % \ |
|
134 |
- (tempC, DEGSYM, tempF, DEGSYM) |
|
141 |
+ print("Temperature Reg: %s %s" % regdata) |
|
142 |
+ print("%6.2f%sC %6.2f%sF" % \ |
|
143 |
+ (tempC, DEGSYM, tempF, DEGSYM)) |
|
135 | 144 |
time.sleep(2) |
136 | 145 |
## end while |
137 | 146 |
## end def |