Skip to content

Commit

Permalink
Merge pull request #30 from ChristianTremblay/release_0_99_82
Browse files Browse the repository at this point in the history
Release 0 99 82
  • Loading branch information
ChristianTremblay authored Jan 25, 2017
2 parents 8799fc3 + 32a0807 commit cae83a4
Show file tree
Hide file tree
Showing 19 changed files with 505 additions and 536 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ install:
- pip install coveralls
- pip install pytest
- pip install pytest-cov
- pip install nose
script:
- nosetests --with-coverage --cover-package=BAC0 -v
- coverage run --source BAC0 -m pytest -v
- coverage report
after_success: coveralls
deploy:
provider: pypi
Expand Down
4 changes: 2 additions & 2 deletions BAC0/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
from .tasks.Match import Match as match
from .bokeh.BokehRenderer import BokehPlot as chart
from .infos import __version__ as version
except ImportError:
pass
except ImportError as error:
print(error)
# Not installed yet
34 changes: 18 additions & 16 deletions BAC0/bokeh/BokehRenderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,26 +242,28 @@ def update_data(self):
df = self.read_lst()
for renderer in self.p.renderers:
name = renderer.name
if name in self.points_list:
glyph_renderer = renderer
glyph_renderer = renderer
new_data = {}
if name in self.points_list:
df['name'] = ('%s / %s' % (name, self.device[name]['description']))
glyph_renderer.data_source.data['x'] = df['index']
glyph_renderer.data_source.data['y'] = df[name]
glyph_renderer.data_source.data['desc'] = df['name']
glyph_renderer.data_source.data['time'] = df['time_s']
new_data['x'] = df['index']
new_data['y'] = df[name]
new_data['desc'] = df['name']
new_data['time'] = df['time_s']
if name in self.multi_states:
glyph_renderer.data_source.data['units'] = [self.multi_states[name][int(math.fabs(x-1))] for x in df[name]]
new_data['units'] = [self.multi_states[name][int(math.fabs(x-1))] for x in df[name]]
elif name in self.binary_states:
glyph_renderer.data_source.data['y'] = df[name]
glyph_renderer.data_source.data['units'] = [self.binary_states[name][int(x/1)] for x in df[name]]
new_data['y'] = df[name]
new_data['units'] = [self.binary_states[name][int(x/1)] for x in df[name]]
else:
df['units'] = self.analog_units[name]
glyph_renderer.data_source.data['units'] = df['units']
new_data['units'] = df['units']
glyph_renderer.data_source.data = new_data
elif name == 'Notes':
notes_df = self.read_notes()
glyph_renderer = renderer
glyph_renderer.data_source.data['x'] = notes_df['index']
glyph_renderer.data_source.data['y'] = notes_df['value']
glyph_renderer.data_source.data['desc'] = notes_df['desc']
glyph_renderer.data_source.data['units'] = notes_df[0]
glyph_renderer.data_source.data['time'] = notes_df['time_s']
new_data['x'] = notes_df['index']
new_data['y'] = notes_df['value']
new_data['desc'] = notes_df['desc']
new_data['units'] = notes_df[0]
new_data['time'] = notes_df['time_s']
glyph_renderer.data_source.data = new_data
2 changes: 1 addition & 1 deletion BAC0/bokeh/BokehServer.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def process(self):

def startServer(self):
if 'win32' in sys.platform:
commandToExecute = "bokeh.bat serve"
commandToExecute = "bokeh serve"
else:
commandToExecute = "bokeh serve"
cmdargs = shlex.split(commandToExecute)
Expand Down
277 changes: 28 additions & 249 deletions BAC0/core/app/ScriptApplication.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,12 @@
from bacpypes.debugging import bacpypes_debugging

from bacpypes.app import BIPSimpleApplication
from bacpypes.object import get_datatype
from bacpypes.apdu import Error, AbortPDU, SimpleAckPDU, ReadPropertyRequest, \
ReadPropertyACK, ReadPropertyMultipleRequest, ReadPropertyMultipleACK, \
IAmRequest, WhoIsRequest, AbortReason
from bacpypes.primitivedata import Unsigned
from bacpypes.constructeddata import Array
from bacpypes.pdu import Address
from bacpypes.object import PropertyError
from bacpypes.basetypes import ErrorCode

from collections import defaultdict
from threading import Event, Lock
from queue import Queue
import logging

from ..io.IOExceptions import WriteAccessDenied, NoResponseFromController, SegmentationNotSupported, APDUError

# some debugging
_DEBUG = 0

from ..functions.debug import log_debug

@bacpypes_debugging
class ScriptApplication(BIPSimpleApplication):
Expand All @@ -60,260 +46,53 @@ def __init__(self, *args):
See BAC0.scripts.BasicScript for more details.
"""
logging.getLogger("comtypes").setLevel(logging.INFO)
log_debug("__init__ %r", args)

self.localAddress = None

BIPSimpleApplication.__init__(self, *args)

# _lock will be used by read/write operation to wait for answer before
# making another request
self._lock = Lock()
super().__init__(*args)

self._request = None
self.value = None
self.error = None
self.values = []

self.i_am_counter = defaultdict(int)
self.who_is_counter = defaultdict(int)
self.ResponseQueue = Queue()

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 request(self, apdu):
"""
Reset class variables to None for the present request
Sends the apdu to the application request
:param apdu: apdu
"""
log_debug("request %r", apdu)

# save a copy of the request
self._request = apdu
self.value = None
self.error = None
self.values = []

# Will store responses to IAm and Whois
self.i_am_counter = defaultdict(int)
self.who_is_counter = defaultdict(int)

# forward it along
BIPSimpleApplication.request(self, apdu)

def indication(self, apdu):
"""
Indication will treat unconfirmed messages on the stack
:param apdu: apdu
"""
log_debug("do_IAmRequest %r", apdu)

if _DEBUG:
if apdu.pduSource == self.local_unicast_tuple[0]:
log_debug("indication:received broadcast from self\n")
else:
log_debug("indication:received broadcast from %s (local:%s|source:%s)\n" %
(apdu.pduSource, self.local_unicast_tuple, apdu.pduSource))
else:
log_debug("cannot test broadcast")

# Given an I-Am request, cache it.
if isinstance(apdu, IAmRequest):
# build a key from the source, just use the instance number
key = (str(apdu.pduSource),
apdu.iAmDeviceIdentifier[1],)
# count the times this has been received
self.i_am_counter[key] += 1

# Given an Who Is request, cache it.
if isinstance(apdu, WhoIsRequest):
# build a key from the source and parameters
key = (str(apdu.pduSource),
apdu.deviceInstanceRangeLowLimit,
apdu.deviceInstanceRangeHighLimit,
)

# count the times this has been received
self.who_is_counter[key] += 1

# pass back to the default implementation
BIPSimpleApplication.indication(self, apdu)
#log_debug(ScriptApplication, "__init__ %r" % args)

def confirmation(self, apdu):
"""
This function process confirmed answers from the stack and looks for a returned
value or an error.
If a valid value is found, it's stored in the ResponseQueue. We'll wait for the
response to be used by the caller then resume the function.
def do_WhoIsRequest(self, apdu):
"""Respond to a Who-Is request."""
if self._debug: ScriptApplication._debug("do_WhoIsRequest %r", apdu)

How we deal with the Queue::
# build a key from the source and parameters
key = (str(apdu.pduSource),
apdu.deviceInstanceRangeLowLimit,
apdu.deviceInstanceRangeHighLimit,
)

# Creation of an event
evt = Event()
# Store the value and the event in the Queue
self.ResponseQueue.put((self.value, evt))
# Wait until the event is set by the caller (read function for example)
evt.wait()
# count the times this has been received
self.who_is_counter[key] += 1

:param apdu: apdu
"""
log_debug("confirmation from %r", apdu.pduSource)
# continue with the default implementation
BIPSimpleApplication.do_WhoIsRequest(self, apdu)

if isinstance(apdu, Error):
self.error = "%s" % (apdu.errorCode,)
#print('Error : %s' % self.error)

if self.error == "writeAccessDenied":
print('%s : Try writing to relinquish default.' % self.error)
evt = Event()
self.ResponseQueue.put((None, evt))
evt.wait()
raise WriteAccessDenied('Cannot write to point')

elif self.error == "unknownProperty":
#print('%s : Unknow property.' % self.error)
evt = Event()
self.ResponseQueue.put(('', evt))
evt.wait()
#raise UnknownPropertyError('Cannot find property on point')


elif isinstance(apdu, AbortPDU):

self.abort_pdu_reason = apdu.apduAbortRejectReason
#print('Abort PDU : %s' % self.abort_pdu_reason)
# UNCOMMENT STRING
if self.abort_pdu_reason == AbortReason.segmentationNotSupported:
#print('Segment Abort PDU : %s' % self.abort_pdu_reason)
evt = Event()
self.ResponseQueue.put(('err_seg', evt))
evt.wait()
# probably because of thread... the raise do not seem
# to be fired.... putting err_seg to raise from caller...
#raise SegmentationNotSupported('Segmentation problem with device')

else:
#print('Abort PDU : %s' % self.abort_pdu_reason)
#evt = Event()
self.ResponseQueue.put((None, evt))
evt.wait()
#raise NoResponseFromController('Abort PDU received')

if isinstance(apdu, SimpleAckPDU):
evt = Event()
self.ResponseQueue.put((self.value, evt))
evt.wait()

elif (isinstance(self._request, ReadPropertyRequest)) \
and (isinstance(apdu, ReadPropertyACK)):

# find the datatype
datatype = get_datatype(
apdu.objectIdentifier[0],
apdu.propertyIdentifier)
log_debug(" - datatype: %r", datatype)

if not datatype:
raise TypeError("unknown datatype")

# special case for array parts, others are managed by cast_out
if issubclass(datatype, Array) and (
apdu.propertyArrayIndex is not None):
if apdu.propertyArrayIndex == 0:
self.value = apdu.propertyValue.cast_out(Unsigned)
else:
self.value = apdu.propertyValue.cast_out(datatype.subtype)
else:
self.value = apdu.propertyValue.cast_out(datatype)

# Share data with script
evt = Event()
self.ResponseQueue.put((self.value, evt))
evt.wait()

log_debug(" - value: %r", self.value)
def do_IAmRequest(self, apdu):
"""Given an I-Am request, cache it."""
if self._debug: ScriptApplication._debug("do_IAmRequest %r", apdu)

elif (isinstance(self._request, ReadPropertyMultipleRequest)) \
and (isinstance(apdu, ReadPropertyMultipleACK)):
# build a key from the source, just use the instance number
key = (str(apdu.pduSource),
apdu.iAmDeviceIdentifier[1],
)

# loop through the results
for result in apdu.listOfReadAccessResults:
# here is the object identifier
objectIdentifier = result.objectIdentifier
log_debug(" - objectIdentifier: %r", objectIdentifier)
# count the times this has been received
self.i_am_counter[key] += 1

# now come the property values per object
for element in result.listOfResults:
# get the property and array index
propertyIdentifier = element.propertyIdentifier
log_debug(
" - propertyIdentifier: %r",
propertyIdentifier)
# continue with the default implementation
BIPSimpleApplication.do_IAmRequest(self, apdu)

propertyArrayIndex = element.propertyArrayIndex
log_debug(
" - propertyArrayIndex: %r",
propertyArrayIndex)

# here is the read result
readResult = element.readResult

if propertyArrayIndex is not None:
#sys.stdout.write("[" + str(propertyArrayIndex) + "]")
pass

# check for an error
if readResult.propertyAccessError is not None:
#sys.stdout.write(" ! " + str(readResult.propertyAccessError) + '\n')
pass

else:
# here is the value
propertyValue = readResult.propertyValue

# find the datatype
datatype = get_datatype(
objectIdentifier[0], propertyIdentifier)
log_debug(" - datatype: %r", datatype)

if not datatype:
raise TypeError("unknown datatype")

# special case for array parts, others are managed by
# cast_out
if issubclass(datatype, Array) and (
propertyArrayIndex is not None):
if propertyArrayIndex == 0:
self.values.append(
propertyValue.cast_out(Unsigned))
else:
self.values.append(
propertyValue.cast_out(datatype.subtype))
else:
value = propertyValue.cast_out(datatype)
log_debug(" - value: %r", value)

self.values.append(value)
# Use a queue to store the response, wait for it to be used then
# resume
evt = Event()
self.ResponseQueue.put((self.values, evt))
evt.wait()

def log_debug(txt, *args):
"""
Helper function to log debug messages
"""
if _DEBUG:
if args:
msg = txt % args
else:
msg = txt
# pylint: disable=E1101,W0212
ScriptApplication._debug(msg)
Loading

0 comments on commit cae83a4

Please sign in to comment.