Skip to content

Commit

Permalink
Merge pull request #157 from ChristianTremblay/release_20190908
Browse files Browse the repository at this point in the history
Release 19.9.8
  • Loading branch information
ChristianTremblay committed Sep 9, 2019
2 parents 39bbd82 + 0c4df6d commit b6af631
Show file tree
Hide file tree
Showing 53 changed files with 2,830 additions and 1,140 deletions.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,10 @@ __pycache__
/tests/archive
/BAC0/web/test
/venv/
obj300.db
BAC0.bin
BAC0.db
obj30.bin
obj30.db
obj300.bin
/.vscode
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ python:
- '3.4'
- '3.5'
- '3.6'
- '3.7'
bundler_args: "--retry 3"
install:
- pip install -r requirements.txt
Expand All @@ -24,6 +25,6 @@ deploy:
secure: Wgb3PCFut7RCbajytFP4IchYZ1ghA6UH0qjLtBm5qdCYesQKipo3p4Pm5N2IlyOfen1RGd0igaksQqHj19bpwoMXvvR6Rb/Q/eqON+mUWtN1BDPJFh60bWfyJ9c3ZsrCmkVChIQxOPwmyUTX23LV+1Z4nuRdlktdzIDbBZn847BesVylHP9gQ7CKZb3Fxr69ZeOtMbTwAyfK1BbMcFjXMQQGzl4W0R9CuFH7eDa8Ih/fUsvUupDNDKwl6hyKNWwtzDu/DPP0TDBgQexjntIo+MkByatmXyGCoYuHzIrjIMKHCxHGtciCnD6cNGT9meUkwjiCHITfRKCdCAZmwfCeVJuB/AsjgMj4M/dA6kMklqs4A+DEqbw+JiYxMSP1VZL5Sy864Api+7Rhm7tQNNPzjQywSlCjwKexEmSdbKy3M0A7OkORzFFsWDE5WUG1OjR8iJGnhKINmAjfvPhlZCjf/9Dgsd5EA735WiUmhTjj7Vzu3MTtMOzg95UtdDVekqZHf4Yjk6cjhqJQxKM0GZSomAdTZhpYiFFMXgpiup9hUmRujPvGMOci/vydi3rWEe4QARGdDZb/6spydyCy/9sw1O+D93hzLq09fSm3IWN/awdj0dkbhKbHSfH+Gq/436DgRdG/5GlevL3u8b/QARGu2bAXzNJJIuBcGcesTzLch30=
on:
branch: master
python: '3.6'
python: '3.7'
distributions: sdist bdist_wheel
repo: ChristianTremblay/BAC0
3 changes: 3 additions & 0 deletions BAC0/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@
lite = connect
# print('All features not available as some modules are missing (flask, flask-bootstrap, bokeh, pandas). See docs for details')

# Import proprietary classes
from .core.proprietary_objects import jci

except ImportError as error:
print("=" * 80)
print(
Expand Down
214 changes: 158 additions & 56 deletions BAC0/core/app/ScriptApplication.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,32 @@
from collections import defaultdict

# --- 3rd party modules ---
from bacpypes.app import BIPSimpleApplication, BIPForeignApplication
from bacpypes.app import ApplicationIOController
from bacpypes.pdu import Address
from bacpypes.service.object import ReadWritePropertyMultipleServices
from bacpypes.netservice import NetworkServiceAccessPoint, NetworkServiceElement
from bacpypes.bvllservice import BIPSimple, BIPForeign, AnnexJCodec, UDPMultiplexer
from bacpypes.appservice import StateMachineAccessPoint, ApplicationServiceAccessPoint
from bacpypes.comm import ApplicationServiceElement, bind

# basic services
from bacpypes.service.device import WhoIsIAmServices
from bacpypes.service.object import ReadWritePropertyServices

# --- this application's modules ---
from ..utils.notes import note_and_log
from ..functions.Discover import NetworkServiceElementWithRequests

# ------------------------------------------------------------------------------


@note_and_log
class SimpleApplication(BIPSimpleApplication, ReadWritePropertyMultipleServices):
class BAC0Application(
ApplicationIOController,
WhoIsIAmServices,
ReadWritePropertyServices,
ReadWritePropertyMultipleServices,
):
"""
Defines a basic BACnet/IP application to process BACnet requests.
Expand All @@ -40,29 +54,78 @@ class SimpleApplication(BIPSimpleApplication, ReadWritePropertyMultipleServices)
"""

def __init__(self, *args, bbmdAddress=None, bbmdTTL=0):
self.localAddress = None
def __init__(
self,
localDevice,
localAddress,
bbmdAddress=None,
bbmdTTL=0,
deviceInfoCache=None,
aseID=None,
):

ApplicationIOController.__init__(
self, localDevice, deviceInfoCache, aseID=aseID
)

# local address might be useful for subclasses
if isinstance(localAddress, Address):
self.localAddress = localAddress
else:
self.localAddress = Address(localAddress)

try:
super().__init__(*args)
except OSError:
raise
# include a application decoder
self.asap = ApplicationServiceAccessPoint()

self._request = None
# 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)

if isinstance(self.localAddress, Address):
self.local_unicast_tuple = self.localAddress.addrTuple
self.local_broadcast_tuple = self.localAddress.addrBroadcastTuple
else:
self.local_unicast_tuple = ("", 47808)
self.local_broadcast_tuple = ("255.255.255.255", 47808)
# 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("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."""
self.log(("do_WhoIsRequest {!r}".format(apdu)))
self.log("do_WhoIsRequest {!r}".format(apdu))

# build a key from the source and parameters
key = (
Expand All @@ -74,24 +137,24 @@ def do_WhoIsRequest(self, apdu):
# count the times this has been received
self.who_is_counter[key] += 1

# continue with the default implementation
BIPSimpleApplication.do_WhoIsRequest(self, apdu)
def close_socket(self):
# pass to the multiplexer, then down to the sockets
self.mux.close_socket()

def do_IAmRequest(self, apdu):
"""Given an I-Am request, cache it."""
self.log(("do_IAmRequest {!r}".format(apdu)))
def request(self, apdu):
# save a copy of the request
self._request = 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

# continue with the default implementation
BIPSimpleApplication.do_IAmRequest(self, apdu)
# forward it along
super(BAC0Application, self).request(apdu)


@note_and_log
class ForeignDeviceApplication(
BIPForeignApplication, ReadWritePropertyMultipleServices
class BAC0ForeignDeviceApplication(
ApplicationIOController,
WhoIsIAmServices,
ReadWritePropertyServices,
ReadWritePropertyMultipleServices,
):
"""
Defines a basic BACnet/IP application to process BACnet requests.
Expand All @@ -101,29 +164,78 @@ class ForeignDeviceApplication(
"""

def __init__(self, *args, bbmdAddress=None, bbmdTTL=0):
self.localAddress = None
def __init__(
self,
localDevice,
localAddress,
bbmdAddress=None,
bbmdTTL=0,
deviceInfoCache=None,
aseID=None,
):

ApplicationIOController.__init__(
self, localDevice, deviceInfoCache, aseID=aseID
)

# local address might be useful for subclasses
if isinstance(localAddress, Address):
self.localAddress = localAddress
else:
self.localAddress = Address(localAddress)

try:
super().__init__(*args, bbmdAddress=bbmdAddress, bbmdTTL=bbmdTTL)
except OSError:
raise
# include a application decoder
self.asap = ApplicationServiceAccessPoint()

self._request = None
# 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 = BIPForeign(bbmdAddress, bbmdTTL)
self.annexj = AnnexJCodec()
self.mux = UDPMultiplexer(self.localAddress, noBroadcast=True)

# bind the bottom layers
bind(self.bip, self.annexj, self.mux.annexJ)

# bind the NSAP to the stack, no network number
self.nsap.bind(self.bip)

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 = []

if isinstance(self.localAddress, Address):
self.local_unicast_tuple = self.localAddress.addrTuple
self.local_broadcast_tuple = self.localAddress.addrBroadcastTuple
else:
self.local_unicast_tuple = ("", 47808)
self.local_broadcast_tuple = ("255.255.255.255", 47808)
def do_IAmRequest(self, apdu):
"""Given an I-Am request, cache it."""
self.log("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)
# continue with the default implementation

def do_WhoIsRequest(self, apdu):
"""Respond to a Who-Is request."""
self.log(("do_WhoIsRequest {!r}".format(apdu)))
self.log("do_WhoIsRequest {!r}".format(apdu))

# build a key from the source and parameters
key = (
Expand All @@ -135,16 +247,6 @@ def do_WhoIsRequest(self, apdu):
# count the times this has been received
self.who_is_counter[key] += 1

# continue with the default implementation
BIPSimpleApplication.do_WhoIsRequest(self, apdu)

def do_IAmRequest(self, apdu):
"""Given an I-Am request, cache it."""
self.log(("do_IAmRequest %r", 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

# continue with the default implementation
BIPSimpleApplication.do_IAmRequest(self, apdu)
def close_socket(self):
# pass to the multiplexer, then down to the sockets
self.mux.close_socket()
Loading

0 comments on commit b6af631

Please sign in to comment.