class DiscoverApplication(ApplicationIOController, WhoIsIAmServices, ReadWritePropertyServices): def __init__(self, localDevice, localAddress, deviceInfoCache=None, aseID=None): if _debug: DiscoverApplication._debug( "__init__ %r %r deviceInfoCache=%r aseID=%r", localDevice, localAddress, deviceInfoCache, aseID) ApplicationIOController.__init__(self, localDevice, localAddress, deviceInfoCache, aseID=aseID) # local address might be useful for subclasses if isinstance(localAddress, Address): self.localAddress = localAddress else: self.localAddress = Address(localAddress) # include a application decoder self.asap = ApplicationServiceAccessPoint() # pass the device object to the state machine access point so it # can know if it should support segmentation self.smap = StateMachineAccessPoint(localDevice) # the segmentation state machines need access to the same device # information cache as the application self.smap.deviceInfoCache = self.deviceInfoCache # a network service access point will be needed self.nsap = NetworkServiceAccessPoint() # give the NSAP a generic network layer service element self.nse = DiscoverNetworkServiceElement() bind(self.nse, self.nsap) # bind the top layers bind(self, self.asap, self.smap, self.nsap) # create a generic BIP stack, bound to the Annex J server # on the UDP multiplexer self.bip = BIPSimple() self.annexj = AnnexJCodec() self.mux = UDPMultiplexer(self.localAddress) # bind the bottom layers bind(self.bip, self.annexj, self.mux.annexJ) # bind the BIP stack to the network, no network number self.nsap.bind(self.bip, address=self.localAddress) # keep track of requests to line up responses self._request = None def close_socket(self): if _debug: DiscoverApplication._debug("close_socket") # pass to the multiplexer, then down to the sockets self.mux.close_socket() def request(self, apdu): if _debug: DiscoverApplication._debug("request %r", apdu) # save a copy of the request self._request = apdu # forward it along super(DiscoverApplication, self).request(apdu) def indication(self, apdu): if _debug: DiscoverApplication._debug("indication %r", apdu) # forward it along super(DiscoverApplication, self).indication(apdu) def response(self, apdu): if _debug: DiscoverApplication._debug("response %r", apdu) # forward it along super(DiscoverApplication, self).response(apdu) def confirmation(self, apdu): if _debug: DiscoverApplication._debug("confirmation %r", apdu) # forward it along super(DiscoverApplication, self).confirmation(apdu) def do_IAmRequest(self, apdu): if _debug: DiscoverApplication._debug("do_IAmRequest %r", apdu) if not isinstance(self._request, WhoIsRequest): if _debug: DiscoverApplication._debug(" - no pending who-is") return device_instance = apdu.iAmDeviceIdentifier[1] if (self._request.deviceInstanceRangeLowLimit is not None) and \ (device_instance < self._request.deviceInstanceRangeLowLimit): return if (self._request.deviceInstanceRangeHighLimit is not None) and \ (device_instance > self._request.deviceInstanceRangeHighLimit): return # print out something if interactive: print("{} @ {}".format(device_instance, apdu.pduSource)) # update the snapshot database snapshot.upsert( apdu.iAmDeviceIdentifier[1], '-', 'address', str(apdu.pduSource), ) snapshot.upsert( apdu.iAmDeviceIdentifier[1], '-', 'maxAPDULengthAccepted', str(apdu.maxAPDULengthAccepted), ) snapshot.upsert( apdu.iAmDeviceIdentifier[1], '-', 'segmentationSupported', apdu.segmentationSupported, )
class BAC0Application( ApplicationIOController, WhoIsIAmServices, ReadWritePropertyServices, ReadWritePropertyMultipleServices, ): """ Defines a basic BACnet/IP application to process BACnet requests. :param *args: local object device, local IP address See BAC0.scripts.BasicScript for more details. """ def __init__( self, localDevice, localAddress, bbmdAddress=None, bbmdTTL=0, deviceInfoCache=None, aseID=None, iam_req=None, ): ApplicationIOController.__init__(self, localDevice, deviceInfoCache, aseID=aseID) self.iam_req = iam_req # local address might be useful for subclasses if isinstance(localAddress, Address): self.localAddress = localAddress else: self.localAddress = Address(localAddress) # include a application decoder self.asap = ApplicationServiceAccessPoint() # pass the device object to the state machine access point so it # can know if it should support segmentation self.smap = StateMachineAccessPoint(localDevice) # the segmentation state machines need access to the same device # information cache as the application self.smap.deviceInfoCache = self.deviceInfoCache # a network service access point will be needed self.nsap = NetworkServiceAccessPoint() # give the NSAP a generic network layer service element self.nse = NetworkServiceElementWithRequests() bind(self.nse, self.nsap) # bind the top layers bind(self, self.asap, self.smap, self.nsap) # create a generic BIP stack, bound to the Annex J server # on the UDP multiplexer self.bip = BIPSimple() self.annexj = AnnexJCodec() self.mux = UDPMultiplexer(self.localAddress) # bind the bottom layers bind(self.bip, self.annexj, self.mux.annexJ) # bind the BIP stack to the network, no network number self.nsap.bind(self.bip, address=self.localAddress) self.i_am_counter = defaultdict(int) self.who_is_counter = defaultdict(int) # keep track of requests to line up responses self._request = None self._last_i_am_received = [] def do_IAmRequest(self, apdu): """Given an I-Am request, cache it.""" self._log.debug("do_IAmRequest {!r}".format(apdu)) # build a key from the source, just use the instance number key = (str(apdu.pduSource), apdu.iAmDeviceIdentifier[1]) self.i_am_counter[key] += 1 self._last_i_am_received.append(key) def do_WhoIsRequest(self, apdu): """Respond to a Who-Is request.""" # build a key from the source and parameters key = ( str(apdu.pduSource), apdu.deviceInstanceRangeLowLimit, apdu.deviceInstanceRangeHighLimit, ) self._log.debug("do_WhoIsRequest from {} | {} to {}".format( key[0], key[1], key[2])) # count the times this has been received self.who_is_counter[key] += 1 low_limit = key[1] high_limit = key[2] # count the times this has been received self.who_is_counter[key] += 1 if low_limit is not None: if self.localDevice.objectIdentifier[1] < low_limit: return if high_limit is not None: if self.localDevice.objectIdentifier[1] > high_limit: return # generate an I-Am self._log.debug("Responding to Who is by a Iam") self.iam_req.pduDestination = apdu.pduSource iocb = IOCB(self.iam_req) # make an IOCB deferred(self.request_io, iocb) def close_socket(self): # pass to the multiplexer, then down to the sockets self.mux.close_socket() def request(self, apdu): # save a copy of the request self._request = apdu # forward it along super(BAC0Application, self).request(apdu)
class BAC0BBMDDeviceApplication( common_mixin, ApplicationIOController, WhoIsIAmServices, WhoHasIHaveServices, ReadWritePropertyServices, ReadWritePropertyMultipleServices, ChangeOfValueServices, ): """ Defines a basic BACnet/IP application to process BACnet requests. :param *args: local object device, local IP address See BAC0.scripts.BasicScript for more details. """ bdt = [] def __init__( self, localDevice, localAddress, bdtable=[], deviceInfoCache=None, aseID=None, iam_req=None, subscription_contexts=None, ): self.bdtable = bdtable null_client = NullClient() ApplicationIOController.__init__( self, localDevice, deviceInfoCache, aseID=aseID ) self.iam_req = iam_req # local address might be useful for subclasses if isinstance(localAddress, Address): self.localAddress = localAddress else: self.localAddress = Address(localAddress) # include a application decoder self.asap = ApplicationServiceAccessPoint() # pass the device object to the state machine access point so it # can know if it should support segmentation self.smap = StateMachineAccessPoint(localDevice) # the segmentation state machines need access to the same device # information cache as the application self.smap.deviceInfoCache = self.deviceInfoCache # a network service access point will be needed self.nsap = NetworkServiceAccessPoint() # give the NSAP a generic network layer service element self.nse = NetworkServiceElementWithRequests() bind(self.nse, self.nsap) # bind the top layers bind(self, self.asap, self.smap, self.nsap) # create a generic BIP stack, bound to the Annex J server # on the UDP multiplexer self.bip = BIPBBMD(self.localAddress) self.annexj = AnnexJCodec() self.mux = UDPMultiplexer(self.localAddress, noBroadcast=True) # bind the bottom layers # bind(self.bip, self.annexj, self.mux.annexJ) bind(null_client, self.bip, self.annexj, self.mux.annexJ) if self.bdtable: for bdtentry in self.bdtable: self.add_peer(bdtentry) # bind the NSAP to the stack, no network number self.nsap.bind(self.bip) self.i_am_counter = defaultdict(int) self.i_have_counter = defaultdict(int) self.who_is_counter = defaultdict(int) # keep track of requests to line up responses self._request = None self._last_i_am_received = [] self._last_i_have_received = [] # to support CoV self.subscription_contexts = subscription_contexts def add_peer(self, address): try: bdt_address = Address(address) self.bip.add_peer(bdt_address) except Exception: raise def remove_peer(self, address): try: bdt_address = Address(address) self.bip.remove_peer(bdt_address) except Exception: raise def close_socket(self): # pass to the multiplexer, then down to the sockets self.mux.close_socket()