mirror of
https://github.com/JoelBender/bacpypes
synced 2025-09-28 22:15:23 +08:00
merging #148 for next release
This commit is contained in:
commit
513e9dac3d
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
@ -25,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
|
||||
|
@ -47,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
|
||||
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
#
|
||||
|
|
|
@ -513,6 +513,7 @@ class EngineeringUnits(Enumerated):
|
|||
, 'megavoltAmpereHoursReactive':244 #Mvarh
|
||||
, 'voltsPerDegreeKelvin':176
|
||||
, 'voltsPerMeter':177
|
||||
, 'voltsSquareHours':245
|
||||
, 'degreesPhase':14
|
||||
, 'powerFactor':15
|
||||
, 'webers':178
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -444,13 +445,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:
|
||||
|
@ -459,20 +453,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)
|
||||
|
@ -533,13 +519,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
|
||||
|
@ -555,13 +534,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)
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
@ -25,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
|
||||
|
@ -47,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
|
||||
|
@ -67,7 +69,7 @@ class CurrentTimeProperty(Property):
|
|||
#
|
||||
|
||||
@bacpypes_debugging
|
||||
class LocalDeviceObject(DeviceObject):
|
||||
class LocalDeviceObject(CurrentPropertyListMixIn, 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')
|
||||
super(LocalDeviceObject, self).__init__(**kwargs)
|
||||
|
||||
#
|
||||
# Who-Is I-Am Services
|
||||
|
|
|
@ -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
|
||||
#
|
||||
|
|
|
@ -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)")
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
@ -25,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
|
||||
|
@ -47,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
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
||||
#
|
||||
|
|
|
@ -16,6 +16,8 @@ from bacpypes.constructeddata import ArrayOf
|
|||
from bacpypes.object import register_object_type, ReadableProperty, \
|
||||
WritableProperty, Object
|
||||
|
||||
from bacpypes.service.object import CurrentPropertyListMixIn
|
||||
|
||||
# some debugging
|
||||
_debug = 0
|
||||
_log = ModuleLogger(globals())
|
||||
|
@ -193,3 +195,62 @@ class TestWritableArray(unittest.TestCase):
|
|||
assert obj.ReadProperty('location', 1) == "home"
|
||||
assert obj.ReadProperty('location', 2) == "work"
|
||||
|
||||
|
||||
@bacpypes_debugging
|
||||
@register_object_type(vendor_id=999)
|
||||
class SampleLocationObject(CurrentPropertyListMixIn, Object):
|
||||
|
||||
objectType = 'sampleLocationObject'
|
||||
properties = [
|
||||
WritableProperty('location', CharacterString),
|
||||
]
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
if _debug: SampleWritableArray._debug("__init__ %r", kwargs)
|
||||
Object.__init__(self, **kwargs)
|
||||
|
||||
|
||||
@bacpypes_debugging
|
||||
class TestCurrentPropertyListMixIn(unittest.TestCase):
|
||||
|
||||
def test_with_location(self):
|
||||
if _debug: TestCurrentPropertyListMixIn._debug("test_with_location")
|
||||
|
||||
# create an object without a location
|
||||
obj = SampleLocationObject(location="home")
|
||||
if _debug: TestCurrentPropertyListMixIn._debug(" - obj.location: %r", obj.location)
|
||||
|
||||
assert obj.propertyList.value == [1, "location"]
|
||||
|
||||
def test_without_location(self):
|
||||
if _debug: TestCurrentPropertyListMixIn._debug("test_property_list_1")
|
||||
|
||||
# create an object without a location
|
||||
obj = SampleLocationObject()
|
||||
if _debug: TestCurrentPropertyListMixIn._debug(" - obj.location: %r", obj.location)
|
||||
|
||||
assert obj.propertyList.value == [0]
|
||||
|
||||
def test_location_appears(self):
|
||||
if _debug: TestCurrentPropertyListMixIn._debug("test_location_appears")
|
||||
|
||||
# create an object without a location
|
||||
obj = SampleLocationObject()
|
||||
if _debug: TestCurrentPropertyListMixIn._debug(" - obj.location: %r", obj.location)
|
||||
|
||||
# give it a location
|
||||
obj.location = "away"
|
||||
assert obj.propertyList.value == [1, "location"]
|
||||
|
||||
def test_location_disappears(self):
|
||||
if _debug: TestCurrentPropertyListMixIn._debug("test_location_disappears")
|
||||
|
||||
# create an object without a location
|
||||
obj = SampleLocationObject(location="home")
|
||||
if _debug: TestCurrentPropertyListMixIn._debug(" - obj.location: %r", obj.location)
|
||||
|
||||
# location 'removed'
|
||||
obj.location = None
|
||||
|
||||
assert obj.propertyList.value == [0]
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user