Skip to content

Commit

Permalink
Merge b0d5365 into f823265
Browse files Browse the repository at this point in the history
  • Loading branch information
anthonydlanza committed Aug 27, 2020
2 parents f823265 + b0d5365 commit 26c8517
Show file tree
Hide file tree
Showing 29 changed files with 804 additions and 302 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,7 @@ obj30.bin
obj30.db
obj300.bin
/.vscode
*.bin
*.db
*.log
wily-report.html
254 changes: 178 additions & 76 deletions BAC0/core/app/ScriptApplication.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,20 @@
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.bvllservice import (
BIPSimple,
BIPForeign,
BIPBBMD,
AnnexJCodec,
UDPMultiplexer,
)
from bacpypes.appservice import StateMachineAccessPoint, ApplicationServiceAccessPoint
from bacpypes.comm import ApplicationServiceElement, bind
from bacpypes.comm import ApplicationServiceElement, bind, Client
from bacpypes.iocb import IOCB
from bacpypes.core import deferred

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

# --- this application's modules ---
Expand All @@ -41,10 +47,63 @@
# ------------------------------------------------------------------------------


class common_mixin:
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_IHaveRequest(self, apdu):
"""Given an I-Have request, cache it."""
self._log.debug("do_IHaveRequest {!r}".format(apdu))

# build a key from the source, using object name
key = (str(apdu.pduSource), apdu.objectName)
self.i_have_counter[key] += 1
self._last_i_have_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 and self.localDevice.objectIdentifier[1] < low_limit:
return
if high_limit is not None and 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)


@note_and_log
class BAC0Application(
common_mixin,
ApplicationIOController,
WhoIsIAmServices,
WhoHasIHaveServices,
ReadWritePropertyServices,
ReadWritePropertyMultipleServices,
):
Expand Down Expand Up @@ -112,53 +171,13 @@ def __init__(
self.nsap.bind(self.bip, address=self.localAddress)

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

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)
self._last_i_have_received = []

def close_socket(self):
# pass to the multiplexer, then down to the sockets
Expand All @@ -174,8 +193,10 @@ def request(self, apdu):

@note_and_log
class BAC0ForeignDeviceApplication(
common_mixin,
ApplicationIOController,
WhoIsIAmServices,
WhoHasIHaveServices,
ReadWritePropertyServices,
ReadWritePropertyMultipleServices,
):
Expand Down Expand Up @@ -243,48 +264,129 @@ def __init__(
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 = []

def do_IAmRequest(self, apdu):
"""Given an I-Am request, cache it."""
self._log.debug("do_IAmRequest {!r}".format(apdu))
def close_socket(self):
# pass to the multiplexer, then down to the sockets
self.mux.close_socket()

# 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.debug("do_WhoIsRequest {!r}".format(apdu))
class NullClient(Client):
def __init__(self, cid=None):
Client.__init__(self, cid=cid)

# build a key from the source and parameters
key = (
str(apdu.pduSource),
apdu.deviceInstanceRangeLowLimit,
apdu.deviceInstanceRangeHighLimit,
def confirmation(self, *args, **kwargs):
pass


@note_and_log
class BAC0BBMDDeviceApplication(
common_mixin,
ApplicationIOController,
WhoIsIAmServices,
WhoHasIHaveServices,
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.
"""

bdt = []

def __init__(
self,
localDevice,
localAddress,
bdtable=[],
deviceInfoCache=None,
aseID=None,
iam_req=None,
):

self.bdtable = bdtable

null_client = NullClient()

ApplicationIOController.__init__(
self, localDevice, deviceInfoCache, aseID=aseID
)
low_limit = key[1]
high_limit = key[2]

# count the times this has been received
self.who_is_counter[key] += 1
self.iam_req = iam_req
# local address might be useful for subclasses
if isinstance(localAddress, Address):
self.localAddress = localAddress
else:
self.localAddress = Address(localAddress)

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

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
Expand Down
23 changes: 11 additions & 12 deletions BAC0/core/devices/Device.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
SegmentationNotSupported,
BadDeviceDefinition,
RemovedPointException,
WritePropertyException,
)

# from ...bokeh.BokehRenderer import BokehPlot
Expand Down Expand Up @@ -141,10 +142,7 @@ def __init__(
self.properties.device_id = device_id
self.properties.network = network
self.properties.pollDelay = poll
if poll < 10:
self.properties.fast_polling = True
else:
self.properties.fast_polling = False
self.properties.fast_polling = True if poll < 10 else False
self.properties.name = ""
self.properties.vendor_id = 0
self.properties.objects_list = []
Expand Down Expand Up @@ -186,7 +184,7 @@ def __init__(
if (
self.properties.network
and self.properties.address
and self.properties.device_id
and self.properties.device_id is not None
):
self.new_state(DeviceDisconnected)
else:
Expand Down Expand Up @@ -358,9 +356,11 @@ def find_point(self, objectType, objectAddress):
Find point based on type and address
"""
for point in self.points:
if point.properties.type == objectType:
if float(point.properties.address) == objectAddress:
return point
if (
point.properties.type == objectType
and float(point.properties.address) == objectAddress
):
return point
raise ValueError(
"{} {} doesn't exist in controller".format(objectType, objectAddress)
)
Expand Down Expand Up @@ -568,8 +568,7 @@ def __getitem__(self, point_name):
self._log.error("{}".format(ve))

def __iter__(self):
for each in self.points:
yield each
yield from self.points

def __contains__(self, value):
"""
Expand All @@ -590,7 +589,7 @@ def __setitem__(self, point_name, value):
"""
try:
self._findPoint(point_name)._set(value)
except ValueError as ve:
except WritePropertyException as ve:
self._log.error("{}".format(ve))

def __len__(self):
Expand Down Expand Up @@ -974,7 +973,7 @@ def connect(self, *, network=None, from_backup=None):
except NoResponseFromController:
self._log.error("Unable to connect, keeping DB mode active")

elif from_backup or not network:
else:
self._log.debug("Not connected, open DB")
if from_backup:
self.properties.db_name = from_backup.split(".")[0]
Expand Down

0 comments on commit 26c8517

Please sign in to comment.