Created
March 1, 2016 13:04
-
-
Save ed-davies/4e2e4045c103e2946135 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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