# -*- coding: utf-8 -*- import asynchat import asyncore import socket import sys import logging import os from lxml import objectify from lxml import etree logger = logging.getLogger("ebus.core") logger.setLevel(logging.INFO) class UnknownPacketException(Exception): pass class EbusPacket(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 EbusPacket.ebus_xml.xpath("/ebus/devices/device[@address=$address]", address="#x%.2x"%address)] return len(d)>0 and d[0] or None 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>" % \ (self.__class__.__name__, self.name(), self.source, self.destination, \ self.primary_command, self.secondary_command, self.length) def get_packet_description(self): p=EbusPacket.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: raise UnknownPacketException("Unknown Packet, primary_command=#x%.2x secondary_command=#x%.2x" % ( self.primary_command, self.secondary_command)) def get_source_name(self): s=EbusPacket.ebus_xml.xpath("/ebus/devices/device[@address=$address]", address="#x%.2x"%self.source) return len(s)>0 and s[0].get("name") or None def get_destination_name(self): d=EbusPacket.ebus_xml.xpath("/ebus/devices/device[@address=$address]", address="#x%.2x"%self.destination) return len(d)>0 and d[0].get("name") or None def name(self): return self.get_packet_description().get("name") def description(self): return self.get_packet_description().get("description") class EbusMasterMaster(EbusPacket): def __init__(self, source, destination, primary_command, secondary_command, data): EbusPacket.__init__(self, source, destination, primary_command, secondary_command) self.length = len(data) self.data = data class EbusMasterSlave(EbusPacket): def __init__(self, source, destination, primary_command, secondary_command, request, response): EbusPacket.__init__(self, source, destination, primary_command, secondary_command) self.length = len(request) self.request = request self.response = response self.data = self.request #XXX class EbusBroadcast(EbusPacket): def __init__(self, source, destination, primary_command, secondary_command, data): EbusPacket.__init__(self, source, destination, primary_command, secondary_command) self.length = len(data) self.data = data class EbusReader(asynchat.async_chat): def __init__(self): asynchat.async_chat.__init__(self) self.set_terminator("") self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.connect(("10.2.2.200", 7970)) self.packetIndex = 0 self.buf = "" self.debug = False def collect_incoming_data(self,data): for it in range(len(data)): if data[it] == "\xaa": if it+1 < len(data) and data[it+1] != "\xaa": self._parse(self.buf) self.buf = "" self.packetIndex = 0 else: self.buf += data[it] self.packetIndex = self.packetIndex + 1 #print "%.2x [%d]" % (ord(data[it]),self.packetIndex) def _parse(self,dataRaw): #0xaa bug i = 0 data = "" while (i+1) < len(dataRaw): if ord(dataRaw[i]) == 0xaa and ord(dataRaw[i+1]) == 0x01: data += "\xaa" i = i + 1 elif ord(dataRaw[i]) == 0xa9 and ord(dataRaw[i+1]) == 0x00: data += "\xa9" i = i + 1 else: data += dataRaw[i] i = i + 1 if len(data) < 2: logger.critical("Daten Gaga") return source = ord(data[0]) sourceType = EbusPacket.address_to_type(source) destination = ord(data[1]) destinationType = EbusPacket.address_to_type(destination) if len(data) < 9: logger.critical("Unvollständige Daten") return primaryCommand = ord(data[2]) secondaryCommand = ord(data[3]) payloadLength = ord(data[4]) 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, " ".join(map(lambda byte: "%.2x"%ord(byte), payload))) try: if sourceType == 'master' and destinationType == 'master': p = EbusMasterMaster(source, destination, primaryCommand, secondaryCommand, payload) self.handle_ebus(p) elif sourceType == 'master' and destinationType == 'slave': p = EbusMasterSlave(source, destination, primaryCommand, secondaryCommand, payload, None) #FIXME logger.info("SKIP MASTER-SLAVE") elif sourceType == 'master' and destinationType == 'broadcast': p = EbusBroadcast(source, destination, primaryCommand, secondaryCommand, payload) self.handle_ebus(p) else: logger.warning("SKIP source=%s sourceType=%s destination=%s destType=%s" % \ (source, sourceType, destination, destinationType)) except UnknownPacketException,e: print e def handle_ebus(self,ebus_packet): logger.critical("unhandled ebus_packet")