Hi all,
I've written the attached simple Python plugin to read the XML output
from a CurrentCost CC128 home power monitoring device, as documented here:
http://www.currentcost.com/cc128/xml.htm
I've got a few questions:
1. Any feedback? I'm new to collectd and a little rusty in Python, so
any suggestions gratefully received.
2. The readings come in from the CurrentCost device whenever it decides,
which seem to be in the range of every 6 - 12 seconds, rather than every
10 seconds. My plugin keeps only the last reading until collectd calls
the read function, which means sometimes two readings arrive in a 10
second interval and one is lost, and sometimes no reading is delivered
(often just before/after). This of course means I'm losing a little
accuracy. Would it be better/acceptable to just ignore the interval and
deliver the readings whenever they arrive, and let god^WRRD sort it out?
3. This is running on a low-CPU ARM NAS box, and sometimes when under
heavy CPU / disk load, several minutes go by without any values being
logged. However, the other native plugins in collectd, disk, CPU,
memory, etc all seem to log without problems. I find it hard to believe
several minutes go by without my reader thread being scheduled while
collectd continues working so I'm kind of confused. Any ideas?
Many Thanks,
Rob
P.S. I've got a couple more questions but they're more about Python than
collectd - namely whether I need the lock at all - and whether I can do
without the 100ms timeout to check if self.run has been set to False by
the shutdown method.
import collectd
import serial
import sys
import threading
import xml.dom.minidom
class Reader(object):
def __init__(self, port='/dev/ttyUSB0'):
self.lock = threading.Lock()
self.ser = serial.Serial(port, 57600, timeout=0.1)
self.run = False
self.readings = {}
def start(self):
try:
self.ser.open()
except serial.SerialException, e:
sys.stderr.write("Could not open serial port %s: %s\n" % (ser.portstr, e))
sys.exit(1)
self.run = True
self.thread = threading.Thread(target=self.loop)
self.thread.daemon = True
self.thread.start()
def stop(self):
self.run = False
self.thread.join()
def get(self):
with self.lock:
readings = self.readings
self.readings = {}
return readings
def loop(self):
while self.run:
line = self.ser.readline()
if not line:
continue
dom = xml.dom.minidom.parseString(line)
# skip history dumps
hist = dom.getElementsByTagName("hist")
if len(hist) != 0:
continue
sensor = int(dom.getElementsByTagName("sensor")[0].childNodes[0].data)
tmpr = float(dom.getElementsByTagName("tmpr")[0].childNodes[0].data)
readings = {}
for ch in ['ch1', 'ch2', 'ch3']:
nodes = dom.getElementsByTagName(ch)
if len(nodes) == 0:
continue
readings[ch] = int(nodes[0].getElementsByTagName("watts")[0].childNodes[0].data)
with self.lock:
self.readings['tmpr'] = tmpr
self.readings[sensor] = readings
reader = None
def plugin_init():
global reader
reader = Reader()
reader.start()
def plugin_read(input_data=None):
global reader
readings = reader.get()
vals = collectd.Values()
vals.plugin = 'currentcost'
for sensor,reading in readings.iteritems():
if sensor == 'tmpr':
vals.plugin_instance = ''
vals.type = 'temperature'
vals.type_instance = ''
vals.values = [reading]
vals.dispatch()
continue
vals.plugin_instance = ('sensor%d' % sensor)
vals.type = 'power'
for ch in ['ch1', 'ch2', 'ch3']:
if ch not in reading:
continue
vals.type_instance = ch
vals.values = [reading[ch]]
vals.dispatch()
def plugin_shutdown():
global reader
reader.stop()
collectd.register_init(plugin_init)
collectd.register_read(plugin_read)
collectd.register_shutdown(plugin_shutdown)
_______________________________________________
collectd mailing list
[email protected]
http://mailman.verplant.org/listinfo/collectd