diff --git a/BAC0/core/app/ScriptApplication.py b/BAC0/core/app/ScriptApplication.py index 8ab45b37..e653fdfd 100644 --- a/BAC0/core/app/ScriptApplication.py +++ b/BAC0/core/app/ScriptApplication.py @@ -27,6 +27,8 @@ from bacpypes.bvllservice import BIPSimple, BIPForeign, AnnexJCodec, UDPMultiplexer from bacpypes.appservice import StateMachineAccessPoint, ApplicationServiceAccessPoint from bacpypes.comm import ApplicationServiceElement, bind +from bacpypes.iocb import IOCB +from bacpypes.core import deferred # basic services from bacpypes.service.device import WhoIsIAmServices @@ -62,12 +64,14 @@ def __init__( 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 @@ -116,7 +120,7 @@ def __init__( def do_IAmRequest(self, apdu): """Given an I-Am request, cache it.""" - self.log("do_IAmRequest {!r}".format(apdu)) + 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]) @@ -125,7 +129,7 @@ def do_IAmRequest(self, apdu): def do_WhoIsRequest(self, apdu): """Respond to a Who-Is request.""" - self.log("do_WhoIsRequest {!r}".format(apdu)) + self._log.debug("do_WhoIsRequest {!r}".format(apdu)) # build a key from the source and parameters key = ( @@ -136,6 +140,23 @@ def do_WhoIsRequest(self, apdu): # 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.info("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 @@ -172,12 +193,14 @@ def __init__( 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 @@ -225,7 +248,7 @@ def __init__( def do_IAmRequest(self, apdu): """Given an I-Am request, cache it.""" - self.log("do_IAmRequest {!r}".format(apdu)) + 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]) @@ -235,7 +258,7 @@ def do_IAmRequest(self, apdu): def do_WhoIsRequest(self, apdu): """Respond to a Who-Is request.""" - self.log("do_WhoIsRequest {!r}".format(apdu)) + self._log.debug("do_WhoIsRequest {!r}".format(apdu)) # build a key from the source and parameters key = ( @@ -243,10 +266,24 @@ def do_WhoIsRequest(self, apdu): apdu.deviceInstanceRangeLowLimit, apdu.deviceInstanceRangeHighLimit, ) + 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() diff --git a/BAC0/core/functions/Discover.py b/BAC0/core/functions/Discover.py index 4ee754f4..36730c58 100644 --- a/BAC0/core/functions/Discover.py +++ b/BAC0/core/functions/Discover.py @@ -226,7 +226,32 @@ def whois(self, *args, global_broadcast=False): self.discoveredDevices = self.this_application.i_am_counter return self.this_application._last_i_am_received - def iam(self): + def _iam_request(self, dest=None): + """ + Build the IOCB request for a I Am + """ + try: + # build a response + request = IAmRequest() + if dest: + request.pduDestination = dest + else: + request.pduDestination = GlobalBroadcast() + + # fill the response with details about us (from our device object) + request.iAmDeviceIdentifier = self.this_device.objectIdentifier + request.maxAPDULengthAccepted = self.this_device.maxApduLengthAccepted + request.segmentationSupported = self.this_device.segmentationSupported + request.vendorID = self.this_device.vendorIdentifier + self._log.debug("{:>12} {}".format("- request:", request)) + + return request + + except Exception as error: + self._log.error("exception: {!r}".format(error)) + raise + + def iam(self, dest=None): """ Build an IAm response. IAm are sent in response to a WhoIs request that; matches our device ID, whose device range includes us, or is a broadcast. @@ -243,16 +268,7 @@ def iam(self): try: # build a response - request = IAmRequest() - request.pduDestination = GlobalBroadcast() - - # fill the response with details about us (from our device object) - request.iAmDeviceIdentifier = self.this_device.objectIdentifier - request.maxAPDULengthAccepted = self.this_device.maxApduLengthAccepted - request.segmentationSupported = self.this_device.segmentationSupported - request.vendorID = self.this_device.vendorIdentifier - self._log.debug("{:>12} {}".format("- request:", request)) - + request = self._iam_request(dest=dest) iocb = IOCB(request) # make an IOCB deferred(self.this_application.request_io, iocb) iocb.wait() diff --git a/BAC0/infos.py b/BAC0/infos.py index 8355d9ee..97ff4e0b 100644 --- a/BAC0/infos.py +++ b/BAC0/infos.py @@ -12,5 +12,5 @@ __email__ = "christian.tremblay@servisys.com" __url__ = "https://github.com/ChristianTremblay/BAC0" __download_url__ = "https://github.com/ChristianTremblay/BAC0/archive/master.zip" -__version__ = "19.9.8" +__version__ = "19.9.9dev1" __license__ = "LGPLv3" diff --git a/BAC0/scripts/Base.py b/BAC0/scripts/Base.py index 1926999d..5cf4a6c9 100644 --- a/BAC0/scripts/Base.py +++ b/BAC0/scripts/Base.py @@ -172,11 +172,12 @@ def startApp(self): self.localIPAddr, bbmdAddress=self.bbmdAddress, bbmdTTL=self.bbmdTTL, + iam_req=self._iam_request(), ) app_type = "Foreign Device" else: self.this_application = BAC0Application( - self.this_device, self.localIPAddr + self.this_device, self.localIPAddr, iam_req=self._iam_request() ) app_type = "Simple BACnet/IP App" self._log.debug("Starting")