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

Reply via email to