mirror of
https://github.com/JoelBender/bacpypes
synced 2025-10-13 00:42:44 +08:00
merge #207 into stage for next release
This commit is contained in:
parent
78b6586da6
commit
197a405ec7
|
@ -927,7 +927,7 @@ class RangeByPosition(Sequence):
|
|||
|
||||
class RangeBySequenceNumber(Sequence):
|
||||
sequenceElements = \
|
||||
[ Element('referenceIndex', Unsigned)
|
||||
[ Element('referenceSequenceNumber', Unsigned)
|
||||
, Element('count', Integer)
|
||||
]
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ Constructed Data
|
|||
import sys
|
||||
from copy import deepcopy as _deepcopy
|
||||
|
||||
from .errors import DecodingError, \
|
||||
from .errors import DecodingError, EncodingError, \
|
||||
MissingRequiredParameter, InvalidParameterDatatype, InvalidTag
|
||||
from .debugging import ModuleLogger, bacpypes_debugging
|
||||
|
||||
|
@ -1461,3 +1461,47 @@ class AnyAtomic(Atomic):
|
|||
|
||||
bacpypes_debugging(AnyAtomic)
|
||||
|
||||
#
|
||||
# SequenceOfAny
|
||||
#
|
||||
|
||||
class SequenceOfAny(Any):
|
||||
|
||||
def cast_in(self, element):
|
||||
"""encode the element into the internal tag list."""
|
||||
if _debug: SequenceOfAny._debug("cast_in %r", element)
|
||||
|
||||
# make sure it is a list
|
||||
if not isinstance(element, List):
|
||||
raise EncodingError("%r is not a list" % (element,))
|
||||
|
||||
t = TagList()
|
||||
element.encode(t)
|
||||
|
||||
self.tagList.extend(t.tagList)
|
||||
|
||||
def cast_out(self, klass):
|
||||
"""Interpret the content as a particular class."""
|
||||
if _debug: SequenceOfAny._debug("cast_out %r", klass)
|
||||
|
||||
# make sure it is a list
|
||||
if not issubclass(klass, List):
|
||||
raise DecodingError("%r is not a list" % (klass,))
|
||||
|
||||
# build a helper
|
||||
helper = klass()
|
||||
|
||||
# make a copy of the tag list
|
||||
t = TagList(self.tagList[:])
|
||||
|
||||
# let it decode itself
|
||||
helper.decode(t)
|
||||
|
||||
# make sure everything was consumed
|
||||
if len(t) != 0:
|
||||
raise DecodingError("incomplete cast")
|
||||
|
||||
return helper.value
|
||||
|
||||
bacpypes_debugging(AnyAtomic)
|
||||
|
||||
|
|
|
@ -11,7 +11,8 @@ from .pdu import PCI, PDUData
|
|||
from .primitivedata import Boolean, CharacterString, Enumerated, Integer, \
|
||||
ObjectIdentifier, ObjectType, OctetString, Real, TagList, Unsigned, \
|
||||
expand_enumerations
|
||||
from .constructeddata import Any, Choice, Element, Sequence, SequenceOf
|
||||
from .constructeddata import Any, Choice, Element, Sequence, SequenceOf, \
|
||||
SequenceOfAny
|
||||
from .basetypes import ChannelValue, DateTime, DeviceAddress, ErrorType, \
|
||||
EventState, EventTransitionBits, EventType, LifeSafetyOperation, \
|
||||
NotificationParameters, NotifyType, ObjectPropertyReference, \
|
||||
|
@ -920,7 +921,7 @@ class RangeByPosition(Sequence):
|
|||
|
||||
class RangeBySequenceNumber(Sequence):
|
||||
sequenceElements = \
|
||||
[ Element('referenceIndex', Unsigned)
|
||||
[ Element('referenceSequenceNumber', Unsigned)
|
||||
, Element('count', Integer)
|
||||
]
|
||||
|
||||
|
@ -956,7 +957,7 @@ class ReadRangeACK(ComplexAckSequence):
|
|||
, Element('propertyArrayIndex', Unsigned, 2, True)
|
||||
, Element('resultFlags', ResultFlags, 3)
|
||||
, Element('itemCount', Unsigned, 4)
|
||||
, Element('itemData', SequenceOf(Any), 5)
|
||||
, Element('itemData', SequenceOfAny, 5)
|
||||
, Element('firstSequenceNumber', Unsigned, 6, True)
|
||||
]
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ Constructed Data
|
|||
import sys
|
||||
from copy import deepcopy as _deepcopy
|
||||
|
||||
from .errors import DecodingError, \
|
||||
from .errors import DecodingError, EncodingError, \
|
||||
MissingRequiredParameter, InvalidParameterDatatype, InvalidTag
|
||||
from .debugging import ModuleLogger, bacpypes_debugging
|
||||
|
||||
|
@ -1452,3 +1452,45 @@ class AnyAtomic(Atomic):
|
|||
|
||||
return '<' + desc + ' instance at 0x%08x' % (id(self),) + '>'
|
||||
|
||||
#
|
||||
# SequenceOfAny
|
||||
#
|
||||
|
||||
@bacpypes_debugging
|
||||
class SequenceOfAny(Any):
|
||||
|
||||
def cast_in(self, element):
|
||||
"""encode the element into the internal tag list."""
|
||||
if _debug: SequenceOfAny._debug("cast_in %r", element)
|
||||
|
||||
# make sure it is a list
|
||||
if not isinstance(element, List):
|
||||
raise EncodingError("%r is not a list" % (element,))
|
||||
|
||||
t = TagList()
|
||||
element.encode(t)
|
||||
|
||||
self.tagList.extend(t.tagList)
|
||||
|
||||
def cast_out(self, klass):
|
||||
"""Interpret the content as a particular class."""
|
||||
if _debug: SequenceOfAny._debug("cast_out %r", klass)
|
||||
|
||||
# make sure it is a list
|
||||
if not issubclass(klass, List):
|
||||
raise DecodingError("%r is not a list" % (klass,))
|
||||
|
||||
# build a helper
|
||||
helper = klass()
|
||||
|
||||
# make a copy of the tag list
|
||||
t = TagList(self.tagList[:])
|
||||
|
||||
# let it decode itself
|
||||
helper.decode(t)
|
||||
|
||||
# make sure everything was consumed
|
||||
if len(t) != 0:
|
||||
raise DecodingError("incomplete cast")
|
||||
|
||||
return helper.value
|
||||
|
|
|
@ -920,7 +920,7 @@ class RangeByPosition(Sequence):
|
|||
|
||||
class RangeBySequenceNumber(Sequence):
|
||||
sequenceElements = \
|
||||
[ Element('referenceIndex', Unsigned)
|
||||
[ Element('referenceSequenceNumber', Unsigned)
|
||||
, Element('count', Integer)
|
||||
]
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ Constructed Data
|
|||
import sys
|
||||
from copy import deepcopy as _deepcopy
|
||||
|
||||
from .errors import DecodingError, \
|
||||
from .errors import DecodingError, EncodingError, \
|
||||
MissingRequiredParameter, InvalidParameterDatatype, InvalidTag
|
||||
from .debugging import ModuleLogger, bacpypes_debugging
|
||||
|
||||
|
@ -1452,3 +1452,45 @@ class AnyAtomic(Atomic):
|
|||
|
||||
return '<' + desc + ' instance at 0x%08x' % (id(self),) + '>'
|
||||
|
||||
#
|
||||
# SequenceOfAny
|
||||
#
|
||||
|
||||
@bacpypes_debugging
|
||||
class SequenceOfAny(Any):
|
||||
|
||||
def cast_in(self, element):
|
||||
"""encode the element into the internal tag list."""
|
||||
if _debug: SequenceOfAny._debug("cast_in %r", element)
|
||||
|
||||
# make sure it is a list
|
||||
if not isinstance(element, List):
|
||||
raise EncodingError("%r is not a list" % (element,))
|
||||
|
||||
t = TagList()
|
||||
element.encode(t)
|
||||
|
||||
self.tagList.extend(t.tagList)
|
||||
|
||||
def cast_out(self, klass):
|
||||
"""Interpret the content as a particular class."""
|
||||
if _debug: SequenceOfAny._debug("cast_out %r", klass)
|
||||
|
||||
# make sure it is a list
|
||||
if not issubclass(klass, List):
|
||||
raise DecodingError("%r is not a list" % (klass,))
|
||||
|
||||
# build a helper
|
||||
helper = klass()
|
||||
|
||||
# make a copy of the tag list
|
||||
t = TagList(self.tagList[:])
|
||||
|
||||
# let it decode itself
|
||||
helper.decode(t)
|
||||
|
||||
# make sure everything was consumed
|
||||
if len(t) != 0:
|
||||
raise DecodingError("incomplete cast")
|
||||
|
||||
return helper.value
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
This application presents a 'console' prompt to the user asking for readrange commands
|
||||
which create ReadRangeRequest PDUs, then lines up the coorresponding ReadRangeACK
|
||||
and prints the value.
|
||||
This application presents a 'console' prompt to the user asking for readrange
|
||||
commands which create ReadRangeRequest PDUs, then lines up the coorresponding
|
||||
ReadRangeACK and prints the value.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
@ -17,10 +17,19 @@ from bacpypes.iocb import IOCB
|
|||
|
||||
from bacpypes.pdu import Address
|
||||
from bacpypes.object import get_datatype
|
||||
from bacpypes.apdu import ReadRangeRequest, ReadRangeACK
|
||||
from bacpypes.apdu import (
|
||||
ReadRangeRequest,
|
||||
Range,
|
||||
RangeByPosition,
|
||||
RangeBySequenceNumber,
|
||||
RangeByTime,
|
||||
ReadRangeACK,
|
||||
)
|
||||
|
||||
from bacpypes.app import BIPSimpleApplication
|
||||
from bacpypes.primitivedata import ObjectIdentifier
|
||||
from bacpypes.primitivedata import Date, Time, ObjectIdentifier
|
||||
from bacpypes.constructeddata import Array, List
|
||||
from bacpypes.basetypes import DateTime
|
||||
from bacpypes.local.device import LocalDeviceObject
|
||||
|
||||
# some debugging
|
||||
|
@ -34,17 +43,23 @@ this_application = None
|
|||
# ReadRangeConsoleCmd
|
||||
#
|
||||
|
||||
|
||||
@bacpypes_debugging
|
||||
class ReadRangeConsoleCmd(ConsoleCmd):
|
||||
|
||||
def do_readrange(self, args):
|
||||
"""readrange <addr> <objid> <prop> [ <indx> ]"""
|
||||
"""readrange <addr> <objid> <prop> [ <indx> ]
|
||||
[ p <indx> <count> ]
|
||||
[ s <seq> <count> ]
|
||||
[ t <date> <time> <count> ]
|
||||
"""
|
||||
args = args.split()
|
||||
if _debug: ReadRangeConsoleCmd._debug("do_readrange %r", args)
|
||||
if _debug:
|
||||
ReadRangeConsoleCmd._debug("do_readrange %r", args)
|
||||
|
||||
try:
|
||||
addr, obj_id, prop_id = args[:3]
|
||||
obj_id = ObjectIdentifier(obj_id).value
|
||||
addr = Address(args.pop(0))
|
||||
obj_id = ObjectIdentifier(args.pop(0)).value
|
||||
prop_id = args.pop(0)
|
||||
|
||||
datatype = get_datatype(obj_id[0], prop_id)
|
||||
if not datatype:
|
||||
|
@ -52,18 +67,49 @@ class ReadRangeConsoleCmd(ConsoleCmd):
|
|||
|
||||
# build a request
|
||||
request = ReadRangeRequest(
|
||||
objectIdentifier=obj_id,
|
||||
propertyIdentifier=prop_id,
|
||||
)
|
||||
request.pduDestination = Address(addr)
|
||||
destination=addr,
|
||||
objectIdentifier=obj_id, propertyIdentifier=prop_id
|
||||
)
|
||||
|
||||
if len(args) == 4:
|
||||
request.propertyArrayIndex = int(args[3])
|
||||
if _debug: ReadRangeConsoleCmd._debug(" - request: %r", request)
|
||||
# index is optional
|
||||
if args:
|
||||
if args[0].isdigit():
|
||||
if not issubclass(datatype, Array):
|
||||
raise ValueError("property is not an array")
|
||||
request.propertyArrayIndex = int(args.pop(0))
|
||||
datatype = datatype.subtype
|
||||
if not issubclass(datatype, List):
|
||||
raise ValueError("property is not a list")
|
||||
|
||||
# range is optional
|
||||
if args:
|
||||
range_type = args.pop(0)
|
||||
if range_type == "p":
|
||||
rbp = RangeByPosition(
|
||||
referenceIndex=int(args[0]), count=int(args[1])
|
||||
)
|
||||
request.range = Range(byPosition=rbp)
|
||||
elif range_type == "s":
|
||||
rbs = RangeBySequenceNumber(
|
||||
referenceSequenceNumber=int(args[0]), count=int(args[1])
|
||||
)
|
||||
request.range = Range(bySequenceNumber=rbs)
|
||||
elif range_type == "t":
|
||||
rbt = RangeByTime(
|
||||
referenceTime=DateTime(date=Date(args[0]), time=Time(args[1])),
|
||||
count=int(args[2]),
|
||||
)
|
||||
request.range = Range(byTime=rbt)
|
||||
else:
|
||||
raise ValueError("unknown range type: %r" % (range_type,))
|
||||
|
||||
if _debug:
|
||||
ReadRangeConsoleCmd._debug(" - request: %r", request)
|
||||
|
||||
# make an IOCB
|
||||
iocb = IOCB(request)
|
||||
if _debug: ReadRangeConsoleCmd._debug(" - iocb: %r", iocb)
|
||||
if _debug:
|
||||
ReadRangeConsoleCmd._debug(" - iocb: %r", iocb)
|
||||
|
||||
# give it to the application
|
||||
deferred(this_application.request_io, iocb)
|
||||
|
@ -74,20 +120,29 @@ class ReadRangeConsoleCmd(ConsoleCmd):
|
|||
# do something for success
|
||||
if iocb.ioResponse:
|
||||
apdu = iocb.ioResponse
|
||||
if _debug:
|
||||
ReadRangeConsoleCmd._debug(" - apdu: %r", apdu)
|
||||
|
||||
# should be an ack
|
||||
if not isinstance(apdu, ReadRangeACK):
|
||||
if _debug: ReadRangeConsoleCmd._debug(" - not an ack")
|
||||
if _debug:
|
||||
ReadRangeConsoleCmd._debug(" - not an ack")
|
||||
return
|
||||
|
||||
# find the datatype
|
||||
datatype = get_datatype(apdu.objectIdentifier[0], apdu.propertyIdentifier)
|
||||
if _debug: ReadRangeConsoleCmd._debug(" - datatype: %r", datatype)
|
||||
datatype = get_datatype(
|
||||
apdu.objectIdentifier[0], apdu.propertyIdentifier
|
||||
)
|
||||
if _debug:
|
||||
ReadRangeConsoleCmd._debug(" - datatype: %r", datatype)
|
||||
if not datatype:
|
||||
raise TypeError("unknown datatype")
|
||||
|
||||
sys.stdout.write("firstSequenceNumber: %s\n" % (apdu.firstSequenceNumber,))
|
||||
sys.stdout.write("resultFlags: %s\n" % (apdu.resultFlags,))
|
||||
|
||||
# cast out the data into a list
|
||||
value = apdu.itemData[0].cast_out(datatype)
|
||||
value = apdu.itemData.cast_out(datatype)
|
||||
|
||||
# dump it out
|
||||
for i, item in enumerate(value):
|
||||
|
@ -97,34 +152,40 @@ class ReadRangeConsoleCmd(ConsoleCmd):
|
|||
|
||||
# do something for error/reject/abort
|
||||
if iocb.ioError:
|
||||
sys.stdout.write(str(iocb.ioError) + '\n')
|
||||
sys.stdout.write(str(iocb.ioError) + "\n")
|
||||
|
||||
except Exception as error:
|
||||
ReadRangeConsoleCmd._exception("exception: %r", error)
|
||||
|
||||
|
||||
#
|
||||
# __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)
|
||||
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)
|
||||
|
||||
# make a console
|
||||
this_console = ReadRangeConsoleCmd()
|
||||
if _debug: _log.debug(" - this_console: %r", this_console)
|
||||
if _debug:
|
||||
_log.debug(" - this_console: %r", this_console)
|
||||
|
||||
# enable sleeping will help with threads
|
||||
enable_sleeping()
|
||||
|
@ -135,5 +196,6 @@ def main():
|
|||
|
||||
_log.debug("fini")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
148
samples/ReadRangeServer.py
Executable file
148
samples/ReadRangeServer.py
Executable file
|
@ -0,0 +1,148 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Server with a Trend Log Object
|
||||
"""
|
||||
|
||||
from bacpypes.debugging import bacpypes_debugging, ModuleLogger
|
||||
from bacpypes.consolelogging import ConfigArgumentParser
|
||||
|
||||
from bacpypes.core import run
|
||||
from bacpypes.errors import ExecutionError
|
||||
from bacpypes.primitivedata import Date, Time
|
||||
from bacpypes.constructeddata import Array, List, SequenceOfAny
|
||||
from bacpypes.basetypes import DateTime, LogRecord, LogRecordLogDatum, StatusFlags
|
||||
from bacpypes.apdu import ReadRangeACK
|
||||
|
||||
from bacpypes.app import BIPSimpleApplication
|
||||
from bacpypes.local.device import LocalDeviceObject
|
||||
from bacpypes.object import PropertyError, TrendLogObject
|
||||
|
||||
|
||||
# some debugging
|
||||
_debug = 0
|
||||
_log = ModuleLogger(globals())
|
||||
|
||||
# globals
|
||||
this_application = None
|
||||
|
||||
|
||||
#
|
||||
# ReadRangeApplication
|
||||
#
|
||||
|
||||
|
||||
@bacpypes_debugging
|
||||
class ReadRangeApplication(BIPSimpleApplication):
|
||||
def __init__(self, *args):
|
||||
if _debug:
|
||||
ReadRangeApplication._debug("__init__ %r", args)
|
||||
BIPSimpleApplication.__init__(self, *args)
|
||||
|
||||
def do_ReadRangeRequest(self, apdu):
|
||||
if _debug:
|
||||
ReadRangeApplication._debug("do_ReadRangeRequest %r", apdu)
|
||||
|
||||
# extract the object identifier
|
||||
objId = apdu.objectIdentifier
|
||||
|
||||
# get the object
|
||||
obj = self.get_object_id(objId)
|
||||
if _debug:
|
||||
ReadRangeApplication._debug(" - object: %r", obj)
|
||||
|
||||
if not obj:
|
||||
raise ExecutionError(errorClass="object", errorCode="unknownObject")
|
||||
|
||||
# get the datatype
|
||||
datatype = obj.get_datatype(apdu.propertyIdentifier)
|
||||
if _debug:
|
||||
ReadRangeApplication._debug(" - datatype: %r", datatype)
|
||||
|
||||
# must be a list, or an array of lists
|
||||
if issubclass(datatype, List):
|
||||
pass
|
||||
elif (
|
||||
(apdu.propertyArrayIndex is not None)
|
||||
and issubclass(datatype, Array)
|
||||
and issubclass(datatype.subtype, List)
|
||||
):
|
||||
pass
|
||||
else:
|
||||
raise ExecutionError(errorClass="property", errorCode="propertyIsNotAList")
|
||||
|
||||
# get the value
|
||||
value = obj.ReadProperty(apdu.propertyIdentifier, apdu.propertyArrayIndex)
|
||||
if _debug:
|
||||
ReadRangeApplication._debug(" - value: %r", value)
|
||||
if value is None:
|
||||
raise PropertyError(apdu.propertyIdentifier)
|
||||
|
||||
# this is an ack
|
||||
resp = ReadRangeACK(context=apdu)
|
||||
resp.objectIdentifier = objId
|
||||
resp.propertyIdentifier = apdu.propertyIdentifier
|
||||
resp.propertyArrayIndex = apdu.propertyArrayIndex
|
||||
|
||||
resp.resultFlags = [1, 1, 0]
|
||||
resp.itemCount = len(value)
|
||||
|
||||
# save the result in the item data
|
||||
resp.itemData = SequenceOfAny()
|
||||
resp.itemData.cast_in(datatype(value))
|
||||
if _debug:
|
||||
ReadRangeApplication._debug(" - resp: %r", resp)
|
||||
|
||||
# return the result
|
||||
self.response(resp)
|
||||
|
||||
|
||||
#
|
||||
# __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 = ReadRangeApplication(this_device, args.ini.address)
|
||||
|
||||
timestamp = DateTime(date=Date().now().value, time=Time().now().value)
|
||||
# log_status = LogStatus([0,0,0])
|
||||
log_record_datum = LogRecordLogDatum(booleanValue=False)
|
||||
status_flags = StatusFlags([0, 0, 0, 0])
|
||||
log_record = LogRecord(
|
||||
timestamp=timestamp, logDatum=log_record_datum, statusFlags=status_flags
|
||||
)
|
||||
|
||||
trend_log_object = TrendLogObject(
|
||||
objectIdentifier=("trendLog", 1),
|
||||
objectName="Trend-Log-1",
|
||||
logBuffer=[log_record],
|
||||
)
|
||||
_log.debug(" - trend_log_object: %r", trend_log_object)
|
||||
this_application.add_object(trend_log_object)
|
||||
|
||||
_log.debug("running")
|
||||
|
||||
run()
|
||||
|
||||
_log.debug("fini")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in New Issue
Block a user