Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
#!/usr/bin/env python2.6
# -*- coding: utf-8 -*-
import sys
import serial
import threading
import Queue
import datetime
import xml.dom
import xml.dom.minidom
class SporadicMessageReader(object):
""" Read text messages from a serial port delimited by line-idle
periods of at least one second.
Queues the messages received as (timestamp, message-text)
tuples. End of queue in marked by None.
"""
def __init__(self, port, queue):
self.port = port
self.port.timeout = 1.0
self.queue = queue
self.running = True
self.thread = threading.Thread(target = lambda: self.run())
self.thread.start()
def stop(self):
self.running = False
class DoneReadingException(Exception):
pass
def getByte(self):
""" Read one byte, or none if there's a one second timeout.
Raise exception if the thread should stop. """
if not self.running:
raise self.DoneReadingException
return self.port.read(1)
def run(self):
try:
# Discard any part messages in progress as we start up.
while len(self.getByte()) > 0:
pass
# Loop over complete messages.
while True:
# Wait for the first character of the message.
c = self.getByte()
while len(c) < 1:
c = self.getByte()
# Note time of first character of message.
t = datetime.datetime.now()
# Read the rest of the message.
msg = [c] if ord(c) <= 0x7E else []
c = self.getByte()
while len(c) > 0:
if ord(c) <= 0x7E:
msg.append(c)
c = self.getByte()
self.queue.put((t, ''.join(msg)))
except self.DoneReadingException:
pass
finally:
self.queue.put(None)
class CurrentCostMessageHandler(object):
""" To deal with text fragments received from the CurrentCost meter
and put in a Queue.Queue object for us.
These should be one or more <msg> XML documents. """
def __init__(self, queue):
self.queue = queue
self.thread = threading.Thread(target = lambda: self.run())
self.thread.start()
@staticmethod
def safeString(msg):
""" Escape characters other than printable ASCII. """
def safeChar(c):
o = ord(c)
if o < 0x20 or 0x7E < o:
return '%' + hex(o)
else:
return c
return ''.join(map(safeChar, msg))
def getTextMessages(self):
""" Yield the timestamp/message string tuples from the input queue. """
m = self.queue.get()
while m != None:
yield m
m = self.queue.get()
def getXMLMessages(self):
""" Yield timestamp/xml message element tuples from the input. """
for time, text in self.getTextMessages():
# Turn the text which can contain multiple XML elements into a
# single XML document with a <fragment> root element.
x = None
try:
x = xml.dom.minidom.parseString(
''.join(['<fragment>', text.strip(), '</fragment>'])
)
except:
print 'Error parsing XML in message: %s: "%s"' % (time, self.safeString(text))
# Yield the XML element children on the aforementioned <fragment>
# element. Typically these will be CurrentCost <msg> elements.
if x != None:
doc = x.documentElement
c = doc.firstChild
while c != None:
next = c.nextSibling
if c.nodeType == xml.dom.Node.ELEMENT_NODE:
doc.removeChild(c)
yield time, c
c = next
x.unlink()
def run(self):
for time, x in self.getXMLMessages():
print time, x.toprettyxml(indent=' ')
sys.stdout.flush()
x.unlink()
print "CurrentCostMessageHandler done"
def writeLog():
s = serial.Serial(port='/dev/ttyUSB0', baudrate=57600)
q = Queue.Queue()
r = SporadicMessageReader(s, q)
h = CurrentCostMessageHandler(q)
import time
try:
while True:
time.sleep(135)
except:
pass
r.stop()
def readLog():
def entryTexts():
dtime = None
e = []
for line in sys.stdin:
if line.endswith('\n'):
line = line[:-1]
if line.startswith('2'):
if dtime != None:
e = '\n'.join(e)
try:
x = xml.dom.minidom.parseString(e)
yield dtime, x
except:
print 'Error parsing XML:', e
s = line.split(' ')
assert len(s) == 3
dtime = 'T'.join(s[0:2])
e = [s[2]]
else:
e.append(line)
for dt, x in entryTexts():
pass
readLog()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment