From fd2cda186ad308833753ada16dbc7aa18277e183 Mon Sep 17 00:00:00 2001 From: yvesf Date: Tue, 20 Apr 2010 19:43:34 +0000 Subject: foo git-svn-id: http://10.2.2.13/svn/common/ebus@1662 d0e8fea9-7529-0410-93fb-d39fd5b9c1dd --- ebus/__init__.py | 219 ++++++++++++++++++++----------------------------------- 1 file changed, 81 insertions(+), 138 deletions(-) (limited to 'ebus') diff --git a/ebus/__init__.py b/ebus/__init__.py index 71d6397..0813b53 100644 --- a/ebus/__init__.py +++ b/ebus/__init__.py @@ -1,27 +1,18 @@ -# -*- coding:utf8 -*- +# -*- coding: utf-8 -*- import asynchat import asyncore import socket import sys -deviceDescription = [ - {'address':0x03, 'type':'master', 'description':'Feuerungsautomat'}, - {'address':0x10, 'type':'master', 'description':'Heizungsregler #2'}, - {'address':0x30, 'type':'master', 'description':'Heizkreisregler 1'}, - {'address':0x70, 'type':'master', 'description':'Heizkreisregler 2'}, - {'address':0x71, 'type':'master', 'description':'Heizungsregler #9'}, - {'address':0xf1, 'type':'master', 'description':'Heizungsregler #10'}, - {'address':0x50, 'type':'slave', 'description':'Mischer 1'}, - {'address':0x51, 'type':'slave', 'description':'Mischer 2'}, - {'address':0x90, 'type':'slave', 'description':'Raumgeräte/Fernsteller 1'}, - {'address':0x91, 'type':'slave', 'description':'Raumgeräte/Fernsteller 2'}, - {'address':0xfe, 'type':'broadcast', 'description':'Broadcast'}, -] +import os +from lxml import objectify +from lxml import etree """ebus specific data field formats""" class fields: class DataField(object): - def __init__(self, offset): + def __init__(self, name, offset): + self.name = name self.offset = offset def value(self,data): raise NotImplemented() @@ -81,9 +72,10 @@ class fields: class ByteEnum(DataField): - def __init__(self, offset, values): + def __init__(self, name, offset, values): + fields.DataField.__init__(self, name, offset) self.values = values - fields.DataField.__init__(self, offset) + def value(self, data): value = ord(data[self.offset]) if self.values.has_key(value): @@ -91,127 +83,84 @@ class fields: else: return None -#Pflichfelder: primary, secondary, description, name, format (auch leer) -packetDescription = [ - # Service 0x05 (Brennersteuerbefehle) - {'primary':0x5, 'secondary':0x3, 'description':'Betriebsdaten des Feuerungsautomaten an den Regler Block1','name':'betriebsdatenRegler1', 'format': { - 'blocknumber':fields.Byte(0), - #('errorCode':fields.Byte(1), - 'stellgradKesselleistung':fields.Byte(3), - 'kesseltemperatur':fields.Data1c(4), - 'ruecklaufTemperatur':fields.Byte(5), - 'boilerTemperatur':fields.Byte(6), - 'ausenTemperatur':fields.Byte(7), - #if ord(fields.Byte(0)) == 0x01 else - #'abgasTemperatur':fields.Data2c(1), - #'BWW-Vorlauftemperatur':fields.Data1c(3), - #'momentaneRelativeKesselTemperatur':fields.Data1c(4), - #'gemeinsameVorlaufTemperatur':fields.Data1c(5)), - }}, - - {'primary':0x5, 'secondary':0x7, 'description':'Betriebsdaten des Reglers an den Feuerungsautomaten','name':'betriebsdatenFeuerungsautomat','format': { - 'betriebszustand':fields.ByteEnum(0, { - 0x00:'Brenner Abschalten', - 0x01:'Keine Aktion', - 0x55:'Brauchwassbereitung', - 0xaa:'Heizbetrieb', - 0xcc:'Emmissionskontroll', - 0xdd:'TÜV-Funktion', - 0xee:'Regler Stopp', - 0x66:'Brauchwasserbereitung bei Reglerstoppfunktion', - 0xbb:'Brauchwasserbereitung bei Heizbetrieb', - 0x44:'Reglerstoppfunktion bei stufigem Betrieb'}), - 'betriebszustand2':fields.ByteEnum(1, { - 0x00:'Keine Aktion', - 0x01:'Ausschalten Kesselpumpe', - 0x02:'Einschalten Kesselpumpe', - 0x03:'Ausschalten variable Verbraucher', - 0x04:'Einschalten variabler Verbraucher'}), - 'kesselSollwertTemperatur':fields.Data2c(2), - 'kesselSollwertDruck':fields.Data2b(4), - 'stellgrad':fields.Data1c(6), - 'brauchwasserSollwert':fields.Data1c(7) - }}, - # Service 0x07 (Systemdatenbefehle) - {'primary':0x7, 'secondary':0x0, 'description':'Datum/Zeit - Meldung eines eBUS Masters', 'name':'datumZeit', 'format': { - 'aussenTemperatur':fields.Data2b(0), - 'sekunden':fields.Bcd(2), - 'minuten':fields.Bcd(3), - 'stunden':fields.Bcd(4), - 'tag':fields.Bcd(5), - 'monat':fields.Bcd(6), - 'wochentag':fields.Bcd(7), - 'jahr':fields.Bcd(8)}}, - - {'primary':0x7, 'secondary':0x4, 'description':'Identifikation', 'name':'identifikation', 'format':{ - }}, - - # Service 0x08 (Reglerbefehle) - {'primary':0x8, 'secondary':0x0, 'description':'Sollwertübertragung des Reglers an andere Regler', 'name':'sollwertÜbertragungRegler', 'format':{ - }}, - - # Response - #p[0] = Einheit (1=>Liter, 2=>Kubik) - #p[1] = 10^0 - #p[2] = 10^2 - #p[3] = 10^4 - #p[4] = 10^6 - {'primary':0x3, 'secondary':0x8, 'description':'Gesamtbrennstoffmengenzähler lesen', 'name':'brenstoffMengeLesen', 'format':{ - }}, - - {'primary':0x50, 'secondary':0x17, 'description':'Solar Daten', 'name': 'solarDaten', 'format':{ - 'solarPumpe':fields.Bit(0), - 'tempKollektor':fields.Data2c(2), - 'tempWarmwasserSolar':fields.Data2c(4)}}, -] +class EbusXMLMixin(object): + EBUS_SPECIFICATION = os.path.join(os.path.dirname(__file__), "ebus_specification.xml") + ebus_xml = objectify.parse(open(EBUS_SPECIFICATION)) + + @staticmethod + def address_to_type(address): + d=[dev.get("type") for dev in EbusXMLMixin.ebus_xml.xpath("/ebus/devices/device[@address=$address]", + address="0x%.2x"%address)] + return len(d)>0 and d[0] or None + + def _get_packet(self): + p=EbusXMLMixin.ebus_xml.xpath("/ebus/packets/packet[@primary=$primary and @secondary=$secondary]", + primary="0x%.2x"%self.primary_command, + secondary="0x%.2x"%self.secondary_command) + return len(p)>0 and p[0] or None + + def _get_source(self): + s=EbusXMLMixin.ebus_xml.xpath("/ebus/devices/device[@address=$address]", + address="0x%.2x"%self.source) + return len(s)>0 and s[0] or None + + def _get_destination(self): + d=EbusXMLMixin.ebus_xml.xpath("/ebus/devices/device[@address=$address]", + address="0x%.2x"%self.destination) + return len(d)>0 and d[0] or None + + def name(self): + return self._get_packet().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 == "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"),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)) -def getDsc(address): - dev=filter(lambda dev: dev['address'] == address, deviceDescription) - if len(dev)>0: - return dev[0]['description'] - else: - return None - -class EbusPacket(object): +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 - def name(self): - if self._definition(): - return self._definition()['name'] - - def description(self): - if self._definition(): - return self._definition()['description'] - def __str__(self): #XXX self.length only in subclasses - return "<%-18s name=\"%-15s\" source=\"%s\" destination=\"%s\" primary=0x%x secondary=0x%x length=0x%x %s>" % \ - (self.__class__.__name__, self.name(), getDsc(self.source), getDsc(self.destination), \ + 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 _definition(self): - desc = filter(lambda p: p['primary'] == self.primary_command \ - and p['secondary'] == self.secondary_command, packetDescription) - if len(desc) > 0: - return desc[0] - else: - return None - def values(self): - definition = self._definition() - if definition and definition.has_key('format'): - #XXX self.data only in subclass - return dict( map(lambda name: (name, definition['format'][name].value(self.data) ), definition['format'].keys()) ) - else: - return dict() + 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): @@ -235,8 +184,6 @@ class EbusBroadcast(EbusPacket): class EbusReader(asynchat.async_chat): def __init__(self): - self.buffer = "" - asynchat.async_chat.__init__(self) self.set_terminator("") self.create_socket(socket.AF_INET, socket.SOCK_STREAM) @@ -277,13 +224,9 @@ class EbusReader(asynchat.async_chat): print "GAGA" return source = ord(data[0]) + sourceType = EbusXMLMixin.address_to_type(source) destination = ord(data[1]) - - sourceDevice = filter(lambda dev: dev['address'] == source, deviceDescription) - destinationDevice = filter(lambda dev: dev['address'] == destination, deviceDescription) - if len(sourceDevice) == 0 or len(destinationDevice) == 0: - print >>sys.stderr, "Unbekanntes Paket: source=%x destination=%x" % (source, destination) - return + destinationType = EbusXMLMixin.address_to_type(destination) if len(data) < 9: print >>sys.stderr, "Unvollständige Daten" @@ -293,23 +236,23 @@ class EbusReader(asynchat.async_chat): secondaryCommand = ord(data[3]) payloadLength = ord(data[4]) payload = data[5:5+payloadLength] - ### DEBUG ### + if self.debug: - "\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,formatHex(payload)) p = None - if sourceDevice[0]['type'] == 'master' and destinationDevice[0]['type'] == 'master': + if sourceType == 'master' and destinationType == 'master': p = EbusMasterMaster(source, destination, primaryCommand, secondaryCommand, payload) - self.handle_ebus(p) - elif sourceDevice[0]['type'] == 'master' and destinationDevice[0]['type'] == 'slave': + elif sourceType == 'master' and destinationType == 'slave': p = EbusMasterSlave(source, destination, primaryCommand, secondaryCommand, payload, None) #FIXME - self.handle_ebus(p) - elif sourceDevice[0]['type'] == 'master' and destinationDevice[0]['type'] == 'broadcast': + elif sourceType == 'master' and destinationType == 'broadcast': 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" % \ + (source, sourceType, destination, destinationType) return + self.handle_ebus(p) def handle_ebus(self,ebus_packet): print >>sys.stderr, "unhandled ebus_packet" -- cgit v1.2.1