Beispiel #1
0
    def __init__(self, ets, mcastAddr="224.0.23.12", mcastPort=3671):
        """

        @param mcastAddr: multicast address to bind to
        @type mcastAddr: str

        @param mcastPort: multicast port to bind to
        @type mcastPort: str

        raise UDPTransceiverValueError:
        """
        super(UDPTransceiver, self).__init__(ets)

        self._mcastAddr = mcastAddr
        self._mcastPort = mcastPort

        localAddr = "0.0.0.0"
        # socket.gethostbyname(socket.gethostname())
        self._transmitterSock = MulticastSocketTransmit(
            localAddr, 0, mcastAddr, mcastPort)
        self._receiverSock = MulticastSocketReceive(
            localAddr, self._transmitterSock.localPort, mcastAddr, mcastPort)
        self._queue = PriorityQueue(PRIORITY_DISTRIBUTION)

        # Create transmitter and receiver threads
        self._receiver = threading.Thread(target=self._receiverLoop,
                                          name="UDP receiver")
        self._receiver.setDaemon(True)
        self._transmitter = threading.Thread(target=self._transmitterLoop,
                                             name="UDP transmitter")
        self._transmitter.setDaemon(True)
Beispiel #2
0
    def __init__(self, addr, addrRange=-1,
                 transCls=UDPTransceiver,
                 transParams=dict(mcastAddr="224.0.23.12", mcastPort=3671)):
        """
        Set up the ETS stack.

        @param addr: the physical address of this stack (and possibly its sole device)
        """
        super(ETS, self).__init__()
        self._devices = set()
        self._layer2 = set()
        self._addr = IndividualAddress(addr)
        self._addrNum = addrRange
        self._addrAlloc = self._addr
        self._queue = PriorityQueue(PRIORITY_DISTRIBUTION)

        self._scheduler = Scheduler()
        self.setDaemon(True)
        if transCls is None:
            self._tc = None
        else:
            self._tc = transCls(self, **transParams)
Beispiel #3
0
class ETS(threading.Thread):
    """ ETS class

    @ivar _devices: registered devices
    @type _devices: list of L{Device<pyknyx.core.device>}

    @ivar _layer2: registered transports
    @type _devices: list of L{L_DataServiceBase<pyknyx.stack.layer2.l_dataServiceBase>}

    @ivar _running: flag whether ETS has been started
    @type _devices: bool

    raise ETSValueError:
    """
    _running = False

    def __init__(self, addr, addrRange=-1,
                 transCls=UDPTransceiver,
                 transParams=dict(mcastAddr="224.0.23.12", mcastPort=3671)):
        """
        Set up the ETS stack.

        @param addr: the physical address of this stack (and possibly its sole device)
        """
        super(ETS, self).__init__()
        self._devices = set()
        self._layer2 = set()
        self._addr = IndividualAddress(addr)
        self._addrNum = addrRange
        self._addrAlloc = self._addr
        self._queue = PriorityQueue(PRIORITY_DISTRIBUTION)

        self._scheduler = Scheduler()
        self.setDaemon(True)
        if transCls is None:
            self._tc = None
        else:
            self._tc = transCls(self, **transParams)
        #self.addLayer2(self._tc)


    def allocAddress(self):
        """
        Return a new physical address for a device.
        """
        if self._addrNum == -1:
            logger.info("Allocate ETS addr %s to device", self._addr)
            self._addrNum = 0
            return self._addr
        if self._addrNum == 0:
            raise ETSValueError("No free addresses")
        self._addrNum -= 1
        self._addrAlloc += 1
        logger.info("Allocate new ETS addr %s to device", self._addrAlloc)
        return self._addrAlloc

    @property
    def addr(self):
        return self._addr

    @property
    def gadMap(self):
        return self._gadMap

    @property
    def buildingMap(self):
        return self._buildingMap

    def addLayer2(self, layer2):
        self._layer2.add(layer2)
        if self._running:
            layer2.start()

    def register(self, device, buildingMap='root', links=()):
        """
        Register a device

        This method registers pending scheduler/notifier jobs of all FunctionalBlock of the Device.

        @param device: device to register
        @type device: L{Device<pyknyx.core.device>}
        """
        self._devices.add(device)

        for fb in device.fb.values():

            # Register pending scheduler/notifier jobs
            Scheduler().doRegisterJobs(fb)
            Notifier().doRegisterJobs(fb)

        for groupObject, gad in chain(device.lnk,links):
            # Get GroupAddress
            if not isinstance(gad, GroupAddress):
                gad = GroupAddress(gad)

            # Ask the group data service to subscribe this GroupObject to the given gad
            # In return, get the created group
            group = device.stack.agds.subscribe(gad, groupObject)

            # If not already done, set the GroupObject group. This group will be used when the GroupObject wants to
            # communicate on the bus. This mimics the S flag of ETS real application.
            # @todo: find a better way
            if groupObject.group is None:
                groupObject.group = group

        if self._running:
            device.start()

    def putFrame(self, l2, cEMI):
        """
        Add a frame to be processed.

        @param cEMI:
        @type cEMI:
        """
        logger.debug("ETS.putFrame(): cEMI=%s" % cEMI)

        # Get priority from cEMI
        priority = cEMI.priority

        # Add to inQueue and notify inQueue handler
        self._queue.add((l2,cEMI), priority)

    def start(self):
        if self._running:
            return
        self._queue = PriorityQueue(PRIORITY_DISTRIBUTION) # clean start
        super(ETS,self).start()

    def run(self):
        self._running = True
        logger.debug("ETS.run(): starting")
        try:
            for dev in self._layer2:
                dev.start()
            for dev in self._devices:
                dev.start()
            self._scheduler.start()
            while self._running:
                logger.trace("ETS.run(): looping")
                msg = self._queue.remove()
                if msg is None:
                    logger.trace("ETS.run(): exit: None")
                    return
                l2,cEMI = msg
                self.processFrame(l2,cEMI)
            logger.trace("ETS.run(): exit: !_running")
        except Exception:
            logger.exception("ETS main loop")
        finally:
            self._running = False

    def stop(self):
        self._running = False
        self._scheduler.stop()
        self._queue.add(None,Priority('system'))
        for dev in self._devices:
            dev.stop()
        for dev in self._layer2:
            dev.stop()

    def processFrame(self, l2, cEMI):
        """
        Forward the frame @cEMI, received from layer2 device @l2, to all
        other eligible interfaces.
        """

        logger.trace("recv: get %s from %s", cEMI, l2)
        destAddr = cEMI.destinationAddress

        hopCount = cEMI.hopCount
        if hopCount == 7:
            # Refuse to transmit any packet with hopcount=7.
            # They may cause endlessly-looping packets. A multicast storm is
            # bad enough when something is misconfigured.
            cEMI.hopCount = hopCount = 6
            cEMI_b = cEMI
        elif l2.hop:
            # Decrement the hopcount if the packet arrives at one broadcast 
            # Layer2 and then gets re-broadcast. If zero, discard.
            if hopCount:
                cEMI_b = cEMI.copy()
                cEMI_b.hopCount = hopCount-1
            else:
                cEMI_b = None
        else:
            cEMI_b = cEMI
        if isinstance(destAddr, GroupAddress):
            r = 'wantsGroupFrame'
            may_force = False
        elif isinstance(destAddr, IndividualAddress):
            r = 'wantsIndividualFrame'
            may_force = True
        else:
            logger.warning("recv %s: unsupported destination address type (%s)", l2, repr(destAddr))
            return
        done = skipped = False
        for dev in self._layer2:
            if l2 == dev:
                logger.trace("recv: same: %s", l2)
                continue
            cEMI_x = cEMI_b if dev.hop else cEMI
            if not cEMI_x:
                logger.trace("recv: skip: %s", l2)
                skipped = True
            elif getattr(dev,r)(cEMI):
                logger.trace("recv: sent: %s", l2)
                dev.dataInd(cEMI)
                done = True
            else:
                logger.trace("recv: notsent: %s", l2)
        if may_force and not done:
            # We never saw this address. Send to every broadcast device.
            logger.trace("recv: repeat")
            for dev in self._layer2:
                if l2 == dev:
                    continue
                cEMI_x = cEMI_b if dev.hop else cEMI
                if cEMI_x and getattr(dev,r)(cEMI, force=True):
                    done = True
            if not done:
                logger.debug("recv %s: unknown destination address (%s)", repr(destAddr))
        if skipped:
            logger.debug("recv %s: not forwarded (hopcount zero): %s from %s", l2, cEMI)
        elif not done:
            logger.debug("recv %s: not sendable: %s from %s", l2, cEMI)


    def getGrOAT(self, device=None, by="gad", outFormatLevel=3):
        """ Build the Group Object Association Table
        """

        # Retrieve all bound gad
        gads = []
        if device is None:
            devices = self._devices
        else:
            devices = (device,)
        for dev in devices:
            for gad in dev.stack.agds.groups.keys():
                gads.append(GroupAddress(gad, outFormatLevel))
        gads.sort()  #reverse=True)

        output = "\n"

        if by == "gad":
            gadMapTable = GroupAddressTableMapper().table
            title = "%-34s %-30s %-30s %-10s %-10s %-10s" % ("GAD", "Datapoint", "Functional block", "DPTID", "Flags", "Priority")
            output += title
            output += "\n"
            output += len(title) * "-"
            output += "\n"
            gadMain = gadMiddle = gadSub = -1
            for gad in gads:
                if gadMain != gad.main:
                    index = "%d/-/-" % gad.main
                    name = gadMapTable.get(index,{'desc':''})
                    output +=  u"%2d %-33s\n" % (gad.main, name['desc'])
                    gadMain = gad.main
                    gadMiddle = gadSub = -1
                if gadMiddle != gad.middle:
                    index = "%d/%d/-" % (gad.main, gad.middle)
                    name = gadMapTable.get(index,{'desc':''})
                    output +=  u" ├── %2d %-27s\n" % (gad.middle, name['desc'])
                    gadMiddle = gad.middle
                    gadSub = -1
                index = "%d/%d/%d" % (gad.main, gad.middle, gad.sub)
                name = gadMapTable.get(index,{'desc':''})
                output +=  u" │    ├── %3d %-21s" % (gad.sub, name['desc'])
                gadSub = gad.sub

                i = 0
                for dev in devices:
                    groups = dev.stack.agds.groups
                    if gad.address not in groups:
                        continue
                    for go in groups[gad.address].listeners:
                        dp = go.datapoint
                        fb = dp.owner
                        if i:
                            output +=  u" │    │                            "
                        output +=  u"%-30s %-30s %-10s %-10s %-10s\n" % (dp.name, fb.name, dp.dptId, go.flags, go.priority)
                        i += 1

        elif by == "go":

            # Retrieve all groupObjects, not only bound ones
            # @todo: use buildingMap presentation
            mapByDP = {}
            title = "%-29s %-30s %-10s %-30s %-10s %-10s" % ("Functional block", "Datapoint", "DPTID", "GAD", "Flags", "Priority")
            output +=  title
            output += "\n"
            output +=  len(title) * "-"
            output += "\n"
            for dev in devices:
                for fb in dev.fb.values():
                    #output +=  "%-30s" % fb.name
                    for i, go in enumerate(fb.go.values()):
                        output +=  "%-30s" % ("" if i else fb.name)
                        dp = go.datapoint
                        gads_ = set()
                        groups = dev.stack.agds.groups
                        for gad in gads:
                            if gad.address not in groups:
                                continue
                            if go in groups[gad.address].listeners:
                                gads_.add(gad.address)
                        output +=  "%-30s %-10s %-30s %-10s %-10s\n" % (go.name, dp.dptId, ", ".join(gads_), go.flags, go.priority)

        else:
            raise ETSValueError("by param. must be in ('gad', 'dp')")

        return output

    def printGroat(self, *a,**k):
        print(self.getGrOAT(*a,**k))
        
    def mainLoop(self):
        self.start()
        try:
            while True:
                time.sleep(9999)
        except KeyboardInterrupt:
            pass
        finally:
            self._
            self.stop()
Beispiel #4
0
 def start(self):
     if self._running:
         return
     self._queue = PriorityQueue(PRIORITY_DISTRIBUTION) # clean start
     super(ETS,self).start()
Beispiel #5
0
class UDPTransceiver(L_DataServiceBroadcast):
    """ UDPTransceiver class

    @ivar _mcastAddr:
    @type _mcastAddr:

    @ivar _mcastPort:
    @type _mcastPort:

    @ivar _receiver: multicast receiver loop
    @type _receiver: L{Thread<threading>}

    @ivar _transmitter: multicast transmitter loop
    @type _transmitter: L{Thread<threading>}
    """
    def __init__(self, ets, mcastAddr="224.0.23.12", mcastPort=3671):
        """

        @param mcastAddr: multicast address to bind to
        @type mcastAddr: str

        @param mcastPort: multicast port to bind to
        @type mcastPort: str

        raise UDPTransceiverValueError:
        """
        super(UDPTransceiver, self).__init__(ets)

        self._mcastAddr = mcastAddr
        self._mcastPort = mcastPort

        localAddr = "0.0.0.0"
        # socket.gethostbyname(socket.gethostname())
        self._transmitterSock = MulticastSocketTransmit(
            localAddr, 0, mcastAddr, mcastPort)
        self._receiverSock = MulticastSocketReceive(
            localAddr, self._transmitterSock.localPort, mcastAddr, mcastPort)
        self._queue = PriorityQueue(PRIORITY_DISTRIBUTION)

        # Create transmitter and receiver threads
        self._receiver = threading.Thread(target=self._receiverLoop,
                                          name="UDP receiver")
        self._receiver.setDaemon(True)
        self._transmitter = threading.Thread(target=self._transmitterLoop,
                                             name="UDP transmitter")
        self._transmitter.setDaemon(True)

    @property
    def mcastAddr(self):
        return self._mcastAddr

    @property
    def mcastPort(self):
        return self._mcastPort

    @property
    def localAddr(self):
        return self._receiverSock.localAddr

    @property
    def localPort(self):
        return self._receiverSock.localPort

    def _receiverLoop(self):
        """
        """
        logger.trace("UDPTransceiver._receiverLoop()")

        while self._running:
            try:
                inFrame, (fromAddr, fromPort) = self._receiverSock.receive()
                logger.debug(
                    "UDPTransceiver._receiverLoop(): inFrame=%s (%s, %d)" %
                    (repr(inFrame), fromAddr, fromPort))
                if fromAddr == self._transmitterSock.localAddress and fromPort == self._transmitterSock.localPort:
                    continue  # we got our own packet
                inFrame = bytearray(inFrame)
                try:
                    header = KNXnetIPHeader(inFrame)
                except KNXnetIPHeaderValueError:
                    logger.exception("UDPTransceiver._receiverLoop()")
                    continue
                logger.debug(
                    "UDPTransceiver._receiverLoop(): KNXnetIP header=%s" %
                    repr(header))

                frame = inFrame[KNXnetIPHeader.HEADER_SIZE:]
                logger.debug("UDPTransceiver._receiverLoop(): frame=%s" %
                             repr(frame))
                try:
                    cEMI = CEMILData(frame)
                except CEMIValueError:
                    logger.exception("UDPTransceiver._receiverLoop()")
                    continue
                logger.debug("UDPTransceiver._receiverLoop(): cEMI=%s" % cEMI)

                self.dataReq(cEMI)

            except socket.timeout:
                pass

            except:
                logger.exception("UDPTransceiver._receiverLoop()")

        logger.trace("UDPTransceiver._receiverLoop(): ended")

    def dataInd(self, cEMI):
        self._queue.add(cEMI, cEMI.priority)

    def _transmitterLoop(self):
        """
        """
        logger.trace("UDPTransceiver._transmitterLoop()")

        while self._running:
            try:
                cEMI = self._queue.remove()
                if cEMI is None:
                    return

                logger.debug("UDPTransceiver._transmitterLoop(): frame=%s" %
                             repr(cEMI))

                cEMIFrame = cEMI.frame
                cEMIRawFrame = cEMIFrame.raw
                header = KNXnetIPHeader(service=KNXnetIPHeader.ROUTING_IND,
                                        serviceLength=len(cEMIRawFrame))
                frame = header.frame + cEMIRawFrame
                logger.debug("UDPTransceiver._transmitterLoop(): frame= %s" %
                             repr(frame))

                self._transmitterSock.transmit(frame)

            except Exception:
                logger.exception("UDPTransceiver._transmitterLoop()")

        logger.trace("UDPTransceiver._transmitterLoop(): ended")

    def start(self):
        """
        """
        logger.trace("UDPTransceiver.start()")

        self._running = True
        self._receiver.start()
        self._transmitter.start()

    def stop(self):
        """
        """
        logger.trace("UDPTransceiver.stop()")

        self._running = False
        self._queue.add(None, Priority('system'))
        self._transmitterSock.close()
        self._receiverSock.close()