1
0
mirror of https://github.com/JoelBender/bacpypes synced 2025-09-28 22:15:23 +08:00

strip out unused imports, remove localAddress from application layer with a deprecation warning, add local address to BIP in case it's needed by other applications

This commit is contained in:
Joel Bender 2016-08-29 16:40:12 -04:00
parent 431eaebe36
commit 147b29c79a
6 changed files with 216 additions and 133 deletions

View File

@ -4,39 +4,33 @@
Application Module
"""
import warnings
from .debugging import bacpypes_debugging, DebugContents, ModuleLogger
from .comm import ApplicationServiceElement, bind
from .pdu import Address, LocalStation, RemoteStation
from .pdu import Address
from .primitivedata import Atomic, Date, Null, ObjectIdentifier, Time, Unsigned
from .constructeddata import Any, Array, ArrayOf
from .primitivedata import Date, Time, ObjectIdentifier
from .constructeddata import ArrayOf
from .capability import Collector
from .appservice import StateMachineAccessPoint, ApplicationServiceAccessPoint
from .netservice import NetworkServiceAccessPoint, NetworkServiceElement
from .bvllservice import BIPSimple, BIPForeign, AnnexJCodec, UDPMultiplexer
from .object import Property, PropertyError, DeviceObject, \
from .object import Property, DeviceObject, \
registered_object_types, register_object_type
from .apdu import ConfirmedRequestPDU, SimpleAckPDU, RejectPDU, RejectReason
from .apdu import IAmRequest, ReadPropertyACK, Error
from .errors import ExecutionError, \
RejectException, UnrecognizedService, MissingRequiredParameter, \
ParameterOutOfRange, \
AbortException
from .apdu import ConfirmedRequestPDU, Error
from .errors import ExecutionError, UnrecognizedService, AbortException, RejectException
# for computing protocol services supported
from .apdu import confirmed_request_types, unconfirmed_request_types, \
ConfirmedServiceChoice, UnconfirmedServiceChoice
from .basetypes import ServicesSupported
from .apdu import \
AtomicReadFileACK, \
AtomicReadFileACKAccessMethodChoice, \
AtomicReadFileACKAccessMethodRecordAccess, \
AtomicReadFileACKAccessMethodStreamAccess, \
AtomicWriteFileACK
# basic services
from .service.device import WhoIsIAmServices, ReadWritePropertyServices
# some debugging
_debug = 0
@ -303,30 +297,42 @@ class LocalDeviceObject(DeviceObject):
@bacpypes_debugging
class Application(ApplicationServiceElement, Collector):
def __init__(self, localDevice, localAddress, deviceInfoCache=None, aseID=None):
def __init__(self, localDevice=None, localAddress=None, deviceInfoCache=None, aseID=None):
if _debug: Application._debug("__init__ %r %r deviceInfoCache=%r aseID=%r", localDevice, localAddress, deviceInfoCache, aseID)
ApplicationServiceElement.__init__(self, aseID)
Collector.__init__(self)
# local objects by ID and name
self.objectName = {}
self.objectIdentifier = {}
# keep track of the local device
self.localDevice = localDevice
if localDevice:
self.localDevice = localDevice
# bind the device object to this application
localDevice._app = self
# local objects by ID and name
self.objectName[localDevice.objectName] = localDevice
self.objectIdentifier[localDevice.objectIdentifier] = localDevice
# local address deprecated, but continue to use the old initializer
if localAddress is not None:
warnings.warn(
"local address at the application layer deprecated",
DeprecationWarning,
)
# allow the address to be cast to the correct type
if isinstance(localAddress, Address):
self.localAddress = localAddress
else:
self.localAddress = Address(localAddress)
# use the provided cache or make a default one
self.deviceInfoCache = deviceInfoCache or DeviceInfoCache()
# bind the device object to this application
localDevice._app = self
# allow the address to be cast to the correct type
if isinstance(localAddress, Address):
self.localAddress = localAddress
else:
self.localAddress = Address(localAddress)
# local objects by ID and name
self.objectName = {localDevice.objectName:localDevice}
self.objectIdentifier = {localDevice.objectIdentifier:localDevice}
def add_object(self, obj):
"""Add an object to the local collection."""
if _debug: Application._debug("add_object %r", obj)
@ -353,8 +359,10 @@ class Application(ApplicationServiceElement, Collector):
self.objectName[object_name] = obj
self.objectIdentifier[object_identifier] = obj
# append the new object's identifier to the device's object list
self.localDevice.objectList.append(object_identifier)
# append the new object's identifier to the local device's object list
# if there is one and it has an object list property
if self.localDevice and self.localDevice.objectList:
self.localDevice.objectList.append(object_identifier)
# let the object know which application stack it belongs to
obj._app = self
@ -372,8 +380,10 @@ class Application(ApplicationServiceElement, Collector):
del self.objectIdentifier[object_identifier]
# remove the object's identifier from the device's object list
indx = self.localDevice.objectList.index(object_identifier)
del self.localDevice.objectList[indx]
# if there is one and it has an object list property
if self.localDevice and self.localDevice.objectList:
indx = self.localDevice.objectList.index(object_identifier)
del self.localDevice.objectList[indx]
# make sure the object knows it's detached from an application
obj._app = None
@ -460,11 +470,17 @@ class Application(ApplicationServiceElement, Collector):
#
@bacpypes_debugging
class BIPSimpleApplication(Application):
class BIPSimpleApplication(Application, WhoIsIAmServices, ReadWritePropertyServices):
def __init__(self, localDevice, localAddress, deviceInfoCache=None, aseID=None):
if _debug: BIPSimpleApplication._debug("__init__ %r %r deviceInfoCache=%r aseID=%r", localDevice, localAddress, deviceInfoCache, aseID)
Application.__init__(self, localDevice, localAddress, deviceInfoCache, aseID)
Application.__init__(self, localDevice, deviceInfoCache, aseID)
# local address might be useful for subclasses
if isinstance(localAddress, Address):
self.localAddress = localAddress
else:
self.localAddress = Address(localAddress)
# include a application decoder
self.asap = ApplicationServiceAccessPoint()
@ -504,11 +520,17 @@ class BIPSimpleApplication(Application):
#
@bacpypes_debugging
class BIPForeignApplication(Application):
class BIPForeignApplication(Application, WhoIsIAmServices, ReadWritePropertyServices):
def __init__(self, localDevice, localAddress, bbmdAddress, bbmdTTL, aseID=None):
if _debug: BIPForeignApplication._debug("__init__ %r %r %r %r aseID=%r", localDevice, localAddress, bbmdAddress, bbmdTTL, aseID)
Application.__init__(self, localDevice, localAddress, aseID)
Application.__init__(self, localDevice, aseID)
# local address might be useful for subclasses
if isinstance(localAddress, Address):
self.localAddress = localAddress
else:
self.localAddress = Address(localAddress)
# include a application decoder
self.asap = ApplicationServiceAccessPoint()

View File

@ -1061,8 +1061,7 @@ class StateMachineAccessPoint(Client, ServiceAccessPoint):
Client.__init__(self, cid)
ServiceAccessPoint.__init__(self, sap)
# save a reference to the local device object and the cache
self.localDevice = localDevice
# save a reference to the device information cache
self.deviceInfoCache = deviceInfoCache
# client settings

View File

@ -7,4 +7,6 @@ Service Subpackage
from . import test
from . import device
from . import cov
from . import file

168
py27/bacpypes/service/device.py Executable file → Normal file
View File

@ -4,7 +4,17 @@ from ..debugging import bacpypes_debugging, ModuleLogger
from ..capability import Capability
from ..pdu import GlobalBroadcast
from ..apdu import WhoIsRequest, IAmRequest
from ..basetypes import ErrorType
from ..primitivedata import Atomic, Null, Unsigned
from ..constructeddata import Any, Array
from ..apdu import Error, WhoIsRequest, IAmRequest, \
SimpleAckPDU, ReadPropertyACK, ReadPropertyMultipleACK, \
ReadAccessResult, ReadAccessResultElement, ReadAccessResultElementChoice
from ..errors import ExecutionError, InconsistentParameters, \
MissingRequiredParameter, ParameterOutOfRange
from ..object import PropertyError
# some debugging
_debug = 0
@ -159,6 +169,8 @@ class WhoHasIHaveServices(Capability):
def who_has(self, thing, address=None):
if _debug: WhoHasIHaveServices._debug("who_has %r address=%r", thing, address)
raise NotImplementedError("i_have")
def do_WhoHasRequest(self, apdu):
"""Respond to a Who-Has request."""
if _debug: WhoHasIHaveServices._debug("do_WhoHasRequest, %r", apdu)
@ -168,19 +180,24 @@ class WhoHasIHaveServices(Capability):
if _debug: WhoIsIAmServices._debug(" - no local device")
return
key = (str(apdu.pduSource),)
# find the object
if apdu.object.objectIdentifier is not None:
key += (str(apdu.object.objectIdentifier),)
obj = self.objectIdentifier.get(apdu.object.objectIdentifier, None)
elif apdu.object.objectName is not None:
key += (apdu.object.objectName,)
obj = self.objectName.get(apdu.object.objectName, None)
else:
raise InconsistentParameters("object identifier or object name required")
if not obj:
raise Error(errorClass='object', errorCode='unknownObject')
### check the objects for a match, call self.i_have(obj, address=apdu.pduSource)
# # send out the response
# self.i_have(obj, address=apdu.pduSource)
def i_have(self, thing, address=None):
if _debug: WhoHasIHaveServices._debug("i_have %r address=%r", thing, address)
raise NotImplementedError("i_have")
def do_IHaveRequest(self, apdu):
"""Respond to a I-Have request."""
if _debug: WhoHasIHaveServices._debug("do_IHaveRequest %r", apdu)
@ -203,109 +220,108 @@ class ReadWritePropertyServices(Capability):
def do_ReadPropertyRequest(self, apdu):
"""Return the value of some property of one of our objects."""
if _debug: Application._debug("do_ReadPropertyRequest %r", apdu)
if _debug: ReadWritePropertyServices._debug("do_ReadPropertyRequest %r", apdu)
# extract the object identifier
objId = apdu.objectIdentifier
# check for wildcard
if (objId == ('device', 4194303)):
if _debug: Application._debug(" - wildcard device identifier")
if _debug: ReadWritePropertyServices._debug(" - wildcard device identifier")
objId = self.localDevice.objectIdentifier
# get the object
obj = self.get_object_id(objId)
if _debug: Application._debug(" - object: %r", obj)
if _debug: ReadWritePropertyServices._debug(" - object: %r", obj)
if not obj:
resp = Error(errorClass='object', errorCode='unknownObject', context=apdu)
else:
try:
# get the datatype
datatype = obj.get_datatype(apdu.propertyIdentifier)
if _debug: Application._debug(" - datatype: %r", datatype)
raise Error(errorClass='object', errorCode='unknownObject')
# get the value
value = obj.ReadProperty(apdu.propertyIdentifier, apdu.propertyArrayIndex)
if _debug: Application._debug(" - value: %r", value)
if value is None:
raise PropertyError(apdu.propertyIdentifier)
try:
# get the datatype
datatype = obj.get_datatype(apdu.propertyIdentifier)
if _debug: ReadWritePropertyServices._debug(" - datatype: %r", datatype)
# change atomic values into something encodeable
if issubclass(datatype, Atomic):
value = datatype(value)
elif issubclass(datatype, Array) and (apdu.propertyArrayIndex is not None):
if apdu.propertyArrayIndex == 0:
value = Unsigned(value)
elif issubclass(datatype.subtype, Atomic):
value = datatype.subtype(value)
elif not isinstance(value, datatype.subtype):
raise TypeError("invalid result datatype, expecting {0} and got {1}" \
.format(datatype.subtype.__name__, type(value).__name__))
elif not isinstance(value, datatype):
# get the value
value = obj.ReadProperty(apdu.propertyIdentifier, apdu.propertyArrayIndex)
if _debug: ReadWritePropertyServices._debug(" - value: %r", value)
if value is None:
raise PropertyError(apdu.propertyIdentifier)
# change atomic values into something encodeable
if issubclass(datatype, Atomic):
value = datatype(value)
elif issubclass(datatype, Array) and (apdu.propertyArrayIndex is not None):
if apdu.propertyArrayIndex == 0:
value = Unsigned(value)
elif issubclass(datatype.subtype, Atomic):
value = datatype.subtype(value)
elif not isinstance(value, datatype.subtype):
raise TypeError("invalid result datatype, expecting {0} and got {1}" \
.format(datatype.__name__, type(value).__name__))
if _debug: Application._debug(" - encodeable value: %r", value)
.format(datatype.subtype.__name__, type(value).__name__))
elif not isinstance(value, datatype):
raise TypeError("invalid result datatype, expecting {0} and got {1}" \
.format(datatype.__name__, type(value).__name__))
if _debug: ReadWritePropertyServices._debug(" - encodeable value: %r", value)
# this is a ReadProperty ack
resp = ReadPropertyACK(context=apdu)
resp.objectIdentifier = objId
resp.propertyIdentifier = apdu.propertyIdentifier
resp.propertyArrayIndex = apdu.propertyArrayIndex
# this is a ReadProperty ack
resp = ReadPropertyACK(context=apdu)
resp.objectIdentifier = objId
resp.propertyIdentifier = apdu.propertyIdentifier
resp.propertyArrayIndex = apdu.propertyArrayIndex
# save the result in the property value
resp.propertyValue = Any()
resp.propertyValue.cast_in(value)
# save the result in the property value
resp.propertyValue = Any()
resp.propertyValue.cast_in(value)
if _debug: ReadWritePropertyServices._debug(" - resp: %r", resp)
except PropertyError:
resp = Error(errorClass='object', errorCode='unknownProperty', context=apdu)
if _debug: Application._debug(" - resp: %r", resp)
except PropertyError:
raise Error(errorClass='object', errorCode='unknownProperty')
# return the result
self.response(resp)
def do_WritePropertyRequest(self, apdu):
"""Change the value of some property of one of our objects."""
if _debug: Application._debug("do_WritePropertyRequest %r", apdu)
if _debug: ReadWritePropertyServices._debug("do_WritePropertyRequest %r", apdu)
# get the object
obj = self.get_object_id(apdu.objectIdentifier)
if _debug: Application._debug(" - object: %r", obj)
if _debug: ReadWritePropertyServices._debug(" - object: %r", obj)
if not obj:
resp = Error(errorClass='object', errorCode='unknownObject', context=apdu)
else:
try:
# check if the property exists
if obj.ReadProperty(apdu.propertyIdentifier, apdu.propertyArrayIndex) is None:
raise PropertyError(apdu.propertyIdentifier)
raise Error(errorClass='object', errorCode='unknownObject')
# get the datatype, special case for null
if apdu.propertyValue.is_application_class_null():
datatype = Null
try:
# check if the property exists
if obj.ReadProperty(apdu.propertyIdentifier, apdu.propertyArrayIndex) is None:
raise PropertyError(apdu.propertyIdentifier)
# get the datatype, special case for null
if apdu.propertyValue.is_application_class_null():
datatype = Null
else:
datatype = obj.get_datatype(apdu.propertyIdentifier)
if _debug: ReadWritePropertyServices._debug(" - datatype: %r", 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:
datatype = obj.get_datatype(apdu.propertyIdentifier)
if _debug: Application._debug(" - datatype: %r", datatype)
value = apdu.propertyValue.cast_out(datatype.subtype)
else:
value = apdu.propertyValue.cast_out(datatype)
if _debug: ReadWritePropertyServices._debug(" - value: %r", value)
# 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: Application._debug(" - value: %r", value)
# change the value
value = obj.WriteProperty(apdu.propertyIdentifier, value, apdu.propertyArrayIndex, apdu.priority)
# change the value
value = obj.WriteProperty(apdu.propertyIdentifier, value, apdu.propertyArrayIndex, apdu.priority)
# success
resp = SimpleAckPDU(context=apdu)
if _debug: ReadWritePropertyServices._debug(" - resp: %r", resp)
# success
resp = SimpleAckPDU(context=apdu)
except PropertyError:
resp = Error(errorClass='object', errorCode='unknownProperty', context=apdu)
if _debug: Application._debug(" - resp: %r", resp)
except PropertyError:
raise Error(errorClass='object', errorCode='unknownProperty')
# return the result
self.response(resp)
@ -391,7 +407,7 @@ def read_property_to_result_element(obj, propertyIdentifier, propertyArrayIndex=
return read_access_result_element
#
#
# ReadWritePropertyMultipleServices
#
class ReadWritePropertyMultipleServices(Capability):

74
py27/bacpypes/service/file.py Executable file → Normal file
View File

@ -5,6 +5,12 @@ from ..capability import Capability
from ..object import FileObject
from ..apdu import AtomicReadFileACK, AtomicReadFileACKAccessMethodChoice, \
AtomicReadFileACKAccessMethodRecordAccess, \
AtomicReadFileACKAccessMethodStreamAccess, \
AtomicWriteFileACK
from ..errors import ExecutionError, MissingRequiredParameter
# some debugging
_debug = 0
_log = ModuleLogger(globals())
@ -112,15 +118,24 @@ class FileServices(Capability):
if obj.fileAccessMethod != 'recordAccess':
raise ExecutionError('services', 'invalidFileAccessMethod')
# simplify
record_access = apdu.accessMethod.recordAccess
# check for required parameters
if record_access.fileStartRecord is None:
raise MissingRequiredParameter("fileStartRecord required")
if record_access.requestedRecordCount is None:
raise MissingRequiredParameter("requestedRecordCount required")
### verify start is valid - double check this (empty files?)
if (apdu.accessMethod.recordAccess.fileStartRecord < 0) or \
(apdu.accessMethod.recordAccess.fileStartRecord >= len(obj)):
if (record_access.fileStartRecord < 0) or \
(record_access.fileStartRecord >= len(obj)):
raise ExecutionError('services', 'invalidFileStartPosition')
# pass along to the object
end_of_file, record_data = obj.read_record(
apdu.accessMethod.recordAccess.fileStartRecord,
apdu.accessMethod.recordAccess.requestedRecordCount,
record_access.fileStartRecord,
record_access.requestedRecordCount,
)
if _debug: FileServices._debug(" - record_data: %r", record_data)
@ -129,7 +144,7 @@ class FileServices(Capability):
endOfFile=end_of_file,
accessMethod=AtomicReadFileACKAccessMethodChoice(
recordAccess=AtomicReadFileACKAccessMethodRecordAccess(
fileStartRecord=apdu.accessMethod.recordAccess.fileStartRecord,
fileStartRecord=record_access.fileStartRecord,
returnedRecordCount=len(record_data),
fileRecordData=record_data,
),
@ -141,15 +156,24 @@ class FileServices(Capability):
if obj.fileAccessMethod != 'streamAccess':
raise ExecutionError('services', 'invalidFileAccessMethod')
# simplify
stream_access = apdu.accessMethod.streamAccess
# check for required parameters
if stream_access.fileStartPosition is None:
raise MissingRequiredParameter("fileStartPosition required")
if stream_access.requestedOctetCount is None:
raise MissingRequiredParameter("requestedOctetCount required")
### verify start is valid - double check this (empty files?)
if (apdu.accessMethod.streamAccess.fileStartPosition < 0) or \
(apdu.accessMethod.streamAccess.fileStartPosition >= len(obj)):
if (stream_access.fileStartPosition < 0) or \
(stream_access.fileStartPosition >= len(obj)):
raise ExecutionError('services', 'invalidFileStartPosition')
# pass along to the object
end_of_file, record_data = obj.read_stream(
apdu.accessMethod.streamAccess.fileStartPosition,
apdu.accessMethod.streamAccess.requestedOctetCount,
stream_access.fileStartPosition,
stream_access.requestedOctetCount,
)
if _debug: FileServices._debug(" - record_data: %r", record_data)
@ -158,7 +182,7 @@ class FileServices(Capability):
endOfFile=end_of_file,
accessMethod=AtomicReadFileACKAccessMethodChoice(
streamAccess=AtomicReadFileACKAccessMethodStreamAccess(
fileStartPosition=apdu.accessMethod.streamAccess.fileStartPosition,
fileStartPosition=stream_access.fileStartPosition,
fileData=record_data,
),
),
@ -188,15 +212,26 @@ class FileServices(Capability):
if obj.fileAccessMethod != 'recordAccess':
raise ExecutionError('services', 'invalidFileAccessMethod')
# simplify
record_access = apdu.accessMethod.recordAccess
# check for required parameters
if record_access.fileStartRecord is None:
raise MissingRequiredParameter("fileStartRecord required")
if record_access.recordCount is None:
raise MissingRequiredParameter("recordCount required")
if record_access.fileRecordData is None:
raise MissingRequiredParameter("fileRecordData required")
# check for read-only
if obj.readOnly:
raise ExecutionError('services', 'fileAccessDenied')
# pass along to the object
start_record = obj.write_record(
apdu.accessMethod.recordAccess.fileStartRecord,
apdu.accessMethod.recordAccess.recordCount,
apdu.accessMethod.recordAccess.fileRecordData,
record_access.fileStartRecord,
record_access.recordCount,
record_access.fileRecordData,
)
if _debug: FileServices._debug(" - start_record: %r", start_record)
@ -210,14 +245,23 @@ class FileServices(Capability):
if obj.fileAccessMethod != 'streamAccess':
raise ExecutionError('services', 'invalidFileAccessMethod')
# simplify
stream_access = apdu.accessMethod.streamAccess
# check for required parameters
if stream_access.fileStartPosition is None:
raise MissingRequiredParameter("fileStartPosition required")
if stream_access.fileData is None:
raise MissingRequiredParameter("fileData required")
# check for read-only
if obj.readOnly:
raise ExecutionError('services', 'fileAccessDenied')
# pass along to the object
start_position = obj.write_stream(
apdu.accessMethod.streamAccess.fileStartPosition,
apdu.accessMethod.streamAccess.fileData,
stream_access.fileStartPosition,
stream_access.fileData,
)
if _debug: FileServices._debug(" - start_position: %r", start_position)

View File

@ -14,5 +14,5 @@ _log = ModuleLogger(globals())
def some_function(*args):
if _debug: some_function._debug("f %r", args)
return x + 1
return args[0] + 1