summaryrefslogtreecommitdiff
path: root/ebus
diff options
context:
space:
mode:
Diffstat (limited to 'ebus')
-rw-r--r--ebus/__init__.py189
-rw-r--r--ebus/model/__init__.py92
-rw-r--r--ebus/model/sql.py128
-rw-r--r--ebus/web/controller.py2
4 files changed, 252 insertions, 159 deletions
diff --git a/ebus/__init__.py b/ebus/__init__.py
index 23e6595..832e9ff 100644
--- a/ebus/__init__.py
+++ b/ebus/__init__.py
@@ -8,102 +8,7 @@ import os
from lxml import objectify
from lxml import etree
-"""ebus specific data field formats"""
-class fields:
- class DataField(object):
- def __init__(self, name, offset):
- self.name = name
- self.offset = offset
- def value(self,data):
- raise NotImplemented()
-
- class Data1b(DataField):
- """
- Beispiel für die Berechnung:
- if ((x & 80h) == 80h) // y negativ
- y = - [dez(!x) + 1]
- else
- y = dez(x)
- """
- def value(self,data):
- x=ord(data[self.offset])
- if x & 0x80 == 0x80:
- return (-1) * (0xff^x + 1)
- else:
- return x
-
- class Data1c(DataField):
- def value(self,data):
- return ord(data[self.offset])/2.0
-
- class Data2c(DataField):
- """
- Beispiel für die Berechnung:
- if ((x & 8000h) == 8000h) // y negativ
- y = - [dez(High_Byte(!x)) 16 + dez(High_Nibble (Low_Byte (!x)))
- + (dez(Low_Nibble (Low_Byte (!x))) +1 ) / 16]
- else // y positiv
- y = dez(High_Byte(x)) 16 + dez(High_ Nibble (Low Byte (x)))
- + dez(Low_ Nibble (Low Byte (x))) / 16
- """
- def value(self,data):
- highByte = ord(data[self.offset+1])
- lowByte= ord(data[self.offset])
- if (0x8000 & (highByte<<8 | lowByte)) == 0x8000:
- return (-1) * ( (0xff^highByte)*16 + (0xff^(lowByte>>4)) + (0x0ff^(0xf&lowByte) + 1)/16.0 )
- else:
- return highByte*16 + (lowByte>>4) + (lowByte&0xf)/16.0
-
- class Data2b(DataField):
- """
- if ((x&8000h) == 8000h) // y negativ
- y = - [dez(High_Byte(!x)) + (dez(Low_Byte(!x)) + 1) / 256]
- else // y positiv
- y = dez(High_Byte (x)) + dez(Low_Byte (x)) / 256
- """
- def value(self,data):
- highByte = ord(data[self.offset+1])
- lowByte= ord(data[self.offset])
- if (0x8000 & (highByte<<8 | lowByte)) == 0x8000:
- return (-1) * ((0xff^highByte) + (0xff^lowByte+1)/256.0)
- else:
- return highByte + lowByte/256.0
-
- class Bit(DataField):
- def value(self, data):
- return ord(data[self.offset]) == 0x1
-
- class Word(DataField):
- def value(self, data):
- lb, hb = data[self.offset], data[self.offset+1]
- return ord(lb) + (ord(hb)<<8)
-
- class Bcd(DataField):
- """
- y = dez(High_Nibble(x))*10 + dez(Low_Nibble(x))
- """
- def value(self, data):
- byte = ord(data[self.offset])
- return (byte >> 4) * 10 + (byte & 0xf)
-
- class Byte(DataField):
- def value(self, data):
- return ord(data[self.offset])
-
-
- class ByteEnum(DataField):
- def __init__(self, name, offset, values):
- fields.DataField.__init__(self, name, offset)
- self.values = values
-
- def value(self, data):
- value = ord(data[self.offset])
- if self.values.has_key(value):
- return self.values[value]
- else:
- return None
-
-class EbusXMLMixin(object):
+class EbusPacket(object):
EBUS_SPECIFICATION = os.path.join(os.path.dirname(__file__), "ebus_specification.xml")
ebus_xml = objectify.parse(open(EBUS_SPECIFICATION))
@@ -113,83 +18,46 @@ class EbusXMLMixin(object):
address="#x%.2x"%address)]
return len(d)>0 and d[0] or None
- def _get_packet(self):
+ def __init__(self, source, destination, primary_command, secondary_command):
+ self.source = source
+ self.destination = destination
+ self.primary_command = primary_command
+ self.secondary_command = secondary_command
+
+ def __str__(self):
+ #XXX self.length only in subclasses
+ return "<%-18s name=\"%s\" source=\"0x%.2x\" destination=\"0x%.2x\" primary=0x%x secondary=0x%x length=0x%x %s>" % \
+ (self.__class__.__name__, self.name(), self.source, self.destination, \
+ self.primary_command, self.secondary_command, self.length, \
+ " ".join(map(lambda name: "%s=%s" % (name, self.values()[name]),self.values())))
+
+ def get_packet_description(self):
p=EbusXMLMixin.ebus_xml.xpath("/ebus/packets/packet[@primary=$primary and @secondary=$secondary]",
primary="#x%.2x"%self.primary_command,
secondary="#x%.2x"%self.secondary_command)
if len(p)>0 and p[0] is not None:
return p[0]
else:
- return None
+ raise "Unknown Packet, primary_command=#x%.2 secondary_command=#x%.2" % (
+ self.primary_command,
+ self.secondardy_command)
- def _get_source(self):
+ def _get_source_name(self):
s=EbusXMLMixin.ebus_xml.xpath("/ebus/devices/device[@address=$address]",
address="#x%.2x"%self.source)
return len(s)>0 and s[0] or None
- def _get_destination(self):
+ def _get_destination_name(self):
d=EbusXMLMixin.ebus_xml.xpath("/ebus/devices/device[@address=$address]",
address="#x%.2x"%self.destination)
return len(d)>0 and d[0] or None
def name(self):
- return self._get_packet().get("name")
+ return self.get_packet_description().get("name")
def description(self):
- return self._get_packet().get("description")
-
- def format(self):
- packet = self._get_packet()
- if packet is not None:
- for field in packet.fields.iterchildren():
- name = field.get("name")
- offset = field.get("offset")
- if not name:
- continue
- elif field.tag == "bit":
- yield fields.Bit( name, int(offset) )
- elif field.tag == "byte":
- yield fields.Byte( name, int(offset) )
- elif field.tag == "word":
- yield fields.Word( name, int(offset) )
- elif field.tag == "data1b":
- yield fields.Data1b( name, int(offset) )
- elif field.tag == "data1c":
- yield fields.Data1c( name, int(offset) )
- elif field.tag == "data2b":
- yield fields.Data2b( name, int(offset) )
- elif field.tag == "data2c":
- yield fields.Data2c( name, int(offset) )
- elif field.tag == "bcd":
- yield fields.Bcd( name, int(offset) )
- elif field.tag == "byteEnum":
- options = dict(map(lambda opt: ( int(opt.get("value")[2:],16), opt.get("name")),
- field.xpath("./option")))
- yield fields.ByteEnum( name, int(offset), options)
- else:
- print "error: %s in %s" % (name, self.name())
-
-
-def formatHex(data):
- return " ".join(map(lambda byte: "%.2x"%ord(byte), data))
-
-class EbusPacket(EbusXMLMixin):
- def __init__(self, source, destination, primary_command, secondary_command):
- self.source = source
- self.destination = destination
- self.primary_command = primary_command
- self.secondary_command = secondary_command
+ return self.get_packet_description().get("description")
- def __str__(self):
- #XXX self.length only in subclasses
- return "<%-18s name=\"%s\" source=\"0x%.2x\" destination=\"0x%.2x\" primary=0x%x secondary=0x%x length=0x%x %s>" % \
- (self.__class__.__name__, self.name(), self.source, self.destination, \
- self.primary_command, self.secondary_command, self.length, \
- " ".join(map(lambda name: "%s=%s" % (name, self.values()[name]),self.values())))
-
- def values(self):
- return dict( map(lambda format: (format.name, format.value(self.data) ),
- self.format() ))
class EbusMasterMaster(EbusPacket):
def __init__(self, source, destination, primary_command, secondary_command, data):
@@ -269,9 +137,15 @@ class EbusReader(asynchat.async_chat):
payload = data[5:5+payloadLength]
if self.debug:
- print "\033[1;31m%.2x %.2x\033[1;m \033[1;33m%.2x %.2x\033[1;m \033[1;30m%.2x\033[1;m \033[1;45m%s\033[1;m" % (source,destination,primaryCommand,secondaryCommand,payloadLength,formatHex(payload))
+ print "\033[1;31m%.2x %.2x\033[1;m \033[1;33m%.2x %.2x\033[1;m \033[1;30m%.2x\033[1;m \033[1;45m%s\033[1;m" % (
+ source,
+ destination,
+ primaryCommand,
+ secondaryCommand,
+ payloadLength,
+ " ".join(map(lambda byte: "%.2x"%ord(byte), payload)))
+
- p = None
if sourceType == 'master' and destinationType == 'master':
p = EbusMasterMaster(source, destination, primaryCommand, secondaryCommand, payload)
self.handle_ebus(p)
@@ -282,8 +156,7 @@ class EbusReader(asynchat.async_chat):
p = EbusBroadcast(source, destination, primaryCommand, secondaryCommand, payload)
self.handle_ebus(p)
else:
- print >>sys.stderr, "KOMISCHES ZEUG"
- print >>sys.stderr, "source=%s sourceType=%s destination=%s destType=%s" % \
+ print >>sys.stderr, "SKIP source=%s sourceType=%s destination=%s destType=%s" % \
(source, sourceType, destination, destinationType)
def handle_ebus(self,ebus_packet):
diff --git a/ebus/model/__init__.py b/ebus/model/__init__.py
new file mode 100644
index 0000000..ecdf3bc
--- /dev/null
+++ b/ebus/model/__init__.py
@@ -0,0 +1,92 @@
+class DataField(object):
+ def __init__(self, name, offset):
+ self.name = name
+ self.offset = offset
+ def value(self,data):
+ raise NotImplemented()
+
+ class Data1b(DataField):
+ """
+ Beispiel für die Berechnung:
+ if ((x & 80h) == 80h) // y negativ
+ y = - [dez(!x) + 1]
+ else
+ y = dez(x)
+ """
+ def value(self,data):
+ x = ord(data[self.offset])
+ if x & 0x80 == 0x80:
+ return (-1) * (0xff^x + 1)
+ else:
+ return x
+
+class Data1c(DataField):
+ def value(self,data):
+ return ord(data[self.offset])/2.0
+
+class Data2c(DataField):
+ """
+ Beispiel für die Berechnung:
+ if ((x & 8000h) == 8000h) // y negativ
+ y = - [dez(High_Byte(!x)) 16 + dez(High_Nibble (Low_Byte (!x)))
+ + (dez(Low_Nibble (Low_Byte (!x))) +1 ) / 16]
+ else // y positiv
+ y = dez(High_Byte(x)) 16 + dez(High_ Nibble (Low Byte (x)))
+ + dez(Low_ Nibble (Low Byte (x))) / 16
+ """
+ def value(self,data):
+ highByte = ord(data[self.offset+1])
+ lowByte= ord(data[self.offset])
+ if (0x8000 & (highByte<<8 | lowByte)) == 0x8000:
+ return (-1) * ( (0xff^highByte)*16 + (0xff^(lowByte>>4)) + (0x0ff^(0xf&lowByte) + 1)/16.0 )
+ else:
+ return highByte*16 + (lowByte>>4) + (lowByte&0xf)/16.0
+
+class Data2b(DataField):
+ """
+ if ((x&8000h) == 8000h) // y negativ
+ y = - [dez(High_Byte(!x)) + (dez(Low_Byte(!x)) + 1) / 256]
+ else // y positiv
+ y = dez(High_Byte (x)) + dez(Low_Byte (x)) / 256
+ """
+ def value(self,data):
+ highByte = ord(data[self.offset+1])
+ lowByte= ord(data[self.offset])
+ if (0x8000 & (highByte<<8 | lowByte)) == 0x8000:
+ return (-1) * ((0xff^highByte) + (0xff^lowByte+1)/256.0)
+ else:
+ return highByte + lowByte/256.0
+
+class Bit(DataField):
+ def value(self, data):
+ return ord(data[self.offset]) == 0x1
+
+class Word(DataField):
+ def value(self, data):
+ lb, hb = data[self.offset], data[self.offset+1]
+ return ord(lb) + (ord(hb)<<8)
+
+class Bcd(DataField):
+ """
+ y = dez(High_Nibble(x))*10 + dez(Low_Nibble(x))
+ """
+ def value(self, data):
+ byte = ord(data[self.offset])
+ return (byte >> 4) * 10 + (byte & 0xf)
+
+class Byte(DataField):
+ def value(self, data):
+ return ord(data[self.offset])
+
+
+class ByteEnum(DataField):
+ def __init__(self, name, offset, values):
+ fields.DataField.__init__(self, name, offset)
+ self.values = values
+
+ def value(self, data):
+ value = ord(data[self.offset])
+ if self.values.has_key(value):
+ return self.values[value]
+ else:
+ return None
diff --git a/ebus/model/sql.py b/ebus/model/sql.py
new file mode 100644
index 0000000..fa9b479
--- /dev/null
+++ b/ebus/model/sql.py
@@ -0,0 +1,128 @@
+# -*- coding: utf-8 -*-
+from datetime import datetime
+from sqlalchemy import Column, Integer, Float, DateTime, String, ForeignKey
+from sqlalchemy.orm import relationship, backref
+from sqlalchemy.ext.declarative import declarative_base
+
+ModelBase = declarative_base()
+
+class Sensor(ModelBase):
+ __tablename__ = 'sensor'
+
+ id = Column(Integer, primary_key=True)
+ name = Column(String)
+ description = Column(String, default="")
+
+ def __init__(self, name, description=""):
+ self.name = name
+ self.description = description
+
+ def __repr__(self):
+ return "<Sensor('%s','%s','%s')>" % (self.id, self.name, self.description)
+
+# http://www.sqlalchemy.org/docs/05/reference/ext/declarative.html#inheritance-configuration
+class Value(ModelBase):
+ __tablename__ = 'value'
+
+ id = Column(Integer, primary_key=True)
+ timestamp = Column(DateTime)
+ sensor_id = Column(Integer, ForeignKey("sensor.id"))
+ sensor = relationship(Sensor, backref=backref('values', order_by=timestamp))
+
+ discriminator = Column('type', String(50))
+ __mapper_args__ = {'polymorphic_on': discriminator}
+
+ def __init__(self, sensor, timestamp=datetime.now()):
+ self.timestamp = timestamp
+ self.sensor = sensor
+
+ def __repr__(self):
+ return "<Value('%s','%s','%s','%s'>" % (self.id, self.sensor, self.value, self.timestamp)
+
+ def value(self):
+ raise NotImplementedException()
+
+class ValueFloat(Value):
+ __mapper_args__ = {'polymorphic_identity': 'float'}
+ value_float = Column(Float(precision=4))
+
+ def __init__(self, sensor, value, timestamp=datetime.now()):
+ Value.__init__(self, sensor, timestamp)
+ self.value_float = value
+
+ def value(self):
+ return self.value_float
+
+class ValueInt(Value):
+ __mapper_args__ = {'polymorphic_identity': 'int'}
+ value_int = Column(Integer)
+
+ def __init__(self, sensor, value, timestamp=datetime.now()):
+ Value.__init__(self, sensor, timestamp)
+ self.value_int = value
+
+ def value(self):
+ return self.value_int
+
+class ValueString(Value):
+ __mapper_args__ = {'polymorphic_identity': 'string'}
+ value_string = Column(String)
+
+ def __init__(self, sensor, value, timestamp=datetime.now()):
+ Value.__init__(self, sensor, timestamp)
+ self.value_string = value
+
+ def value(self):
+ return self.value_string
+
+
+class EbusPacketSQL(object):
+ def __init__(self,session):
+ self.session = session
+
+ def get_values(self,packet):
+ packet_description = packet.get_packet_description()
+ sensor_name = packet._get_source_name()
+ sensor = self.session.query(Sensor) \
+ .filter(Sensor.name==sensor_name)
+
+ if packet is None:
+ return
+
+ for field in packet_description.fields:
+ name = field.name
+ offset = int(field.offset)
+ if not name:
+ continue
+ elif field.tag == "bit":
+ yield ValueInt(sensor, \
+ fields.Bit(name, offset).value())
+ elif field.tag == "byte":
+ yield ValueInt(sensor, \
+ fields.Byte(name, offset).value())
+ elif field.tag == "word":
+ yield ValueInt(sensor, \
+ fields.Word(name, offset).value())
+ elif field.tag == "data1b":
+ yield ValueInt(sensor, \
+ fields.Data1b(name, offset).value())
+ elif field.tag == "data1c":
+ yield ValueInt(sensor, \
+ fields.Data1c(name, offset).value())
+ elif field.tag == "data2b":
+ yield ValueInt(sensor, \
+ fields.Data2b(name, offset).value())
+ elif field.tag == "data2c":
+ yield ValueInt(sensor,
+ fields.Data2c(name, offset).value())
+ elif field.tag == "bcd":
+ yield ValueString(sensor,
+ fields.Bcd(name, offset).value())
+ elif field.tag == "byteEnum":
+ options = dict(map(lambda opt: ( int(opt.get("value")[2:],16), opt.get("name")),
+ field.xpath("./option")))
+ yield ValueString(sensor,
+ fields.ByteEnum(name, offset, options).value())
+ else:
+ print "Unsupported field-type %s" % (name)
+
diff --git a/ebus/web/controller.py b/ebus/web/controller.py
index c5b50d2..683bb6c 100644
--- a/ebus/web/controller.py
+++ b/ebus/web/controller.py
@@ -6,7 +6,7 @@ from werkzeug.contrib.cache import SimpleCache
from simplejson import dumps as dump_json
from sqlalchemy.orm import sessionmaker
-from ebus.web import model
+from ebus import model
Session = sessionmaker()