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)
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)
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()
def start(self): if self._running: return self._queue = PriorityQueue(PRIORITY_DISTRIBUTION) # clean start super(ETS,self).start()
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()