@@ -7,6 +7,9 @@ import inspect
import cStringIO
import numpy as np
cdef enum:
max_value_count = 64
cimport cython
cimport libc.stdlib
cimport libc.stdio
@@ -22,118 +25,117 @@ class FormatterError(Exception):
class Layout:
"""Represents a NILM database layout"""
def description(self):
"""Return the PyTables description of this layout"""
desc = {}
for (n, (name, type)) in enumerate(self.fields):
desc[name] = tables.Col.from_type(type, pos=n+1)
return tables.Description(desc)
def parse(self, char *text):
raise ParserError("n/a", "no parser for this layout")
def format(self, char *text):
raise FormatterError("no formatter for this layout")
class PrepData(Layout):
rate_hz = 120
fields = [ ( 'timestamp', 'float64' ),
( 'p1', 'float32' ),
( 'q1', 'float32' ),
( 'p3', 'float32' ),
( 'q3', 'float32' ),
( 'p5', 'float32' ),
( 'q5', 'float32' ),
( 'p7', 'float32' ),
( 'q7', 'float32' ) ]
def parse(self, char *text):
def __init__(self, typestring):
"""Initialize this Layout object to handle the specified
type string"""
try:
[ datatype, count ] = typestring.split("_")
except:
raise KeyError("invalid layout string")
try:
self.count = int(count)
except ValueError:
raise KeyError("invalid count")
if self.count < 1 or self.count > max_value_count:
raise KeyError("invalid count")
if datatype == 'uint16':
self.parse = self.parse_uint16
self.format = self.format_uint16
elif datatype == 'float32' or datatype == 'float64':
self.parse = self.parse_float64
self.format = self.format_float64
else:
raise KeyError("invalid type")
self.datatype = datatype
# Parsers
def parse_float64(self, char *text):
cdef int n
cdef double ts
# return doubles instead of float32, since they're going into
# Return doubles even in float32 case , since they're going into
# a Python array which would upconvert to double anyway.
cdef double v[8]
cdef char dummy
n = libc.stdio.sscanf(text, " %lf %lf %lf %lf %lf %lf %lf %lf %lf %c",
&ts, &v[0], &v[1], &v[2], &v[3], &v[4],
&v[5], &v[6], &v[7], &dummy)
if (n < 9) or (n > 9 and (dummy != '#' and dummy != '\n')):
raise ValueError("wrong number of values: wanted 9, got " + str(n))
return (ts, [ts, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]])
def format(self, d):
n = len(d)
if n != 9:
raise ValueError("wrong number of values: wanted 9, got " + str(n))
return ("%.6f %f %f %f %f %f %f %f %f\n" %
(d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8]))
class RawData(Layout):
rate_hz = 8000
fields = [ ( 'timestamp', 'float64' ),
( 'va', 'uint16' ),
( 'vb', 'uint16' ),
( 'vc', 'uint16' ),
( 'ia', 'uint16' ),
( 'ib', 'uint16' ),
( 'ic', 'uint16' ) ]
def parse(self, char *text):
result = []
cdef char *end
ts = libc.stdlib.strtod(text, &end)
if end == text:
raise ValueError("bad timestamp")
result.append(ts)
for n in range(self.count):
text = end
result.append(libc.stdlib.strtod(text, &end))
if end == text:
raise ValueError("wrong number of values")
n = 0
while end[n] == ' ':
n += 1
if end[n] != '\n' and end[n] != '#' and end[n] != '\0':
raise ValueError("extra data on line")
return (ts, result)
def parse_uint16(self, char *text):
cdef int n
cdef double ts
cdef int v[6]
cdef char dummy
n = libc.stdio.sscanf(text, " %lf %u %u %u %u %u %u %c",
&ts, &v[0], &v[1], &v[2],
&v[3], &v[4], &v[5], &dummy)
if (n < 7) or (n > 7 and (dummy != '#' and dummy != '\n')):
raise ValueError("wrong number of values: wanted 7, got " + str(n))
for i in range(6):
if v[i] < 0 or v[i] > 65535:
raise ValueError("value out of range: " + str(v[i]))
return (ts, [ts, v[0], v[1], v[2], v[3], v[4], v[5]])
def format(self, d):
n = len(d)
if n != 7:
raise ValueError("wrong number of values: wanted 7, got " + str(n))
return ("%.6f %d %d %d %d %d %d\n" %
(d[0], d[1], d[2], d[3], d[4], d[5], d[6]))
class RawNotchedData(RawData):
rate_hz = 8000
fields = RawData.fields + [
( 'notch_ia', 'uint16' ),
( 'notch_ib', 'uint16' ),
( 'notch_ic', 'uint16' ) ]
def parse(self, char *text):
cdef int n
cdef double ts
cdef int v[9]
cdef char dummy
n = libc.stdio.sscanf(text, " %lf %u %u %u %u %u %u %u %u %u %c",
&ts, &v[0], &v[1], &v[2], &v[3], &v[4],
&v[5], &v[6], &v[7], &v[8], &dummy)
if (n < 10) or (n > 10 and (dummy != '#' and dummy != '\n')):
raise ValueError("wrong number of values: wanted 10, got " + str(n))
for i in range(9):
if v[i] < 0 or v[i] > 65535:
raise ValueError("value out of range: " + str(v[i]))
return (ts, [ts, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8]])
def format(self, d):
n = len(d)
if n != 10:
raise ValueError("wrong number of values: wanted 10, got " + str(n))
return ("%.6f %d %d %d %d %d %d %d %d %d\n" %
(d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9]))
# Instantiate all layouts, indexed by their name
named = {}
for name, obj in inspect.getmembers(sys.modules[__name__]):
if inspect.isclass(obj) and issubclass(obj, Layout):
named[name] = obj()
result = []
cdef char *end
ts = libc.stdlib.strtod(text, &end)
if end == text:
raise ValueError("bad timestamp")
result.append(ts)
for n in range(self.count):
text = end
result.append(libc.stdlib.strtol(text, &end, 10))
if end == text:
raise ValueError("wrong number of values")
n = 0
while end[n] == ' ':
n += 1
if end[n] != '\n' and end[n] != '#' and end[n] != '\0':
raise ValueError("extra data on line")
return (ts, result)
# Formatters
def format_float64(self, d):
n = len(d) - 1
if n != self.count:
raise ValueError("wrong number of values for layout type: "
"got %d, wanted %d" % (n, self.count))
s = "%.6f" % d[0]
for i in range(n):
s += " %f" % d[i+1]
return s + "\n"
def format_uint16(self, d):
n = len(d) - 1
if n != self.count:
raise ValueError("wrong number of values for layout type: "
"got %d, wanted %d" % (n, self.count))
s = "%.6f" % d[0]
for i in range(n):
s += " %d" % d[i+1]
return s + "\n"
# PyTables description
def description(self):
"""Return the PyTables description of this layout"""
desc = {}
desc['timestamp'] = tables.Col.from_type('float64', pos=0)
for n in range(self.count):
desc['c' + str(n+1)] = tables.Col.from_type(self.datatype, pos=n+1)
return tables.Description(desc)
# Get a layout by name
def get_named(typestring):
try:
return Layout(typestring)
except KeyError:
compat = { "PrepData": "float32_8",
"RawData": "uint16_6",
"RawNotchedData": "uint16_9" }
return Layout(compat[typestring])
class Parser(object):
"""Object that parses and stores ASCII data for inclusion into the
@@ -144,7 +146,7 @@ class Parser(object):
self.layout = layout
else:
try:
self.layout = named[layout]
self.layout = get_named(layout)
except KeyError:
raise TypeError("unknown layout")
@@ -195,7 +197,7 @@ class Formatter(object):
self.layout = layout
else:
try:
self.layout = named[layout]
self.layout = get_named(layout)
except KeyError:
raise TypeError("unknown layout")