From 9750c0f13c504d443d4f17871690d7df9326a3c0 Mon Sep 17 00:00:00 2001 From: Joel Bender Date: Fri, 17 Nov 2017 02:02:24 -0500 Subject: [PATCH 1/6] first crack at #148 --- py27/bacpypes/object.py | 29 --------------- py27/bacpypes/service/device.py | 33 ++++++++--------- py27/bacpypes/service/object.py | 63 +++++++++++++++++++++++++++++++-- 3 files changed, 77 insertions(+), 48 deletions(-) diff --git a/py27/bacpypes/object.py b/py27/bacpypes/object.py index 63dce6d..9bc1c9a 100755 --- a/py27/bacpypes/object.py +++ b/py27/bacpypes/object.py @@ -396,13 +396,6 @@ class Object(object): # empty list of property monitors self._property_monitors = defaultdict(list) - # start with a clean array of property identifiers - if 'propertyList' in initargs: - propertyList = None - else: - propertyList = ArrayOf(PropertyIdentifier)() - initargs['propertyList'] = propertyList - # initialize the object for propid, prop in self._properties.items(): if propid in initargs: @@ -411,20 +404,12 @@ class Object(object): # defer to the property object for error checking prop.WriteProperty(self, initargs[propid], direct=True) - # add it to the property list if we are building one - if propertyList is not None: - propertyList.append(propid) - elif prop.default is not None: if _debug: Object._debug(" - setting %s from default", propid) # default values bypass property interface self._values[propid] = prop.default - # add it to the property list if we are building one - if propertyList is not None: - propertyList.append(propid) - else: if not prop.optional: if _debug: Object._debug(" - %s value required", propid) @@ -485,13 +470,6 @@ class Object(object): self._properties[prop.identifier] = prop self._values[prop.identifier] = prop.default - # tell the object it has a new property - if 'propertyList' in self._values: - property_list = self.propertyList - if prop.identifier not in property_list: - if _debug: Object._debug(" - adding to property list") - property_list.append(prop.identifier) - def delete_property(self, prop): """Delete a property from an object. The property is an instance of a Property or one of its derived classes, but only the property @@ -507,13 +485,6 @@ class Object(object): if prop.identifier in self._values: del self._values[prop.identifier] - # remove the property identifier from its list of know properties - if 'propertyList' in self._values: - property_list = self.propertyList - if prop.identifier in property_list: - if _debug: Object._debug(" - removing from property list") - property_list.remove(prop.identifier) - def ReadProperty(self, propid, arrayIndex=None): if _debug: Object._debug("ReadProperty %r arrayIndex=%r", propid, arrayIndex) diff --git a/py27/bacpypes/service/device.py b/py27/bacpypes/service/device.py index 0382f19..873dc4f 100644 --- a/py27/bacpypes/service/device.py +++ b/py27/bacpypes/service/device.py @@ -14,6 +14,8 @@ from ..object import register_object_type, registered_object_types, \ Property, DeviceObject from ..task import FunctionTask +from .object import LocalObject + # some debugging _debug = 0 _log = ModuleLogger(globals()) @@ -67,7 +69,7 @@ class CurrentTimeProperty(Property): # @bacpypes_debugging -class LocalDeviceObject(DeviceObject): +class LocalDeviceObject(LocalObject, DeviceObject): properties = \ [ CurrentTimeProperty('localTime') @@ -102,12 +104,24 @@ class LocalDeviceObject(DeviceObject): raise RuntimeError("vendorIdentifier required to auto-register the LocalDeviceObject class") register_object_type(self.__class__, vendor_id=kwargs['vendorIdentifier']) - # check for local time + # check for properties this class implements if 'localDate' in kwargs: raise RuntimeError("localDate is provided by LocalDeviceObject and cannot be overridden") if 'localTime' in kwargs: raise RuntimeError("localTime is provided by LocalDeviceObject and cannot be overridden") + # the object identifier is required for the object list + if 'objectIdentifier' not in kwargs: + raise RuntimeError("objectIdentifier is required") + + # the object list is provided + if 'objectList' in kwargs: + raise RuntimeError("objectList is provided by LocalDeviceObject and cannot be overridden") + else: + kwargs['objectList'] = ArrayOf(ObjectIdentifier)([ + kwargs['objectIdentifier'], + ]) + # check for a minimum value if kwargs['maxApduLengthAccepted'] < 50: raise ValueError("invalid max APDU length accepted") @@ -116,20 +130,7 @@ class LocalDeviceObject(DeviceObject): if _debug: LocalDeviceObject._debug(" - updated kwargs: %r", kwargs) # proceed as usual - DeviceObject.__init__(self, **kwargs) - - # create a default implementation of an object list for local devices. - # If it is specified in the kwargs, that overrides this default. - if ('objectList' not in kwargs): - self.objectList = ArrayOf(ObjectIdentifier)([self.objectIdentifier]) - - # if the object has a property list and one wasn't provided - # in the kwargs, then it was created by default and the objectList - # property should be included - if ('propertyList' not in kwargs) and self.propertyList: - # make sure it's not already there - if 'objectList' not in self.propertyList: - self.propertyList.append('objectList') + LocalObject.__init__(self, **kwargs) # # Who-Is I-Am Services diff --git a/py27/bacpypes/service/object.py b/py27/bacpypes/service/object.py index 6cbcf13..0c33999 100644 --- a/py27/bacpypes/service/object.py +++ b/py27/bacpypes/service/object.py @@ -3,20 +3,77 @@ from ..debugging import bacpypes_debugging, ModuleLogger from ..capability import Capability -from ..basetypes import ErrorType +from ..basetypes import ErrorType, PropertyIdentifier from ..primitivedata import Atomic, Null, Unsigned -from ..constructeddata import Any, Array +from ..constructeddata import Any, Array, ArrayOf from ..apdu import Error, \ SimpleAckPDU, ReadPropertyACK, ReadPropertyMultipleACK, \ ReadAccessResult, ReadAccessResultElement, ReadAccessResultElementChoice from ..errors import ExecutionError -from ..object import PropertyError +from ..object import Object, PropertyError # some debugging _debug = 0 _log = ModuleLogger(globals()) +# +# LocalObject +# + +@bacpypes_debugging +class LocalObject(Object): + + def __init__(self, **kwargs): + if _debug: LocalObject._debug("__init__ %r", kwargs) + + # check for property list + if 'propertyList' in kwargs: + raise RuntimeError("propertyList is provided by LocalObject and cannot be overridden") + + # proceed as usual + Object.__init__(self, **kwargs) + + # make a list of the properties that were provided + property_list = [k for k, v in self._values.items() + if v is not None + and k not in ('objectName', 'objectType', 'objectIdentifier', 'propertyList') + ] + if _debug: LocalObject._debug(" - property_list: %r", property_list) + + # store the value + self._values['propertyList'] = ArrayOf(PropertyIdentifier)([property_list]) + + def add_property(self, prop): + """Add a property to an object.""" + if _debug: LocalObject._debug("add_property %r", prop) + + # let the Object class do its thing + Object.add_property(self, prop) + + # update the property list + property_list = self.propertyList + property_identifier = prop.identifier + if property_identifier in ('objectName', 'objectType', 'objectIdentifier', 'propertyList'): + pass + elif property_identifier not in property_list: + if _debug: LocalObject._debug(" - adding to property list") + property_list.append(property_identifier) + + def delete_property(self, prop): + """Delete a property from an object.""" + if _debug: LocalObject._debug("delete_property %r", prop) + + # let the Object class do its thing + Object.delete_property(self, prop) + + # remove the property identifier from its list of know properties + property_list = self.propertyList + property_identifier = prop.identifier + if property_identifier in property_list: + if _debug: LocalObject._debug(" - removing from property list") + property_list.remove(property_identifier) + # # ReadProperty and WriteProperty Services # From ea23d5533db856bdf117a00bd86327cfaf1c9e48 Mon Sep 17 00:00:00 2001 From: Joel Bender Date: Fri, 17 Nov 2017 02:06:14 -0500 Subject: [PATCH 2/6] property_list nested too deep --- py27/bacpypes/service/object.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py27/bacpypes/service/object.py b/py27/bacpypes/service/object.py index 0c33999..6d0705e 100644 --- a/py27/bacpypes/service/object.py +++ b/py27/bacpypes/service/object.py @@ -42,7 +42,7 @@ class LocalObject(Object): if _debug: LocalObject._debug(" - property_list: %r", property_list) # store the value - self._values['propertyList'] = ArrayOf(PropertyIdentifier)([property_list]) + self._values['propertyList'] = ArrayOf(PropertyIdentifier)(property_list) def add_property(self, prop): """Add a property to an object.""" From 819a139ae4cdd8f3a02fed8981247391700baa6f Mon Sep 17 00:00:00 2001 From: Joel Bender Date: Sat, 18 Nov 2017 20:24:13 -0500 Subject: [PATCH 3/6] more work on #148 --- py27/bacpypes/object.py | 1 + py27/bacpypes/service/object.py | 97 ++++++++++++++++----------------- 2 files changed, 48 insertions(+), 50 deletions(-) diff --git a/py27/bacpypes/object.py b/py27/bacpypes/object.py index 9bc1c9a..1f5ccd9 100755 --- a/py27/bacpypes/object.py +++ b/py27/bacpypes/object.py @@ -81,6 +81,7 @@ def register_object_type(cls=None, vendor_id=0): # build a property dictionary by going through the class and all its parents _properties = {} for c in cls.__mro__: + if _debug: register_object_type._debug(" - c: %r", c) for prop in getattr(c, 'properties', []): if prop.identifier not in _properties: _properties[prop.identifier] = prop diff --git a/py27/bacpypes/service/object.py b/py27/bacpypes/service/object.py index 6d0705e..51545b4 100644 --- a/py27/bacpypes/service/object.py +++ b/py27/bacpypes/service/object.py @@ -11,12 +11,55 @@ from ..apdu import Error, \ SimpleAckPDU, ReadPropertyACK, ReadPropertyMultipleACK, \ ReadAccessResult, ReadAccessResultElement, ReadAccessResultElementChoice from ..errors import ExecutionError -from ..object import Object, PropertyError +from ..object import Property, Object, PropertyError # some debugging _debug = 0 _log = ModuleLogger(globals()) +# handy reference +ArrayOfPropertyIdentifier = ArrayOf(PropertyIdentifier) + +# +# CurrentPropertyList +# + +@bacpypes_debugging +class CurrentPropertyList(Property): + + def __init__(self): + if _debug: CurrentPropertyList._debug("__init__") + Property.__init__(self, 'propertyList', ArrayOfPropertyIdentifier, default=None, optional=True, mutable=False) + + def ReadProperty(self, obj, arrayIndex=None): + if _debug: CurrentPropertyList._debug("ReadProperty %r %r", obj, arrayIndex) + + # make a list of the properties that have values + property_list = [k for k, v in obj._values.items() + if v is not None + and k not in ('objectName', 'objectType', 'objectIdentifier', 'propertyList') + ] + if _debug: LocalObject._debug(" - property_list: %r", property_list) + + # sort the list so it's stable + property_list.sort() + + # asking for the whole thing + if arrayIndex is None: + return ArrayOfPropertyIdentifier(property_list) + + # asking for the length + if arrayIndex == 0: + return len(property_list) + + # asking for an index + if arrayIndex > len(property_list): + raise ExecutionError(errorClass='property', errorCode='invalidArrayIndex') + return property_list[arrayIndex - 1] + + def WriteProperty(self, obj, value, arrayIndex=None, priority=None, direct=False): + raise ExecutionError(errorClass='property', errorCode='writeAccessDenied') + # # LocalObject # @@ -24,55 +67,9 @@ _log = ModuleLogger(globals()) @bacpypes_debugging class LocalObject(Object): - def __init__(self, **kwargs): - if _debug: LocalObject._debug("__init__ %r", kwargs) - - # check for property list - if 'propertyList' in kwargs: - raise RuntimeError("propertyList is provided by LocalObject and cannot be overridden") - - # proceed as usual - Object.__init__(self, **kwargs) - - # make a list of the properties that were provided - property_list = [k for k, v in self._values.items() - if v is not None - and k not in ('objectName', 'objectType', 'objectIdentifier', 'propertyList') - ] - if _debug: LocalObject._debug(" - property_list: %r", property_list) - - # store the value - self._values['propertyList'] = ArrayOf(PropertyIdentifier)(property_list) - - def add_property(self, prop): - """Add a property to an object.""" - if _debug: LocalObject._debug("add_property %r", prop) - - # let the Object class do its thing - Object.add_property(self, prop) - - # update the property list - property_list = self.propertyList - property_identifier = prop.identifier - if property_identifier in ('objectName', 'objectType', 'objectIdentifier', 'propertyList'): - pass - elif property_identifier not in property_list: - if _debug: LocalObject._debug(" - adding to property list") - property_list.append(property_identifier) - - def delete_property(self, prop): - """Delete a property from an object.""" - if _debug: LocalObject._debug("delete_property %r", prop) - - # let the Object class do its thing - Object.delete_property(self, prop) - - # remove the property identifier from its list of know properties - property_list = self.propertyList - property_identifier = prop.identifier - if property_identifier in property_list: - if _debug: LocalObject._debug(" - removing from property list") - property_list.remove(property_identifier) + properties = [ + CurrentPropertyList(), + ] # # ReadProperty and WriteProperty Services From 8929c60cc3dd3fdd744d6f751c09033194c8ba89 Mon Sep 17 00:00:00 2001 From: Joel Bender Date: Mon, 20 Nov 2017 21:58:40 -0500 Subject: [PATCH 4/6] renamed the class --- py27/bacpypes/service/device.py | 6 +++--- py27/bacpypes/service/object.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/py27/bacpypes/service/device.py b/py27/bacpypes/service/device.py index 873dc4f..29fbd37 100644 --- a/py27/bacpypes/service/device.py +++ b/py27/bacpypes/service/device.py @@ -14,7 +14,7 @@ from ..object import register_object_type, registered_object_types, \ Property, DeviceObject from ..task import FunctionTask -from .object import LocalObject +from .object import CurrentPropertyListMixIn # some debugging _debug = 0 @@ -69,7 +69,7 @@ class CurrentTimeProperty(Property): # @bacpypes_debugging -class LocalDeviceObject(LocalObject, DeviceObject): +class LocalDeviceObject(CurrentPropertyListMixIn, DeviceObject): properties = \ [ CurrentTimeProperty('localTime') @@ -130,7 +130,7 @@ class LocalDeviceObject(LocalObject, DeviceObject): if _debug: LocalDeviceObject._debug(" - updated kwargs: %r", kwargs) # proceed as usual - LocalObject.__init__(self, **kwargs) + super(LocalDeviceObject, self).__init__(**kwargs) # # Who-Is I-Am Services diff --git a/py27/bacpypes/service/object.py b/py27/bacpypes/service/object.py index 51545b4..ca8d3fe 100644 --- a/py27/bacpypes/service/object.py +++ b/py27/bacpypes/service/object.py @@ -39,7 +39,7 @@ class CurrentPropertyList(Property): if v is not None and k not in ('objectName', 'objectType', 'objectIdentifier', 'propertyList') ] - if _debug: LocalObject._debug(" - property_list: %r", property_list) + if _debug: CurrentPropertyList._debug(" - property_list: %r", property_list) # sort the list so it's stable property_list.sort() @@ -61,11 +61,11 @@ class CurrentPropertyList(Property): raise ExecutionError(errorClass='property', errorCode='writeAccessDenied') # -# LocalObject +# CurrentPropertyListMixIn # @bacpypes_debugging -class LocalObject(Object): +class CurrentPropertyListMixIn(Object): properties = [ CurrentPropertyList(), From 51d6782b3c8530002de4d78d5d73f853845156ea Mon Sep 17 00:00:00 2001 From: Joel Bender Date: Mon, 20 Nov 2017 23:28:24 -0500 Subject: [PATCH 5/6] sync the python versions --- py25/bacpypes/iocb.py | 3 +- py25/bacpypes/object.py | 29 --------------- py25/bacpypes/service/device.py | 31 +++++++++-------- py25/bacpypes/service/object.py | 60 ++++++++++++++++++++++++++++++-- py27/bacpypes/basetypes.py | 1 + py27/bacpypes/iocb.py | 5 +-- py34/bacpypes/constructeddata.py | 2 +- py34/bacpypes/object.py | 29 --------------- py34/bacpypes/service/device.py | 31 +++++++++-------- py34/bacpypes/service/object.py | 60 ++++++++++++++++++++++++++++++-- 10 files changed, 153 insertions(+), 98 deletions(-) diff --git a/py25/bacpypes/iocb.py b/py25/bacpypes/iocb.py index 880e2ce..6906d3c 100644 --- a/py25/bacpypes/iocb.py +++ b/py25/bacpypes/iocb.py @@ -723,6 +723,7 @@ class IOQController(IOController): # if there was an error, abort the request if err: + if _debug: IOQController._debug(" - aborting") self.abort_io(iocb, err) def process_io(self, iocb): @@ -767,7 +768,7 @@ class IOQController(IOController): # schedule a call in the future task = FunctionTask(IOQController._wait_trigger, self) - task.install_task(delay=self.wait_time) + task.install_task(delta=self.wait_time) else: # change our state diff --git a/py25/bacpypes/object.py b/py25/bacpypes/object.py index 1ec58d0..3bbac83 100755 --- a/py25/bacpypes/object.py +++ b/py25/bacpypes/object.py @@ -437,13 +437,6 @@ class Object(Logging): # empty list of property monitors self._property_monitors = defaultdict(list) - # start with a clean array of property identifiers - if 'propertyList' in initargs: - propertyList = None - else: - propertyList = ArrayOf(PropertyIdentifier)() - initargs['propertyList'] = propertyList - # initialize the object for propid, prop in self._properties.items(): if propid in initargs: @@ -452,20 +445,12 @@ class Object(Logging): # defer to the property object for error checking prop.WriteProperty(self, initargs[propid], direct=True) - # add it to the property list if we are building one - if propertyList is not None: - propertyList.append(propid) - elif prop.default is not None: if _debug: Object._debug(" - setting %s from default", propid) # default values bypass property interface self._values[propid] = prop.default - # add it to the property list if we are building one - if propertyList is not None: - propertyList.append(propid) - else: if not prop.optional: if _debug: Object._debug(" - %s value required", propid) @@ -526,13 +511,6 @@ class Object(Logging): self._properties[prop.identifier] = prop self._values[prop.identifier] = prop.default - # tell the object it has a new property - if 'propertyList' in self._values: - property_list = self.propertyList - if prop.identifier not in property_list: - if _debug: Object._debug(" - adding to property list") - property_list.append(prop.identifier) - def delete_property(self, prop): """Delete a property from an object. The property is an instance of a Property or one of its derived classes, but only the property @@ -548,13 +526,6 @@ class Object(Logging): if prop.identifier in self._values: del self._values[prop.identifier] - # remove the property identifier from its list of know properties - if 'propertyList' in self._values: - property_list = self.propertyList - if prop.identifier in property_list: - if _debug: Object._debug(" - removing from property list") - property_list.remove(prop.identifier) - def ReadProperty(self, propid, arrayIndex=None): if _debug: Object._debug("ReadProperty %r arrayIndex=%r", propid, arrayIndex) diff --git a/py25/bacpypes/service/device.py b/py25/bacpypes/service/device.py index ac90405..accf2be 100644 --- a/py25/bacpypes/service/device.py +++ b/py25/bacpypes/service/device.py @@ -14,6 +14,8 @@ from ..object import register_object_type, registered_object_types, \ Property, DeviceObject from ..task import FunctionTask +from .object import CurrentPropertyListMixIn + # some debugging _debug = 0 _log = ModuleLogger(globals()) @@ -66,7 +68,7 @@ class CurrentTimeProperty(Property): # LocalDeviceObject # -class LocalDeviceObject(DeviceObject): +class LocalDeviceObject(CurrentPropertyListMixIn, DeviceObject): properties = \ [ CurrentTimeProperty('localTime') @@ -107,6 +109,18 @@ class LocalDeviceObject(DeviceObject): if 'localTime' in kwargs: raise RuntimeError("localTime is provided by LocalDeviceObject and cannot be overridden") + # the object identifier is required for the object list + if 'objectIdentifier' not in kwargs: + raise RuntimeError("objectIdentifier is required") + + # the object list is provided + if 'objectList' in kwargs: + raise RuntimeError("objectList is provided by LocalDeviceObject and cannot be overridden") + else: + kwargs['objectList'] = ArrayOf(ObjectIdentifier)([ + kwargs['objectIdentifier'], + ]) + # check for a minimum value if kwargs['maxApduLengthAccepted'] < 50: raise ValueError("invalid max APDU length accepted") @@ -115,20 +129,7 @@ class LocalDeviceObject(DeviceObject): if _debug: LocalDeviceObject._debug(" - updated kwargs: %r", kwargs) # proceed as usual - DeviceObject.__init__(self, **kwargs) - - # create a default implementation of an object list for local devices. - # If it is specified in the kwargs, that overrides this default. - if ('objectList' not in kwargs): - self.objectList = ArrayOf(ObjectIdentifier)([self.objectIdentifier]) - - # if the object has a property list and one wasn't provided - # in the kwargs, then it was created by default and the objectList - # property should be included - if ('propertyList' not in kwargs) and self.propertyList: - # make sure it's not already there - if 'objectList' not in self.propertyList: - self.propertyList.append('objectList') + super(LocalDeviceObject, self).__init__(**kwargs) bacpypes_debugging(LocalDeviceObject) diff --git a/py25/bacpypes/service/object.py b/py25/bacpypes/service/object.py index 60f87e8..ae8e7da 100755 --- a/py25/bacpypes/service/object.py +++ b/py25/bacpypes/service/object.py @@ -3,20 +3,74 @@ from ..debugging import bacpypes_debugging, ModuleLogger from ..capability import Capability -from ..basetypes import ErrorType +from ..basetypes import ErrorType, PropertyIdentifier from ..primitivedata import Atomic, Null, Unsigned -from ..constructeddata import Any, Array +from ..constructeddata import Any, Array, ArrayOf from ..apdu import Error, \ SimpleAckPDU, ReadPropertyACK, ReadPropertyMultipleACK, \ ReadAccessResult, ReadAccessResultElement, ReadAccessResultElementChoice from ..errors import ExecutionError -from ..object import PropertyError +from ..object import Property, Object, PropertyError # some debugging _debug = 0 _log = ModuleLogger(globals()) +# handy reference +ArrayOfPropertyIdentifier = ArrayOf(PropertyIdentifier) + +# +# CurrentPropertyList +# + +@bacpypes_debugging +class CurrentPropertyList(Property): + + def __init__(self): + if _debug: CurrentPropertyList._debug("__init__") + Property.__init__(self, 'propertyList', ArrayOfPropertyIdentifier, default=None, optional=True, mutable=False) + + def ReadProperty(self, obj, arrayIndex=None): + if _debug: CurrentPropertyList._debug("ReadProperty %r %r", obj, arrayIndex) + + # make a list of the properties that have values + property_list = [k for k, v in obj._values.items() + if v is not None + and k not in ('objectName', 'objectType', 'objectIdentifier', 'propertyList') + ] + if _debug: CurrentPropertyList._debug(" - property_list: %r", property_list) + + # sort the list so it's stable + property_list.sort() + + # asking for the whole thing + if arrayIndex is None: + return ArrayOfPropertyIdentifier(property_list) + + # asking for the length + if arrayIndex == 0: + return len(property_list) + + # asking for an index + if arrayIndex > len(property_list): + raise ExecutionError(errorClass='property', errorCode='invalidArrayIndex') + return property_list[arrayIndex - 1] + + def WriteProperty(self, obj, value, arrayIndex=None, priority=None, direct=False): + raise ExecutionError(errorClass='property', errorCode='writeAccessDenied') + +# +# CurrentPropertyListMixIn +# + +@bacpypes_debugging +class CurrentPropertyListMixIn(Object): + + properties = [ + CurrentPropertyList(), + ] + # # ReadProperty and WriteProperty Services # diff --git a/py27/bacpypes/basetypes.py b/py27/bacpypes/basetypes.py index d924484..09d0985 100755 --- a/py27/bacpypes/basetypes.py +++ b/py27/bacpypes/basetypes.py @@ -513,6 +513,7 @@ class EngineeringUnits(Enumerated): , 'megavoltAmpereHoursReactive':244 #Mvarh , 'voltsPerDegreeKelvin':176 , 'voltsPerMeter':177 + , 'voltsSquareHours':245 , 'degreesPhase':14 , 'powerFactor':15 , 'webers':178 diff --git a/py27/bacpypes/iocb.py b/py27/bacpypes/iocb.py index 02d0880..db96cba 100644 --- a/py27/bacpypes/iocb.py +++ b/py27/bacpypes/iocb.py @@ -209,7 +209,7 @@ class IOCB(DebugContents): self.ioTimeout = FunctionTask(self.abort, err) # (re)schedule it - self.ioTimeout.install_task(delay=delay) + self.ioTimeout.install_task(delta=delay) def __repr__(self): xid = id(self) @@ -718,6 +718,7 @@ class IOQController(IOController): # if there was an error, abort the request if err: + if _debug: IOQController._debug(" - aborting") self.abort_io(iocb, err) def process_io(self, iocb): @@ -762,7 +763,7 @@ class IOQController(IOController): # schedule a call in the future task = FunctionTask(IOQController._wait_trigger, self) - task.install_task(delay=self.wait_time) + task.install_task(delta=self.wait_time) else: # change our state diff --git a/py34/bacpypes/constructeddata.py b/py34/bacpypes/constructeddata.py index 3890683..88fe28d 100755 --- a/py34/bacpypes/constructeddata.py +++ b/py34/bacpypes/constructeddata.py @@ -928,7 +928,7 @@ class Choice(object): # check for the correct closing tag tag = taglist.Pop() if tag.tagClass != Tag.closingTagClass or tag.tagNumber != element.context: - raise DecodingError("'%s' expected closing tag %d" % (element.name, element.context)) + raise InvalidTag("%s expected closing tag %d" % (element.name, element.context)) # done if _debug: Choice._debug(" - found choice (structure)") diff --git a/py34/bacpypes/object.py b/py34/bacpypes/object.py index ec41daf..9f31dd9 100755 --- a/py34/bacpypes/object.py +++ b/py34/bacpypes/object.py @@ -444,13 +444,6 @@ class Object: # empty list of property monitors self._property_monitors = defaultdict(list) - # start with a clean array of property identifiers - if 'propertyList' in initargs: - propertyList = None - else: - propertyList = ArrayOf(PropertyIdentifier)() - initargs['propertyList'] = propertyList - # initialize the object for propid, prop in self._properties.items(): if propid in initargs: @@ -459,20 +452,12 @@ class Object: # defer to the property object for error checking prop.WriteProperty(self, initargs[propid], direct=True) - # add it to the property list if we are building one - if propertyList is not None: - propertyList.append(propid) - elif prop.default is not None: if _debug: Object._debug(" - setting %s from default", propid) # default values bypass property interface self._values[propid] = prop.default - # add it to the property list if we are building one - if propertyList is not None: - propertyList.append(propid) - else: if not prop.optional: if _debug: Object._debug(" - %s value required", propid) @@ -533,13 +518,6 @@ class Object: self._properties[prop.identifier] = prop self._values[prop.identifier] = prop.default - # tell the object it has a new property - if 'propertyList' in self._values: - property_list = self.propertyList - if prop.identifier not in property_list: - if _debug: Object._debug(" - adding to property list") - property_list.append(prop.identifier) - def delete_property(self, prop): """Delete a property from an object. The property is an instance of a Property or one of its derived classes, but only the property @@ -555,13 +533,6 @@ class Object: if prop.identifier in self._values: del self._values[prop.identifier] - # remove the property identifier from its list of know properties - if 'propertyList' in self._values: - property_list = self.propertyList - if prop.identifier in property_list: - if _debug: Object._debug(" - removing from property list") - property_list.remove(prop.identifier) - def ReadProperty(self, propid, arrayIndex=None): if _debug: Object._debug("ReadProperty %r arrayIndex=%r", propid, arrayIndex) diff --git a/py34/bacpypes/service/device.py b/py34/bacpypes/service/device.py index 0382f19..5fad047 100644 --- a/py34/bacpypes/service/device.py +++ b/py34/bacpypes/service/device.py @@ -14,6 +14,8 @@ from ..object import register_object_type, registered_object_types, \ Property, DeviceObject from ..task import FunctionTask +from .object import CurrentPropertyListMixIn + # some debugging _debug = 0 _log = ModuleLogger(globals()) @@ -67,7 +69,7 @@ class CurrentTimeProperty(Property): # @bacpypes_debugging -class LocalDeviceObject(DeviceObject): +class LocalDeviceObject(CurrentPropertyListMixIn, DeviceObject): properties = \ [ CurrentTimeProperty('localTime') @@ -108,6 +110,18 @@ class LocalDeviceObject(DeviceObject): if 'localTime' in kwargs: raise RuntimeError("localTime is provided by LocalDeviceObject and cannot be overridden") + # the object identifier is required for the object list + if 'objectIdentifier' not in kwargs: + raise RuntimeError("objectIdentifier is required") + + # the object list is provided + if 'objectList' in kwargs: + raise RuntimeError("objectList is provided by LocalDeviceObject and cannot be overridden") + else: + kwargs['objectList'] = ArrayOf(ObjectIdentifier)([ + kwargs['objectIdentifier'], + ]) + # check for a minimum value if kwargs['maxApduLengthAccepted'] < 50: raise ValueError("invalid max APDU length accepted") @@ -116,20 +130,7 @@ class LocalDeviceObject(DeviceObject): if _debug: LocalDeviceObject._debug(" - updated kwargs: %r", kwargs) # proceed as usual - DeviceObject.__init__(self, **kwargs) - - # create a default implementation of an object list for local devices. - # If it is specified in the kwargs, that overrides this default. - if ('objectList' not in kwargs): - self.objectList = ArrayOf(ObjectIdentifier)([self.objectIdentifier]) - - # if the object has a property list and one wasn't provided - # in the kwargs, then it was created by default and the objectList - # property should be included - if ('propertyList' not in kwargs) and self.propertyList: - # make sure it's not already there - if 'objectList' not in self.propertyList: - self.propertyList.append('objectList') + super(LocalDeviceObject, self).__init__(**kwargs) # # Who-Is I-Am Services diff --git a/py34/bacpypes/service/object.py b/py34/bacpypes/service/object.py index 6cbcf13..ca8d3fe 100755 --- a/py34/bacpypes/service/object.py +++ b/py34/bacpypes/service/object.py @@ -3,20 +3,74 @@ from ..debugging import bacpypes_debugging, ModuleLogger from ..capability import Capability -from ..basetypes import ErrorType +from ..basetypes import ErrorType, PropertyIdentifier from ..primitivedata import Atomic, Null, Unsigned -from ..constructeddata import Any, Array +from ..constructeddata import Any, Array, ArrayOf from ..apdu import Error, \ SimpleAckPDU, ReadPropertyACK, ReadPropertyMultipleACK, \ ReadAccessResult, ReadAccessResultElement, ReadAccessResultElementChoice from ..errors import ExecutionError -from ..object import PropertyError +from ..object import Property, Object, PropertyError # some debugging _debug = 0 _log = ModuleLogger(globals()) +# handy reference +ArrayOfPropertyIdentifier = ArrayOf(PropertyIdentifier) + +# +# CurrentPropertyList +# + +@bacpypes_debugging +class CurrentPropertyList(Property): + + def __init__(self): + if _debug: CurrentPropertyList._debug("__init__") + Property.__init__(self, 'propertyList', ArrayOfPropertyIdentifier, default=None, optional=True, mutable=False) + + def ReadProperty(self, obj, arrayIndex=None): + if _debug: CurrentPropertyList._debug("ReadProperty %r %r", obj, arrayIndex) + + # make a list of the properties that have values + property_list = [k for k, v in obj._values.items() + if v is not None + and k not in ('objectName', 'objectType', 'objectIdentifier', 'propertyList') + ] + if _debug: CurrentPropertyList._debug(" - property_list: %r", property_list) + + # sort the list so it's stable + property_list.sort() + + # asking for the whole thing + if arrayIndex is None: + return ArrayOfPropertyIdentifier(property_list) + + # asking for the length + if arrayIndex == 0: + return len(property_list) + + # asking for an index + if arrayIndex > len(property_list): + raise ExecutionError(errorClass='property', errorCode='invalidArrayIndex') + return property_list[arrayIndex - 1] + + def WriteProperty(self, obj, value, arrayIndex=None, priority=None, direct=False): + raise ExecutionError(errorClass='property', errorCode='writeAccessDenied') + +# +# CurrentPropertyListMixIn +# + +@bacpypes_debugging +class CurrentPropertyListMixIn(Object): + + properties = [ + CurrentPropertyList(), + ] + # # ReadProperty and WriteProperty Services # From b786ff0f71ec8ac89769621f7d22845503cde9cf Mon Sep 17 00:00:00 2001 From: Joel Bender Date: Fri, 1 Dec 2017 01:06:55 -0500 Subject: [PATCH 6/6] give the local date and time properties a default value so it's not None, the value is never read or written --- py25/bacpypes/service/device.py | 4 ++-- py27/bacpypes/service/device.py | 4 ++-- py34/bacpypes/service/device.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/py25/bacpypes/service/device.py b/py25/bacpypes/service/device.py index accf2be..87e618f 100644 --- a/py25/bacpypes/service/device.py +++ b/py25/bacpypes/service/device.py @@ -27,7 +27,7 @@ _log = ModuleLogger(globals()) class CurrentDateProperty(Property): def __init__(self, identifier): - Property.__init__(self, identifier, Date, default=None, optional=True, mutable=False) + Property.__init__(self, identifier, Date, default=(), optional=True, mutable=False) def ReadProperty(self, obj, arrayIndex=None): # access an array @@ -49,7 +49,7 @@ class CurrentDateProperty(Property): class CurrentTimeProperty(Property): def __init__(self, identifier): - Property.__init__(self, identifier, Time, default=None, optional=True, mutable=False) + Property.__init__(self, identifier, Time, default=(), optional=True, mutable=False) def ReadProperty(self, obj, arrayIndex=None): # access an array diff --git a/py27/bacpypes/service/device.py b/py27/bacpypes/service/device.py index 29fbd37..647714b 100644 --- a/py27/bacpypes/service/device.py +++ b/py27/bacpypes/service/device.py @@ -27,7 +27,7 @@ _log = ModuleLogger(globals()) class CurrentDateProperty(Property): def __init__(self, identifier): - Property.__init__(self, identifier, Date, default=None, optional=True, mutable=False) + Property.__init__(self, identifier, Date, default=(), optional=True, mutable=False) def ReadProperty(self, obj, arrayIndex=None): # access an array @@ -49,7 +49,7 @@ class CurrentDateProperty(Property): class CurrentTimeProperty(Property): def __init__(self, identifier): - Property.__init__(self, identifier, Time, default=None, optional=True, mutable=False) + Property.__init__(self, identifier, Time, default=(), optional=True, mutable=False) def ReadProperty(self, obj, arrayIndex=None): # access an array diff --git a/py34/bacpypes/service/device.py b/py34/bacpypes/service/device.py index 5fad047..f8871c7 100644 --- a/py34/bacpypes/service/device.py +++ b/py34/bacpypes/service/device.py @@ -27,7 +27,7 @@ _log = ModuleLogger(globals()) class CurrentDateProperty(Property): def __init__(self, identifier): - Property.__init__(self, identifier, Date, default=None, optional=True, mutable=False) + Property.__init__(self, identifier, Date, default=(), optional=True, mutable=False) def ReadProperty(self, obj, arrayIndex=None): # access an array @@ -49,7 +49,7 @@ class CurrentDateProperty(Property): class CurrentTimeProperty(Property): def __init__(self, identifier): - Property.__init__(self, identifier, Time, default=None, optional=True, mutable=False) + Property.__init__(self, identifier, Time, default=(), optional=True, mutable=False) def ReadProperty(self, obj, arrayIndex=None): # access an array