From 5c7da58ad669144d34f8fd7f0648b50f98785cd3 Mon Sep 17 00:00:00 2001 From: Joel Bender Date: Wed, 29 Jan 2020 15:46:08 -0500 Subject: [PATCH] 0.18.0 releasd --- py25/bacpypes/__init__.py | 2 +- py25/bacpypes/app.py | 19 +- py25/bacpypes/appservice.py | 8 +- py25/bacpypes/basetypes.py | 5 +- py25/bacpypes/core.py | 4 +- py25/bacpypes/service/cov.py | 8 +- py27/bacpypes/__init__.py | 2 +- py27/bacpypes/app.py | 19 +- py27/bacpypes/appservice.py | 8 +- py27/bacpypes/basetypes.py | 4 +- py27/bacpypes/core.py | 4 +- py27/bacpypes/service/cov.py | 8 +- py34/bacpypes/__init__.py | 2 +- py34/bacpypes/app.py | 19 +- py34/bacpypes/appservice.py | 8 +- py34/bacpypes/basetypes.py | 4 +- py34/bacpypes/core.py | 4 +- py34/bacpypes/object.py | 205 +++++++------ py34/bacpypes/service/cov.py | 8 +- samples/HTTPServer.py | 194 +++++++----- samples/ReadProperty.py | 3 + samples/WhoIsIAm.py | 2 +- samples/WriteLightingCommand.py | 329 +++++++++++++++++++++ sandbox/read_property_redis.json | 29 ++ sandbox/read_property_redis.py | 194 ++++++++++++ setup.py | 1 + tests/test_constructed_data/test_choice.py | 25 +- tests/test_service/helpers.py | 6 +- 28 files changed, 903 insertions(+), 221 deletions(-) create mode 100755 samples/WriteLightingCommand.py create mode 100644 sandbox/read_property_redis.json create mode 100755 sandbox/read_property_redis.py diff --git a/py25/bacpypes/__init__.py b/py25/bacpypes/__init__.py index 9e13daa5..4ff0dc04 100755 --- a/py25/bacpypes/__init__.py +++ b/py25/bacpypes/__init__.py @@ -18,7 +18,7 @@ # Project Metadata # -__version__ = '0.17.7' +__version__ = '0.18.0' __author__ = 'Joel Bender' __email__ = 'joel@carrickbender.com' diff --git a/py25/bacpypes/app.py b/py25/bacpypes/app.py index 92f3070a..0b60b702 100755 --- a/py25/bacpypes/app.py +++ b/py25/bacpypes/app.py @@ -419,7 +419,7 @@ def process_io(self, iocb): # look up the queue queue = self.queue_by_address.get(destination_address, None) if not queue: - queue = SieveQueue(self.request, destination_address) + queue = SieveQueue(self._app_request, destination_address) self.queue_by_address[destination_address] = queue if _debug: ApplicationIOController._debug(" - queue: %r", queue) @@ -455,16 +455,27 @@ def _app_complete(self, address, apdu): if _debug: ApplicationIOController._debug(" - queue is empty") del self.queue_by_address[address] - def request(self, apdu): - if _debug: ApplicationIOController._debug("request %r", apdu) + def _app_request(self, apdu): + if _debug: ApplicationIOController._debug("_app_request %r", apdu) - # send it downstream + # send it downstream, bypass the guard super(ApplicationIOController, self).request(apdu) # if this was an unconfirmed request, it's complete, no message if isinstance(apdu, UnconfirmedRequestPDU): self._app_complete(apdu.pduDestination, None) + def request(self, apdu): + if _debug: ApplicationIOController._debug("request %r", apdu) + + # if this is not unconfirmed request, tell the application to use + # the IOCB interface + if not isinstance(apdu, UnconfirmedRequestPDU): + raise RuntimeError("use IOCB for confirmed requests") + + # send it downstream + super(ApplicationIOController, self).request(apdu) + def confirmation(self, apdu): if _debug: ApplicationIOController._debug("confirmation %r", apdu) diff --git a/py25/bacpypes/appservice.py b/py25/bacpypes/appservice.py index 110dee3e..12929be5 100755 --- a/py25/bacpypes/appservice.py +++ b/py25/bacpypes/appservice.py @@ -84,7 +84,7 @@ def __init__(self, sap, pdu_address): self.apduTimeout = getattr(sap.localDevice, 'apduTimeout', sap.apduTimeout) self.segmentationSupported = getattr(sap.localDevice, 'segmentationSupported', sap.segmentationSupported) - self.segmentTimeout = getattr(sap.localDevice, 'segmentTimeout', sap.segmentTimeout) + self.segmentTimeout = getattr(sap.localDevice, 'apduSegmentTimeout', sap.segmentTimeout) self.maxSegmentsAccepted = getattr(sap.localDevice, 'maxSegmentsAccepted', sap.maxSegmentsAccepted) self.maxApduLengthAccepted = getattr(sap.localDevice, 'maxApduLengthAccepted', sap.maxApduLengthAccepted) @@ -530,7 +530,11 @@ def segmented_request_timeout(self): self.segmentRetryCount += 1 self.start_timer(self.segmentTimeout) - self.fill_window(self.initialSequenceNumber) + + if self.initialSequenceNumber == 0: + self.request(self.get_segment(0)) + else: + self.fill_window(self.initialSequenceNumber) else: if _debug: ClientSSM._debug(" - abort, no response from the device") diff --git a/py25/bacpypes/basetypes.py b/py25/bacpypes/basetypes.py index 4d479a28..624de8ad 100755 --- a/py25/bacpypes/basetypes.py +++ b/py25/bacpypes/basetypes.py @@ -1800,7 +1800,6 @@ def decode(self, taglist): bacpypes_debugging(NameValue) ->>>>>>> stage class DeviceAddress(Sequence): sequenceElements = \ [ Element('networkNumber', Unsigned) @@ -2625,8 +2624,8 @@ class PropertyReference(Sequence): class Scale(Choice): choiceElements = \ - [ Element('floatScale', Real) - , Element('integerScale', Integer) + [ Element('floatScale', Real, 0) + , Element('integerScale', Integer, 1) ] class SecurityKeySet(Sequence): diff --git a/py25/bacpypes/core.py b/py25/bacpypes/core.py index 30974ff3..366eabd5 100755 --- a/py25/bacpypes/core.py +++ b/py25/bacpypes/core.py @@ -173,7 +173,7 @@ def run(spin=SPIN, sigterm=stop, sigusr1=print_stack): if _debug: run._info("keyboard interrupt") running = False except Exception, err: - if _debug: run._exception("an error has occurred: %s", err) + run._exception("an error has occurred: %s", err) running = False @@ -223,7 +223,7 @@ def run_once(): except KeyboardInterrupt: if _debug: run_once._info("keyboard interrupt") except Exception, err: - if _debug: run_once._exception("an error has occurred: %s", err) + run_once._exception("an error has occurred: %s", err) bacpypes_debugging(run_once) diff --git a/py25/bacpypes/service/cov.py b/py25/bacpypes/service/cov.py index d37b7515..463b58d4 100644 --- a/py25/bacpypes/service/cov.py +++ b/py25/bacpypes/service/cov.py @@ -523,8 +523,8 @@ def ReadProperty(self, obj, arrayIndex=None): current_time = TaskManager().get_time() if _debug: ActiveCOVSubscriptions._debug(" - current_time: %r", current_time) - # start with an empty sequence - cov_subscriptions = ListOf(COVSubscription)() + # start with an empty list + cov_subscriptions = [] # loop through the subscriptions for cov in obj._app.subscriptions(): @@ -553,6 +553,10 @@ def ReadProperty(self, obj, arrayIndex=None): ) if _debug: ActiveCOVSubscriptions._debug(" - recipient_process: %r", recipient_process) + # look for the algorithm already associated with this object + cov_detection = cov.obj_ref._app.cov_detections[cov.obj_ref] + if _debug: ActiveCOVSubscriptions._debug(" - cov_detection: %r", cov_detection) + cov_subscription = COVSubscription( recipient=recipient_process, monitoredPropertyReference=ObjectPropertyReference( diff --git a/py27/bacpypes/__init__.py b/py27/bacpypes/__init__.py index 9e13daa5..4ff0dc04 100755 --- a/py27/bacpypes/__init__.py +++ b/py27/bacpypes/__init__.py @@ -18,7 +18,7 @@ # Project Metadata # -__version__ = '0.17.7' +__version__ = '0.18.0' __author__ = 'Joel Bender' __email__ = 'joel@carrickbender.com' diff --git a/py27/bacpypes/app.py b/py27/bacpypes/app.py index b3bc96ee..819e75b4 100755 --- a/py27/bacpypes/app.py +++ b/py27/bacpypes/app.py @@ -427,7 +427,7 @@ def process_io(self, iocb): # look up the queue queue = self.queue_by_address.get(destination_address, None) if not queue: - queue = SieveQueue(self.request, destination_address) + queue = SieveQueue(self._app_request, destination_address) self.queue_by_address[destination_address] = queue if _debug: ApplicationIOController._debug(" - queue: %r", queue) @@ -463,16 +463,27 @@ def _app_complete(self, address, apdu): if _debug: ApplicationIOController._debug(" - queue is empty") del self.queue_by_address[address] - def request(self, apdu): - if _debug: ApplicationIOController._debug("request %r", apdu) + def _app_request(self, apdu): + if _debug: ApplicationIOController._debug("_app_request %r", apdu) - # send it downstream + # send it downstream, bypass the guard super(ApplicationIOController, self).request(apdu) # if this was an unconfirmed request, it's complete, no message if isinstance(apdu, UnconfirmedRequestPDU): self._app_complete(apdu.pduDestination, None) + def request(self, apdu): + if _debug: ApplicationIOController._debug("request %r", apdu) + + # if this is not unconfirmed request, tell the application to use + # the IOCB interface + if not isinstance(apdu, UnconfirmedRequestPDU): + raise RuntimeError("use IOCB for confirmed requests") + + # send it downstream + super(ApplicationIOController, self).request(apdu) + def confirmation(self, apdu): if _debug: ApplicationIOController._debug("confirmation %r", apdu) diff --git a/py27/bacpypes/appservice.py b/py27/bacpypes/appservice.py index c021282f..3617c49a 100755 --- a/py27/bacpypes/appservice.py +++ b/py27/bacpypes/appservice.py @@ -85,7 +85,7 @@ def __init__(self, sap, pdu_address): self.apduTimeout = getattr(sap.localDevice, 'apduTimeout', sap.apduTimeout) self.segmentationSupported = getattr(sap.localDevice, 'segmentationSupported', sap.segmentationSupported) - self.segmentTimeout = getattr(sap.localDevice, 'segmentTimeout', sap.segmentTimeout) + self.segmentTimeout = getattr(sap.localDevice, 'apduSegmentTimeout', sap.segmentTimeout) self.maxSegmentsAccepted = getattr(sap.localDevice, 'maxSegmentsAccepted', sap.maxSegmentsAccepted) self.maxApduLengthAccepted = getattr(sap.localDevice, 'maxApduLengthAccepted', sap.maxApduLengthAccepted) @@ -530,7 +530,11 @@ def segmented_request_timeout(self): self.segmentRetryCount += 1 self.start_timer(self.segmentTimeout) - self.fill_window(self.initialSequenceNumber) + + if self.initialSequenceNumber == 0: + self.request(self.get_segment(0)) + else: + self.fill_window(self.initialSequenceNumber) else: if _debug: ClientSSM._debug(" - abort, no response from the device") diff --git a/py27/bacpypes/basetypes.py b/py27/bacpypes/basetypes.py index 1bc45450..31b93733 100755 --- a/py27/bacpypes/basetypes.py +++ b/py27/bacpypes/basetypes.py @@ -2623,8 +2623,8 @@ class PropertyReference(Sequence): class Scale(Choice): choiceElements = \ - [ Element('floatScale', Real) - , Element('integerScale', Integer) + [ Element('floatScale', Real, 0) + , Element('integerScale', Integer, 1) ] class SecurityKeySet(Sequence): diff --git a/py27/bacpypes/core.py b/py27/bacpypes/core.py index 6430b864..604106ce 100755 --- a/py27/bacpypes/core.py +++ b/py27/bacpypes/core.py @@ -171,7 +171,7 @@ def run(spin=SPIN, sigterm=stop, sigusr1=print_stack): if _debug: run._info("keyboard interrupt") running = False except Exception as err: - if _debug: run._exception("an error has occurred: %s", err) + run._exception("an error has occurred: %s", err) running = False @@ -220,7 +220,7 @@ def run_once(): except KeyboardInterrupt: if _debug: run_once._info("keyboard interrupt") except Exception as err: - if _debug: run_once._exception("an error has occurred: %s", err) + run_once._exception("an error has occurred: %s", err) # # deferred diff --git a/py27/bacpypes/service/cov.py b/py27/bacpypes/service/cov.py index 3ba68158..02887131 100644 --- a/py27/bacpypes/service/cov.py +++ b/py27/bacpypes/service/cov.py @@ -522,8 +522,8 @@ def ReadProperty(self, obj, arrayIndex=None): current_time = TaskManager().get_time() if _debug: ActiveCOVSubscriptions._debug(" - current_time: %r", current_time) - # start with an empty sequence - cov_subscriptions = ListOf(COVSubscription)() + # start with an empty list + cov_subscriptions = [] # loop through the subscriptions for cov in obj._app.subscriptions(): @@ -552,6 +552,10 @@ def ReadProperty(self, obj, arrayIndex=None): ) if _debug: ActiveCOVSubscriptions._debug(" - recipient_process: %r", recipient_process) + # look for the algorithm already associated with this object + cov_detection = cov.obj_ref._app.cov_detections[cov.obj_ref] + if _debug: ActiveCOVSubscriptions._debug(" - cov_detection: %r", cov_detection) + cov_subscription = COVSubscription( recipient=recipient_process, monitoredPropertyReference=ObjectPropertyReference( diff --git a/py34/bacpypes/__init__.py b/py34/bacpypes/__init__.py index f57571ff..9058178e 100755 --- a/py34/bacpypes/__init__.py +++ b/py34/bacpypes/__init__.py @@ -18,7 +18,7 @@ # Project Metadata # -__version__ = '0.17.7' +__version__ = '0.18.0' __author__ = 'Joel Bender' __email__ = 'joel@carrickbender.com' diff --git a/py34/bacpypes/app.py b/py34/bacpypes/app.py index b3bc96ee..819e75b4 100755 --- a/py34/bacpypes/app.py +++ b/py34/bacpypes/app.py @@ -427,7 +427,7 @@ def process_io(self, iocb): # look up the queue queue = self.queue_by_address.get(destination_address, None) if not queue: - queue = SieveQueue(self.request, destination_address) + queue = SieveQueue(self._app_request, destination_address) self.queue_by_address[destination_address] = queue if _debug: ApplicationIOController._debug(" - queue: %r", queue) @@ -463,16 +463,27 @@ def _app_complete(self, address, apdu): if _debug: ApplicationIOController._debug(" - queue is empty") del self.queue_by_address[address] - def request(self, apdu): - if _debug: ApplicationIOController._debug("request %r", apdu) + def _app_request(self, apdu): + if _debug: ApplicationIOController._debug("_app_request %r", apdu) - # send it downstream + # send it downstream, bypass the guard super(ApplicationIOController, self).request(apdu) # if this was an unconfirmed request, it's complete, no message if isinstance(apdu, UnconfirmedRequestPDU): self._app_complete(apdu.pduDestination, None) + def request(self, apdu): + if _debug: ApplicationIOController._debug("request %r", apdu) + + # if this is not unconfirmed request, tell the application to use + # the IOCB interface + if not isinstance(apdu, UnconfirmedRequestPDU): + raise RuntimeError("use IOCB for confirmed requests") + + # send it downstream + super(ApplicationIOController, self).request(apdu) + def confirmation(self, apdu): if _debug: ApplicationIOController._debug("confirmation %r", apdu) diff --git a/py34/bacpypes/appservice.py b/py34/bacpypes/appservice.py index 6504e3e5..655306e3 100755 --- a/py34/bacpypes/appservice.py +++ b/py34/bacpypes/appservice.py @@ -85,7 +85,7 @@ def __init__(self, sap, pdu_address): self.apduTimeout = getattr(sap.localDevice, 'apduTimeout', sap.apduTimeout) self.segmentationSupported = getattr(sap.localDevice, 'segmentationSupported', sap.segmentationSupported) - self.segmentTimeout = getattr(sap.localDevice, 'segmentTimeout', sap.segmentTimeout) + self.segmentTimeout = getattr(sap.localDevice, 'apduSegmentTimeout', sap.segmentTimeout) self.maxSegmentsAccepted = getattr(sap.localDevice, 'maxSegmentsAccepted', sap.maxSegmentsAccepted) self.maxApduLengthAccepted = getattr(sap.localDevice, 'maxApduLengthAccepted', sap.maxApduLengthAccepted) @@ -530,7 +530,11 @@ def segmented_request_timeout(self): self.segmentRetryCount += 1 self.start_timer(self.segmentTimeout) - self.fill_window(self.initialSequenceNumber) + + if self.initialSequenceNumber == 0: + self.request(self.get_segment(0)) + else: + self.fill_window(self.initialSequenceNumber) else: if _debug: ClientSSM._debug(" - abort, no response from the device") diff --git a/py34/bacpypes/basetypes.py b/py34/bacpypes/basetypes.py index 1bc45450..31b93733 100755 --- a/py34/bacpypes/basetypes.py +++ b/py34/bacpypes/basetypes.py @@ -2623,8 +2623,8 @@ class PropertyReference(Sequence): class Scale(Choice): choiceElements = \ - [ Element('floatScale', Real) - , Element('integerScale', Integer) + [ Element('floatScale', Real, 0) + , Element('integerScale', Integer, 1) ] class SecurityKeySet(Sequence): diff --git a/py34/bacpypes/core.py b/py34/bacpypes/core.py index 6430b864..604106ce 100755 --- a/py34/bacpypes/core.py +++ b/py34/bacpypes/core.py @@ -171,7 +171,7 @@ def run(spin=SPIN, sigterm=stop, sigusr1=print_stack): if _debug: run._info("keyboard interrupt") running = False except Exception as err: - if _debug: run._exception("an error has occurred: %s", err) + run._exception("an error has occurred: %s", err) running = False @@ -220,7 +220,7 @@ def run_once(): except KeyboardInterrupt: if _debug: run_once._info("keyboard interrupt") except Exception as err: - if _debug: run_once._exception("an error has occurred: %s", err) + run_once._exception("an error has occurred: %s", err) # # deferred diff --git a/py34/bacpypes/object.py b/py34/bacpypes/object.py index 0115050a..fdff04bd 100644 --- a/py34/bacpypes/object.py +++ b/py34/bacpypes/object.py @@ -764,9 +764,9 @@ class AccessDoorObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -817,9 +817,9 @@ class AccessPointObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -886,9 +886,9 @@ class AccessZoneObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -925,9 +925,9 @@ class AccumulatorObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -946,9 +946,9 @@ class AlertEnrollmentObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) ] @@ -980,9 +980,9 @@ class AnalogInputObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -1018,9 +1018,9 @@ class AnalogOutputObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -1055,9 +1055,9 @@ class AnalogValueObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -1108,9 +1108,9 @@ class BinaryInputObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -1148,9 +1148,9 @@ class BinaryOutputObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -1186,9 +1186,9 @@ class BinaryValueObject(Object): , OptionalProperty('eventEnable',EventTransitionBits) , OptionalProperty('ackedTransitions',EventTransitionBits) , OptionalProperty('notifyType',NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -1215,9 +1215,9 @@ class BitStringValueObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -1254,9 +1254,9 @@ class ChannelObject(Object): , OptionalProperty('eventState', EventState) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @@ -1280,9 +1280,9 @@ class CharacterStringValueObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -1319,9 +1319,9 @@ class CredentialDataInputObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @@ -1455,9 +1455,9 @@ class EventEnrollmentObject(Object): , ReadableProperty('eventEnable', EventTransitionBits) , ReadableProperty('ackedTransitions', EventTransitionBits) , ReadableProperty('notificationClass', Unsigned) - , ReadableProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , ReadableProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -1506,9 +1506,9 @@ class EventLogObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -1552,9 +1552,9 @@ class GlobalGroupObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -1596,9 +1596,9 @@ class IntegerValueObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -1633,9 +1633,9 @@ class LargeAnalogValueObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -1669,9 +1669,9 @@ class LifeSafetyPointObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -1709,9 +1709,9 @@ class LifeSafetyZoneObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -1782,9 +1782,9 @@ class LoadControlObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -1830,9 +1830,9 @@ class LoopObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -1861,9 +1861,9 @@ class MultiStateInputObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -1893,9 +1893,9 @@ class MultiStateOutputObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -1925,9 +1925,9 @@ class MultiStateValueObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -1953,7 +1953,6 @@ class NetworkPortObject(Object): , ReadableProperty('apduLength', Unsigned) #388 , ReadableProperty('linkSpeed', Real) #420 , OptionalProperty('linkSpeeds', ArrayOf(Real)) #421 - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) #130 , OptionalProperty('linkSpeedAutonegotiate', Boolean) #422 , OptionalProperty('networkInterfaceName', CharacterString) #424 , OptionalProperty('bacnetIPMode', IPMode) #408 @@ -1999,9 +1998,9 @@ class NetworkPortObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) #35 , OptionalProperty('ackedTransitions', EventTransitionBits) #0 , OptionalProperty('notifyType', NotifyType) #72 - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) #130 - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) #351 - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) #352 + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventState', EventState) #36 , ReadableProperty('reliabilityEvaluationInhibit', Boolean) #357 ] @@ -2087,9 +2086,9 @@ class PositiveIntegerValueObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -2118,9 +2117,9 @@ class ProgramObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @@ -2154,9 +2153,9 @@ class PulseConverterObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -2184,9 +2183,9 @@ class ScheduleObject(Object): , ReadableProperty('eventState', EventState) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @@ -2263,9 +2262,9 @@ class TrendLogObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) @@ -2300,9 +2299,9 @@ class TrendLogMultipleObject(Object): , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) - , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) - , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) - , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) + , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)) + , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)) + , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) diff --git a/py34/bacpypes/service/cov.py b/py34/bacpypes/service/cov.py index 3ba68158..02887131 100644 --- a/py34/bacpypes/service/cov.py +++ b/py34/bacpypes/service/cov.py @@ -522,8 +522,8 @@ def ReadProperty(self, obj, arrayIndex=None): current_time = TaskManager().get_time() if _debug: ActiveCOVSubscriptions._debug(" - current_time: %r", current_time) - # start with an empty sequence - cov_subscriptions = ListOf(COVSubscription)() + # start with an empty list + cov_subscriptions = [] # loop through the subscriptions for cov in obj._app.subscriptions(): @@ -552,6 +552,10 @@ def ReadProperty(self, obj, arrayIndex=None): ) if _debug: ActiveCOVSubscriptions._debug(" - recipient_process: %r", recipient_process) + # look for the algorithm already associated with this object + cov_detection = cov.obj_ref._app.cov_detections[cov.obj_ref] + if _debug: ActiveCOVSubscriptions._debug(" - cov_detection: %r", cov_detection) + cov_subscription = COVSubscription( recipient=recipient_process, monitoredPropertyReference=ObjectPropertyReference( diff --git a/samples/HTTPServer.py b/samples/HTTPServer.py index f8c686dd..249a4c06 100755 --- a/samples/HTTPServer.py +++ b/samples/HTTPServer.py @@ -4,17 +4,23 @@ HTTPServer """ +import os import threading import json import zlib from collections import OrderedDict -from urlparse import urlparse, parse_qs -import SocketServer -import SimpleHTTPServer - -from bacpypes.debugging import class_debugging, ModuleLogger +try: + from urlparse import urlparse, parse_qs + from SocketServer import ThreadingMixIn, TCPServer + from SimpleHTTPServer import SimpleHTTPRequestHandler +except ImportError: + from urllib.parse import urlparse, parse_qs + from socketserver import ThreadingMixIn, TCPServer + from http.server import SimpleHTTPRequestHandler + +from bacpypes.debugging import bacpypes_debugging, ModuleLogger from bacpypes.consolelogging import ConfigArgumentParser from bacpypes.core import run, deferred @@ -33,73 +39,86 @@ _debug = 0 _log = ModuleLogger(globals()) +# settings +HOST = os.getenv("HOST", "") +PORT = int(os.getenv("PORT", 8080)) + # reference a simple application this_application = None server = None # favorite icon favicon = zlib.decompress( -'x\x9c\xb5\x93\xcdN\xdb@\x14\x85\x07\x95\x07\xc8\x8amYv\xc9#\xe4\x11x\x04\x96}' -'\x8c\x88\x1dl\xa0\x9b\xb6A\xa2)\x0bVTB\xa9"\xa5?*I\x16\xad"\x84d\x84DE\x93' -'\x14;v\xc01M\xe2$\x988\xb1l\x9d\xde;v\\\x03\x89TU\xea\xb5N\xe4\xb9\x9a\xef' -'\x1c\xcfO\x84X\xa0\'\x95\x12\xf4\xbb,\x9e/\n\xb1$\x84xF\xa2\x16u\xc2>WzQ\xfc' -'\xf7\xca\xad\xafo\x91T\xd2\x1ai\xe5\x1fx[\xf9\xf4\x01\xc57\xbb\xd8\xdf\xd8' -'\x00\x8d\x11\xf9\x95\x12\xda\x9a\xc3\xae\xe5_\xbdDpk\x03\xc3\xaeT\xd0\xb3\xd0' -'>?\x83Z\xfd\x86Z\xa5\x84\x1fG_\xa4\xe7\x1c^\xa9W\xbfJ\xfe\xb4\xf0\x0e^\xdb' -'\x88}0 \xafA\x0f\xa3+c&O\xbd\xf4\xc1\xf6\xb6d\x9d\xc6\x05\xdcVSz\xb0x\x1c\x10' -'\x0fo\x02\xc7\xd0\xe7\xf1%\xe5\xf3\xc78\xdb\xf9Y\x93\x1eI\x1f\xf8>\xfa\xb5' -'\x8bG<\x8dW\x0f^\x84\xd9\xee\xb5~\x8f\xe1w\xaf{\x83\x80\xb2\xbd\xe1\x10\x83' -'\x88\'\xa5\x12\xbcZ?9\x8e\xb3%\xd3\xeb`\xd4\xd2\xffdS\xb9\x96\x89!}W!\xfb\x9a' -'\xf9t\xc4f\x8aos\x92\x9dtn\xe0\xe8Z\xcc\xc8=\xec\xf7d6\x97\xa3]\xc2Q\x1b(\xec' -'d\x99_\x8dx\xd4\x15%\xce\x96\xf9\xbf\xacP\xd1:\xfc\xf1\x18\xbe\xeb\xe2\xaey' -'\x89;]\xc5\xf1\xfb<\xf3\x99\xe9\x99\xefon\xa2\xdb6\xe5\x1c\xbb^\x8b}FV\x1b' -'\x9es+\xb3\xbd\x81M\xeb\xd1\xe0^5\xf1\xbd|\xc4\xfca\xf2\xde\xf0w\x9cW\xabr.' -'\xe7\xd9\x8dFx\x0e\xa6){\x93\x8e\x85\xf1\xb5\x81\x89\xd9\x82\xa1\x9c\xc8;\xf9' -'\xe0\x0cV\xb8W\xdc\xdb\x83\xa9i\xb1O@g\xa6T*\xd3=O\xeaP\xcc(^\x17\xfb\xe4\xb3' -'Y\xc9\xb1\x17{N\xf7\xfbo\x8b\xf7\x97\x94\xe3;\xcd\xff)\xd2\xf2\xacy\xa0\x9b' -'\xd4g=\x11B\x8bT\x8e\x94Y\x08%\x12\xe2q\x99\xd4\x7f*\x84O\xfa\r\xb5\x916R' + b"x\x9c\xb5\x93\xcdN\xdb@\x14\x85\x07\x95\x07\xc8\x8amYv\xc9#\xe4\x11x\x04\x96}" + b'\x8c\x88\x1dl\xa0\x9b\xb6A\xa2)\x0bVTB\xa9"\xa5?*I\x16\xad"\x84d\x84DE\x93' + b"\x14;v\xc01M\xe2$\x988\xb1l\x9d\xde;v\\\x03\x89TU\xea\xb5N\xe4\xb9\x9a\xef" + b"\x1c\xcfO\x84X\xa0'\x95\x12\xf4\xbb,\x9e/\n\xb1$\x84xF\xa2\x16u\xc2>WzQ\xfc" + b"\xf7\xca\xad\xafo\x91T\xd2\x1ai\xe5\x1fx[\xf9\xf4\x01\xc57\xbb\xd8\xdf\xd8" + b"\x00\x8d\x11\xf9\x95\x12\xda\x9a\xc3\xae\xe5_\xbdDpk\x03\xc3\xaeT\xd0\xb3\xd0" + b">?\x83Z\xfd\x86Z\xa5\x84\x1fG_\xa4\xe7\x1c^\xa9W\xbfJ\xfe\xb4\xf0\x0e^\xdb" + b"\x88}0 \xafA\x0f\xa3+c&O\xbd\xf4\xc1\xf6\xb6d\x9d\xc6\x05\xdcVSz\xb0x\x1c\x10" + b"\x0fo\x02\xc7\xd0\xe7\xf1%\xe5\xf3\xc78\xdb\xf9Y\x93\x1eI\x1f\xf8>\xfa\xb5" + b"\x8bG<\x8dW\x0f^\x84\xd9\xee\xb5~\x8f\xe1w\xaf{\x83\x80\xb2\xbd\xe1\x10\x83" + b"\x88'\xa5\x12\xbcZ?9\x8e\xb3%\xd3\xeb`\xd4\xd2\xffdS\xb9\x96\x89!}W!\xfb\x9a" + b"\xf9t\xc4f\x8aos\x92\x9dtn\xe0\xe8Z\xcc\xc8=\xec\xf7d6\x97\xa3]\xc2Q\x1b(\xec" + b"d\x99_\x8dx\xd4\x15%\xce\x96\xf9\xbf\xacP\xd1:\xfc\xf1\x18\xbe\xeb\xe2\xaey" + b"\x89;]\xc5\xf1\xfb<\xf3\x99\xe9\x99\xefon\xa2\xdb6\xe5\x1c\xbb^\x8b}FV\x1b" + b"\x9es+\xb3\xbd\x81M\xeb\xd1\xe0^5\xf1\xbd|\xc4\xfca\xf2\xde\xf0w\x9cW\xabr." + b"\xe7\xd9\x8dFx\x0e\xa6){\x93\x8e\x85\xf1\xb5\x81\x89\xd9\x82\xa1\x9c\xc8;\xf9" + b"\xe0\x0cV\xb8W\xdc\xdb\x83\xa9i\xb1O@g\xa6T*\xd3=O\xeaP\xcc(^\x17\xfb\xe4\xb3" + b"Y\xc9\xb1\x17{N\xf7\xfbo\x8b\xf7\x97\x94\xe3;\xcd\xff)\xd2\xf2\xacy\xa0\x9b" + b"\xd4g=\x11B\x8bT\x8e\x94Y\x08%\x12\xe2q\x99\xd4\x7f*\x84O\xfa\r\xb5\x916R" ) # # ThreadedHTTPRequestHandler # -@class_debugging -class ThreadedHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): +@bacpypes_debugging +class ThreadedHTTPRequestHandler(SimpleHTTPRequestHandler): def do_GET(self): - if _debug: ThreadedHTTPRequestHandler._debug("do_GET") + if _debug: + ThreadedHTTPRequestHandler._debug("do_GET") global favicon # get the thread cur_thread = threading.current_thread() - if _debug: ThreadedHTTPRequestHandler._debug(" - cur_thread: %r", cur_thread) + if _debug: + ThreadedHTTPRequestHandler._debug(" - cur_thread: %r", cur_thread) # parse query data and params to find out what was passed parsed_params = urlparse(self.path) - if _debug: ThreadedHTTPRequestHandler._debug(" - parsed_params: %r", parsed_params) + if _debug: + ThreadedHTTPRequestHandler._debug(" - parsed_params: %r", parsed_params) parsed_query = parse_qs(parsed_params.query) - if _debug: ThreadedHTTPRequestHandler._debug(" - parsed_query: %r", parsed_query) + if _debug: + ThreadedHTTPRequestHandler._debug(" - parsed_query: %r", parsed_query) # find the pieces - args = parsed_params.path.split('/') - if _debug: ThreadedHTTPRequestHandler._debug(" - args: %r", args) + args = parsed_params.path.split("/") + if _debug: + ThreadedHTTPRequestHandler._debug(" - args: %r", args) - if (args[1] == 'read'): + if args[1] == "read": self.do_read(args[2:]) - elif (args[1] == 'whois'): + elif args[1] == "whois": self.do_whois(args[2:]) - if args[1] == 'favicon.ico': + elif args[1] == "favicon.ico": self.send_response(200) - self.send_header("Content-type", 'image/x-icon') + self.send_header("Content-type", "image/x-icon") self.send_header("Content-Length", len(favicon)) self.end_headers() self.wfile.write(favicon) else: - return "'read' or 'whois' expected" + self.send_response(200) + self.send_header("Content-type", "text/plain") + self.end_headers() + self.wfile.write(b"'read' or 'whois' expected") def do_read(self, args): - if _debug: ThreadedHTTPRequestHandler._debug("do_read %r", args) + if _debug: + ThreadedHTTPRequestHandler._debug("do_read %r", args) try: addr, obj_id = args[:2] @@ -123,19 +142,20 @@ def do_read(self, args): # build a request request = ReadPropertyRequest( - objectIdentifier=obj_id, - propertyIdentifier=prop_id, - ) + objectIdentifier=obj_id, propertyIdentifier=prop_id + ) request.pduDestination = Address(addr) # look for an optional array index if len(args) == 5: request.propertyArrayIndex = int(args[4]) - if _debug: ThreadedHTTPRequestHandler._debug(" - request: %r", request) + if _debug: + ThreadedHTTPRequestHandler._debug(" - request: %r", request) # make an IOCB iocb = IOCB(request) - if _debug: ThreadedHTTPRequestHandler._debug(" - iocb: %r", iocb) + if _debug: + ThreadedHTTPRequestHandler._debug(" - iocb: %r", iocb) # give it to the application deferred(this_application.request_io, iocb) @@ -145,20 +165,29 @@ def do_read(self, args): # filter out errors and aborts if iocb.ioError: - if _debug: ThreadedHTTPRequestHandler._debug(" - error: %r", iocb.ioError) - result = { "error": str(iocb.ioError) } + if _debug: + ThreadedHTTPRequestHandler._debug(" - error: %r", iocb.ioError) + result = {"error": str(iocb.ioError)} else: - if _debug: ThreadedHTTPRequestHandler._debug(" - response: %r", iocb.ioResponse) + if _debug: + ThreadedHTTPRequestHandler._debug( + " - response: %r", iocb.ioResponse + ) apdu = iocb.ioResponse # find the datatype - datatype = get_datatype(apdu.objectIdentifier[0], apdu.propertyIdentifier) - if _debug: ThreadedHTTPRequestHandler._debug(" - datatype: %r", datatype) + datatype = get_datatype( + apdu.objectIdentifier[0], apdu.propertyIdentifier + ) + if _debug: + ThreadedHTTPRequestHandler._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 issubclass(datatype, Array) and ( + apdu.propertyArrayIndex is not None + ): if apdu.propertyArrayIndex == 0: datatype = Unsigned else: @@ -167,21 +196,26 @@ def do_read(self, args): # convert the value to a dict if possible value = apdu.propertyValue.cast_out(datatype) - if hasattr(value, 'dict_contents'): + if hasattr(value, "dict_contents"): value = value.dict_contents(as_class=OrderedDict) - if _debug: ThreadedHTTPRequestHandler._debug(" - value: %r", value) + if _debug: + ThreadedHTTPRequestHandler._debug(" - value: %r", value) - result = { "value": value } + result = {"value": value} except Exception as err: ThreadedHTTPRequestHandler._exception("exception: %r", err) - result = { "exception": str(err) } + result = {"exception": str(err)} + + # encode the results as JSON, convert to bytes + result_bytes = json.dumps(result).encode("utf-8") # write the result - json.dump(result, self.wfile) + self.wfile.write(result_bytes) def do_whois(self, args): - if _debug: ThreadedHTTPRequestHandler._debug("do_whois %r", args) + if _debug: + ThreadedHTTPRequestHandler._debug("do_whois %r", args) try: # build a request @@ -195,11 +229,13 @@ def do_whois(self, args): if len(args) == 2: request.deviceInstanceRangeLowLimit = int(args[0]) request.deviceInstanceRangeHighLimit = int(args[1]) - if _debug: ThreadedHTTPRequestHandler._debug(" - request: %r", request) + if _debug: + ThreadedHTTPRequestHandler._debug(" - request: %r", request) # make an IOCB iocb = IOCB(request) - if _debug: ThreadedHTTPRequestHandler._debug(" - iocb: %r", iocb) + if _debug: + ThreadedHTTPRequestHandler._debug(" - iocb: %r", iocb) # give it to the application this_application.request_io(iocb) @@ -209,14 +245,19 @@ def do_whois(self, args): except Exception as err: ThreadedHTTPRequestHandler._exception("exception: %r", err) - result = { "exception": str(err) } + result = {"exception": str(err)} + + # encode the results as JSON, convert to bytes + result_bytes = json.dumps(result).encode("utf-8") # write the result - json.dump(result, self.wfile) + self.wfile.write(result_bytes) -class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): + +class ThreadedTCPServer(ThreadingMixIn, TCPServer): pass + # # __main__ # @@ -225,37 +266,41 @@ class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): # parse the command line arguments parser = ConfigArgumentParser(description=__doc__) - # add an option to override the port in the config file - parser.add_argument('--port', type=int, - help="override the port in the config file to PORT", - default=9000, - ) + # add an option for the server host + parser.add_argument("--host", type=str, help="server host", default=HOST) + # add an option for the server port + parser.add_argument("--port", type=int, help="server port", default=PORT) args = parser.parse_args() - if _debug: _log.debug("initialization") - if _debug: _log.debug(" - args: %r", args) + if _debug: + _log.debug("initialization") + if _debug: + _log.debug(" - args: %r", args) # make a device object this_device = LocalDeviceObject(ini=args.ini) - if _debug: _log.debug(" - this_device: %r", this_device) + if _debug: + _log.debug(" - this_device: %r", this_device) # make a simple application this_application = BIPSimpleApplication(this_device, args.ini.address) # local host, special port - HOST, PORT = "", int(args.port) - server = ThreadedTCPServer((HOST, PORT), ThreadedHTTPRequestHandler) - if _debug: _log.debug(" - server: %r", server) + server = ThreadedTCPServer((args.host, args.port), ThreadedHTTPRequestHandler) + if _debug: + _log.debug(" - server: %r", server) # Start a thread with the server -- that thread will then start a thread for each request server_thread = threading.Thread(target=server.serve_forever) - if _debug: _log.debug(" - server_thread: %r", server_thread) + if _debug: + _log.debug(" - server_thread: %r", server_thread) # exit the server thread when the main thread terminates server_thread.daemon = True server_thread.start() - if _debug: _log.debug("running") + if _debug: + _log.debug("running") run() @@ -266,4 +311,5 @@ class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): if server: server.shutdown() - if _debug: _log.debug("finally") + if _debug: + _log.debug("finally") diff --git a/samples/ReadProperty.py b/samples/ReadProperty.py index 4da003bb..4001d23b 100755 --- a/samples/ReadProperty.py +++ b/samples/ReadProperty.py @@ -135,6 +135,9 @@ def main(): global this_application ConsoleLogHandler('bacpypes.consolelogging') + # add logging early to debug argument parsers + # ConsoleLogHandler('bacpypes.consolelogging') + # parse the command line arguments args = ConfigArgumentParser(description=__doc__).parse_args() diff --git a/samples/WhoIsIAm.py b/samples/WhoIsIAm.py index dc07e82e..8e6132c1 100755 --- a/samples/WhoIsIAm.py +++ b/samples/WhoIsIAm.py @@ -12,7 +12,7 @@ from bacpypes.consolelogging import ConfigArgumentParser from bacpypes.consolecmd import ConsoleCmd -from bacpypes.core import run, enable_sleeping +from bacpypes.core import run, deferred, enable_sleeping from bacpypes.iocb import IOCB from bacpypes.pdu import Address, GlobalBroadcast diff --git a/samples/WriteLightingCommand.py b/samples/WriteLightingCommand.py new file mode 100755 index 00000000..51708be1 --- /dev/null +++ b/samples/WriteLightingCommand.py @@ -0,0 +1,329 @@ +#!/usr/bin/env python + +""" +This application presents a 'console' prompt to the user asking for commands. +""" + +import sys + +from bacpypes.debugging import bacpypes_debugging, ModuleLogger +from bacpypes.consolelogging import ConfigArgumentParser +from bacpypes.consolecmd import ConsoleCmd + +from bacpypes.core import run, deferred, enable_sleeping +from bacpypes.iocb import IOCB + +from bacpypes.pdu import Address +from bacpypes.object import get_datatype + +from bacpypes.apdu import ( + SimpleAckPDU, + ReadPropertyRequest, + ReadPropertyACK, + WritePropertyRequest, +) +from bacpypes.primitivedata import Unsigned, ObjectIdentifier +from bacpypes.constructeddata import Array, Any +from bacpypes.basetypes import LightingCommand + +from bacpypes.app import BIPSimpleApplication +from bacpypes.local.device import LocalDeviceObject + +# some debugging +_debug = 0 +_log = ModuleLogger(globals()) + +# globals +this_application = None + +# +# WriteLightingConsoleCmd +# + + +@bacpypes_debugging +class WriteLightingConsoleCmd(ConsoleCmd): + def do_read(self, args): + """read [ ]""" + args = args.split() + if _debug: + WriteLightingConsoleCmd._debug("do_read %r", args) + + try: + addr, obj_id, prop_id = args[:3] + obj_id = ObjectIdentifier(obj_id).value + + datatype = get_datatype(obj_id[0], prop_id) + if not datatype: + raise ValueError("invalid property for object type") + + # build a request + request = ReadPropertyRequest( + objectIdentifier=obj_id, propertyIdentifier=prop_id + ) + request.pduDestination = Address(addr) + + if len(args) == 4: + request.propertyArrayIndex = int(args[3]) + if _debug: + WriteLightingConsoleCmd._debug(" - request: %r", request) + + # make an IOCB + iocb = IOCB(request) + if _debug: + WriteLightingConsoleCmd._debug(" - iocb: %r", iocb) + + # give it to the application + deferred(this_application.request_io, iocb) + + # wait for it to complete + iocb.wait() + + # do something for success + if iocb.ioResponse: + apdu = iocb.ioResponse + + # should be an ack + if not isinstance(apdu, ReadPropertyACK): + if _debug: + WriteLightingConsoleCmd._debug(" - not an ack") + return + + # find the datatype + datatype = get_datatype( + apdu.objectIdentifier[0], apdu.propertyIdentifier + ) + if _debug: + WriteLightingConsoleCmd._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: + value = apdu.propertyValue.cast_out(Unsigned) + else: + value = apdu.propertyValue.cast_out(datatype.subtype) + else: + value = apdu.propertyValue.cast_out(datatype) + if _debug: + WriteLightingConsoleCmd._debug(" - value: %r", value) + + sys.stdout.write(str(value) + "\n") + if hasattr(value, "debug_contents"): + value.debug_contents(file=sys.stdout) + sys.stdout.flush() + + # do something for error/reject/abort + if iocb.ioError: + sys.stdout.write(str(iocb.ioError) + "\n") + + except Exception as error: + WriteLightingConsoleCmd._exception("exception: %r", error) + + def do_write(self, args): + """write [ ... ]""" + args = args.split() + if _debug: + WriteLightingConsoleCmd._debug("do_write %r", args) + + try: + addr = args.pop(0) + obj_id = ObjectIdentifier(args.pop(0)).value + prop_id = args.pop(0) + + if obj_id[0] != "lightingOutput": + raise ValueError("lightingOutput") + if prop_id != "lightingCommand": + raise ValueError("lightingCommand") + if not args: + raise ValueError("operation required") + + value = LightingCommand() + value.operation = operation = args.pop(0) + + if operation == "fadeTo": + if not args: + raise ValueError("target level required") + value.targetLevel = float(args.pop(0)) + + if args: + value.priority = int(args.pop(0)) + if args: + value.fadeTime = int(args.pop(0)) + + elif operation == "rampTo": + if not args: + raise ValueError("target level required") + value.targetLevel = float(args.pop(0)) + + if args: + value.priority = int(args.pop(0)) + if args: + value.rampRate = float(args.pop(0)) + + elif operation == "stepUp": + if args: + value.priority = int(args.pop(0)) + if args: + value.stepIncrement = float(args.pop(0)) + + elif operation == "stepDown": + if args: + value.priority = int(args.pop(0)) + if args: + value.stepIncrement = float(args.pop(0)) + + elif operation == "stepOn": + if args: + value.priority = int(args.pop(0)) + if args: + value.stepIncrement = float(args.pop(0)) + + elif operation == "stepOff": + if args: + value.priority = int(args.pop(0)) + if args: + value.stepIncrement = float(args.pop(0)) + + elif operation == "warn": + if args: + value.priority = int(args.pop(0)) + + elif operation == "warnOff": + if args: + value.priority = int(args.pop(0)) + + elif operation == "warnRelinquish": + if args: + value.priority = int(args.pop(0)) + + elif operation == "stop": + if args: + value.priority = int(args.pop(0)) + + else: + raise ValueError("invalid operation") + + if (value.targetLevel is not None) and not ( + 0.0 <= value.targetLevel <= 100.0 + ): + raise ValueError("invalid target level (0.0..100.0)") + if (value.rampRate is not None) and not (0.0 <= value.rampRate <= 100.0): + raise ValueError("invalid ramp rate (0.0..100.0)") + if (value.stepIncrement is not None) and not ( + 0.1 <= value.stepIncrement <= 100.0 + ): + raise ValueError("invalid step increment (0.1..100.0)") + if (value.fadeTime is not None) and not (100 <= value.fadeTime <= 86400000): + raise ValueError("invalid fade time (100..86400000)") + if (value.priority is not None) and not (1 <= value.priority <= 16): + raise ValueError("invalid priority (1..16)") + + if _debug: + WriteLightingConsoleCmd._debug(" - value: %r", value) + + # build a request + request = WritePropertyRequest( + objectIdentifier=obj_id, propertyIdentifier=prop_id + ) + request.pduDestination = Address(addr) + + # save the value + request.propertyValue = Any() + try: + request.propertyValue.cast_in(value) + except Exception as error: + WriteLightingConsoleCmd._exception( + "WriteProperty cast error: %r", error + ) + if _debug: + WriteLightingConsoleCmd._debug(" - request: %r", request) + + # make an IOCB + iocb = IOCB(request) + if _debug: + WriteLightingConsoleCmd._debug(" - iocb: %r", iocb) + + # give it to the application + deferred(this_application.request_io, iocb) + + # wait for it to complete + iocb.wait() + + # do something for success + if iocb.ioResponse: + # should be an ack + if not isinstance(iocb.ioResponse, SimpleAckPDU): + if _debug: + WriteLightingConsoleCmd._debug(" - not an ack") + return + + sys.stdout.write("ack\n") + + # do something for error/reject/abort + if iocb.ioError: + sys.stdout.write(str(iocb.ioError) + "\n") + + except Exception as error: + WriteLightingConsoleCmd._exception("exception: %r", error) + + def do_rtn(self, args): + """rtn ... """ + args = args.split() + if _debug: + WriteLightingConsoleCmd._debug("do_rtn %r", args) + + # provide the address and a list of network numbers + router_address = Address(args[0]) + network_list = [int(arg) for arg in args[1:]] + + # pass along to the service access point + this_application.nsap.update_router_references( + None, router_address, network_list + ) + + +# +# __main__ +# + + +def main(): + global this_application + + # parse the command line arguments + args = ConfigArgumentParser(description=__doc__).parse_args() + + if _debug: + _log.debug("initialization") + if _debug: + _log.debug(" - args: %r", args) + + # make a device object + this_device = LocalDeviceObject(ini=args.ini) + if _debug: + _log.debug(" - this_device: %r", this_device) + + # make a simple application + this_application = BIPSimpleApplication(this_device, args.ini.address) + + # make a console + this_console = WriteLightingConsoleCmd() + if _debug: + _log.debug(" - this_console: %r", this_console) + + # enable sleeping will help with threads + enable_sleeping() + + _log.debug("running") + + run() + + _log.debug("fini") + + +if __name__ == "__main__": + main() diff --git a/sandbox/read_property_redis.json b/sandbox/read_property_redis.json new file mode 100644 index 00000000..44f91ec8 --- /dev/null +++ b/sandbox/read_property_redis.json @@ -0,0 +1,29 @@ +{ + "bacpypes": { + "debug": ["__main__"], + "color": true + }, + "local-device": { + "objectName": "Betelgeuse-47808", + "address": "wlp2s0:47809", + "objectIdentifier": 599, + "maxApduLengthAccepted": 1024, + "segmentationSupported": "segmentedBoth", + "maxSegmentsAccepted": 1024, + "vendorIdentifier": 15, + "foreignBBMD": "128.253.109.254", + "foreignTTL": 30 + }, + "redis": { + "host": "enterprise" + }, + "redis-stream": "read-prop-stream", + "point-list": [ + { + "key": "random1", + "address": "wlp2s0:47808", + "objectIdentifier": "analogValue:1", + "propertyIdentifier": "presentValue" + } + ] +} diff --git a/sandbox/read_property_redis.py b/sandbox/read_property_redis.py new file mode 100755 index 00000000..3fbfd58b --- /dev/null +++ b/sandbox/read_property_redis.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python + +""" +Read some BACnet point values, save them as key/value JSON blobs in Redis, +and publish them to stream. +""" + +import json +import redis +from time import time as _time + +from collections import deque + +from bacpypes.debugging import bacpypes_debugging, ModuleLogger +from bacpypes.consolelogging import JSONArgumentParser + +from bacpypes.core import run, stop, deferred +from bacpypes.iocb import IOCB + +from bacpypes.pdu import Address +from bacpypes.object import get_datatype + +from bacpypes.apdu import ReadPropertyRequest +from bacpypes.primitivedata import Unsigned, ObjectIdentifier +from bacpypes.constructeddata import Array + +from bacpypes.app import BIPSimpleApplication +from bacpypes.local.device import LocalDeviceObject + +# some debugging +_debug = 0 +_log = ModuleLogger(globals()) + +# globals +this_application = None +redis_connection = None +redis_stream = None + + +@bacpypes_debugging +class ReadPointListApplication(BIPSimpleApplication): + def __init__(self, point_list, *args): + if _debug: + ReadPointListApplication._debug("__init__ %r, %r", point_list, args) + BIPSimpleApplication.__init__(self, *args) + + # turn the point list into a queue + self.point_queue = deque(point_list) + + def next_request(self): + if _debug: + ReadPointListApplication._debug("next_request") + + # check to see if we're done + if not self.point_queue: + if _debug: + ReadPointListApplication._debug(" - done") + stop() + return + + # get the next request + point_info = self.point_queue.popleft() + if _debug: + ReadPointListApplication._debug(" - point_info: %r", point_info) + + # build a request + request = ReadPropertyRequest( + destination=Address(point_info["address"]), + objectIdentifier=ObjectIdentifier(point_info["objectIdentifier"]).value, + propertyIdentifier=point_info.get("propertyIdentifier", "presentValue"), + ) + if _debug: + ReadPointListApplication._debug(" - request: %r", request) + + # make an IOCB + iocb = IOCB(request) + iocb.point_info = point_info + + # set a callback for the response + iocb.add_callback(self.complete_request) + if _debug: + ReadPointListApplication._debug(" - iocb: %r", iocb) + + # send the request + this_application.request_io(iocb) + + def complete_request(self, iocb): + if _debug: + ReadPointListApplication._debug("complete_request %r", iocb) + global redis_connection, redis_stream + + # point information has the key + point_info = iocb.point_info + if _debug: + ReadPointListApplication._debug(" - point_info: %r", point_info) + + if iocb.ioResponse: + apdu = iocb.ioResponse + + # find the datatype + datatype = get_datatype(apdu.objectIdentifier[0], apdu.propertyIdentifier) + if _debug: + ReadPointListApplication._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: + value = apdu.propertyValue.cast_out(Unsigned) + else: + value = apdu.propertyValue.cast_out(datatype.subtype) + else: + value = apdu.propertyValue.cast_out(datatype) + if _debug: + ReadPointListApplication._debug(" - value: %r", value) + + # create a blob for the data + point_data = {"timestamp": _time(), "value": value} + + if iocb.ioError: + if _debug: + ReadPointListApplication._debug(" - error: %r", iocb.ioError) + + # create a blob for the data + point_data = {"timestamp": _time(), "error": iocb.ioError} + + # save the content as a JSON + redis_connection.set(point_info["key"], json.dumps(point_data)) + + # update the point info to add the key, save it in the stream + point_data["key"] = point_info["key"] + redis_connection.xadd(redis_stream, point_data) + + # fire off another request + deferred(self.next_request) + + +def main(): + global this_application, redis_connection, redis_stream + + # parse the command line arguments + parser = JSONArgumentParser(description=__doc__) + + if _debug: + _log.debug("initialization") + + args = parser.parse_args() + if _debug: + _log.debug(" - args: %r", args) + + # settings for connecting to the redis server + redis_settings = args.json["redis"] + if _debug: + _log.debug(" - redis_settings: %r", redis_settings) + + # addtional settings for this application + redis_stream = args.json["redis-stream"] + if _debug: + _log.debug(" - redis_stream: %r", redis_stream) + + # connect to Redis + redis_connection = redis.Redis(**redis_settings) + if _debug: + _log.debug(" - redis_connection: %r", redis_connection) + + # make a device object + local_device = args.json["local-device"] + this_device = LocalDeviceObject(**local_device) + if _debug: + _log.debug(" - this_device: %r", this_device) + + # get the point list + point_list = args.json["point-list"] + if _debug: + _log.debug(" - point_list: %r", point_list) + + # make a simple application + this_application = ReadPointListApplication( + point_list, this_device, local_device.address + ) + + # fire off a request when the core has a chance + deferred(this_application.next_request) + + _log.debug("running") + + run() + + _log.debug("fini") + + +if __name__ == "__main__": + main() diff --git a/setup.py b/setup.py index 6f3d1060..daf01186 100644 --- a/setup.py +++ b/setup.py @@ -20,6 +20,7 @@ (3, 5): 'py34', (3, 6): 'py34', (3, 7): 'py34', + (3, 8): 'py34', }.get(version_info, None) if not source_folder: raise EnvironmentError("unsupported version of Python") diff --git a/tests/test_constructed_data/test_choice.py b/tests/test_constructed_data/test_choice.py index f323afee..8715d064 100644 --- a/tests/test_constructed_data/test_choice.py +++ b/tests/test_constructed_data/test_choice.py @@ -1,4 +1,27 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# placeholder +""" +Test Choice +----------- +""" + +import unittest + +from bacpypes.basetypes import Scale +from bacpypes.debugging import bacpypes_debugging, ModuleLogger +from bacpypes.primitivedata import Tag, TagList + +# some debugging +_debug = 0 +_log = ModuleLogger(globals()) + + +@bacpypes_debugging +class TestScaleChoice(unittest.TestCase): + def test_scale_choice(self): + if _debug: TestScaleChoice._debug("test_scale_choice") + taglist = TagList([Tag(1, 1, 1, bytearray(b'\x00'))]) + scale = Scale() + scale.decode(taglist) + self.assertDictEqual(scale.dict_contents(), {'integerScale': 0}) diff --git a/tests/test_service/helpers.py b/tests/test_service/helpers.py index 8bfcd967..5b669ade 100644 --- a/tests/test_service/helpers.py +++ b/tests/test_service/helpers.py @@ -8,6 +8,7 @@ from bacpypes.capability import Capability from bacpypes.comm import Client, bind +from bacpypes.iocb import IOCB from bacpypes.pdu import Address, LocalBroadcast from bacpypes.npdu import NPDU from bacpypes.apdu import apdu_types, APDU, SimpleAckPDU, RejectPDU, AbortPDU @@ -292,8 +293,9 @@ def __init__(self, localDevice, vlan): def send(self, apdu): if _debug: ApplicationStateMachine._debug("send(%s) %r", self.name, apdu) - # send the apdu down the stack - self.request(apdu) + # build an IOCB to wrap the request + iocb = IOCB(apdu) + self.request_io(iocb) def indication(self, apdu): if _debug: ApplicationStateMachine._debug("indication(%s) %r", self.name, apdu)