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

Merge branch 'patch-6' of https://github.com/tjohnsonhvac/bacpypes into tjohnsonhvac-patch-6

This commit is contained in:
Joel Bender 2019-03-25 20:58:47 -04:00
commit 8ebd58727d
7 changed files with 250 additions and 24 deletions

View File

@ -1730,7 +1730,8 @@ class AccessRule(Sequence):
]
class AccessThreatLevel(Unsigned):
pass
_low_limit = 0
_high_limit = 100
class AccumulatorRecord(Sequence):
sequenceElements = \

View File

@ -594,8 +594,10 @@ class Boolean(Atomic):
class Unsigned(Atomic):
_app_tag = Tag.unsignedAppTag
_low_limit = 0
_high_limit = None
def __init__(self,arg = None):
def __init__(self, arg=None):
self.value = 0L
if arg is None:
@ -603,14 +605,16 @@ class Unsigned(Atomic):
elif isinstance(arg, Tag):
self.decode(arg)
elif isinstance(arg, int):
if (arg < 0):
raise ValueError("unsigned integer required")
if not self.is_valid(arg):
raise ValueError("value out of range")
self.value = long(arg)
elif isinstance(arg, long):
if (arg < 0):
raise ValueError("unsigned integer required")
if not self.is_valid(arg):
raise ValueError("value out of range")
self.value = arg
elif isinstance(arg, Unsigned):
if not self.is_valid(arg.value):
raise ValueError("value out of range")
self.value = arg.value
else:
raise TypeError("invalid constructor datatype")
@ -621,7 +625,7 @@ class Unsigned(Atomic):
# reduce the value to the smallest number of octets
while (len(data) > 1) and (data[0] == '\x00'):
data = data[1:]
del data[0]
# encode the tag
tag.set_app_data(Tag.unsignedAppTag, data)
@ -643,10 +647,24 @@ class Unsigned(Atomic):
@classmethod
def is_valid(cls, arg):
"""Return True if arg is valid value for the class."""
return isinstance(arg, (int, long)) and (not isinstance(arg, bool)) and (arg >= 0)
if not isinstance(arg, (int, long)) or isinstance(arg, bool):
return False
if (arg < cls._low_limit):
return False
if (cls._high_limit is not None) and (arg > cls._high_limit):
return False
return True
def __str__(self):
return "Unsigned(%s)" % (self.value, )
return "%s(%s)" % (self.__class__.__name__, self.value)
class Unsigned8(Unsigned):
_low_limit = 0
_high_limit = 255
class Unsigned16(Unsigned):
_low_limit = 0
_high_limit = 65535
#
# Integer

View File

@ -1730,7 +1730,8 @@ class AccessRule(Sequence):
]
class AccessThreatLevel(Unsigned):
pass
_low_limit = 0
_high_limit = 100
class AccumulatorRecord(Sequence):
sequenceElements = \

View File

@ -598,8 +598,10 @@ class Boolean(Atomic):
class Unsigned(Atomic):
_app_tag = Tag.unsignedAppTag
_low_limit = 0
_high_limit = None
def __init__(self,arg = None):
def __init__(self, arg=None):
self.value = 0L
if arg is None:
@ -607,14 +609,16 @@ class Unsigned(Atomic):
elif isinstance(arg, Tag):
self.decode(arg)
elif isinstance(arg, int):
if (arg < 0):
raise ValueError("unsigned integer required")
if not self.is_valid(arg):
raise ValueError("value out of range")
self.value = long(arg)
elif isinstance(arg, long):
if (arg < 0):
raise ValueError("unsigned integer required")
if not self.is_valid(arg):
raise ValueError("value out of range")
self.value = arg
elif isinstance(arg, Unsigned):
if not self.is_valid(arg.value):
raise ValueError("value out of range")
self.value = arg.value
else:
raise TypeError("invalid constructor datatype")
@ -647,10 +651,24 @@ class Unsigned(Atomic):
@classmethod
def is_valid(cls, arg):
"""Return True if arg is valid value for the class."""
return isinstance(arg, (int, long)) and (not isinstance(arg, bool)) and (arg >= 0)
if not isinstance(arg, (int, long)) or isinstance(arg, bool):
return False
if (arg < cls._low_limit):
return False
if (cls._high_limit is not None) and (arg > cls._high_limit):
return False
return True
def __str__(self):
return "Unsigned(%s)" % (self.value, )
return "%s(%s)" % (self.__class__.__name__, self.value)
class Unsigned8(Unsigned):
_low_limit = 0
_high_limit = 255
class Unsigned16(Unsigned):
_low_limit = 0
_high_limit = 65535
#
# Integer

View File

@ -94,6 +94,7 @@ class ObjectTypesSupported(BitString):
, 'accessUser':35
, 'accessZone':36
, 'credentialDataInput':37
, 'networkPort':56
, 'networkSecurity':38
, 'bitstringValue':39
, 'characterstringValue':40
@ -1101,9 +1102,50 @@ class ProgramState(Enumerated):
}
class PropertyIdentifier(Enumerated):
# TODO: Sort Alphabetically
vendor_range = (512, 4194303)
enumerations = \
{ 'absenteeLimit':244
, 'tags':486
, 'profileLocation':91
, 'eventDetectionEnabled':353
, 'apduLength':388
, 'linkSpeed':420
, 'linkSpeeds':421
, 'linkSpeedAutonegotiate':422
, 'networkInterfaceName':424
, 'bacnetIPMode':408
, 'ipAddress':400
, 'bacnetIPUDPPort':412
, 'ipSubnetMask':411
, 'ipDefaultGateway':401
, 'bacnetIPMulticastAddress':409
, 'ipDNSServer':406
, 'ipDHCPEnable':402
, 'ipDHCPLeaseTime':403
, 'ipDHCPLeaseTimeRemaining':404
, 'ipDHCPServer':405
, 'bacnetIPNATTraversal':410
, 'bacnetIPGlobalAddress':407
, 'bbmdBroadcastDistributionTable':414
, 'bbmdAcceptFDRegistrations':413
, 'bbmdForeignDeviceTable':415
, 'fdBBMDAddress':418
, 'fdSubscriptionLifetime':419
, 'bacnetIPv6Mode':435
, 'ipv6Address':436
, 'ipv6PrefixLength':437
, 'bacnetIPv6UDPPort':438
, 'ipv6DefaultGateway':439
, 'bacnetIPv6MulticastAddress':440
, 'ipv6DNSServer':441
, 'ipv6AutoAddressingEnabled':442
, 'ipv6DHCPLeaseTime':443
, 'ipv6DHCPLeaseTimeRemaining':444
, 'ipv6DHCPServer':445
, 'ipv6ZoneIndex':446
, 'virtualMACAddressTable':429
, 'routingTable':428
, 'acceptedModes':175
, 'accessAlarmEvents':245
, 'accessDoors':246
@ -1158,8 +1200,10 @@ class PropertyIdentifier(Enumerated):
, 'bufferSize':126
, 'changeOfStateCount':15
, 'changeOfStateTime':16
, 'changesPending':416
, 'channelNumber':366
, 'clientCovIncrement':127
, 'command':417
, 'configurationFiles':154
, 'controlGroups':367
, 'controlledVariableReference':19
@ -1286,6 +1330,7 @@ class PropertyIdentifier(Enumerated):
, 'loggingRecord':184
, 'loggingType':197
, 'lowLimit':59
, 'macAddress':423
, 'maintenanceRequired':158
, 'manipulatedVariableReference':60
, 'manualSlaveAddressBinding':170
@ -1317,6 +1362,9 @@ class PropertyIdentifier(Enumerated):
, 'musterPoint':287
, 'negativeAccessRules':288
, 'networkAccessSecurityPolicies':332
, 'networkNumber':425
, 'networkNumberQuality':427
, 'networkType': 427
, 'nodeSubtype':207
, 'nodeType':208
, 'notificationClass':17
@ -1365,6 +1413,7 @@ class PropertyIdentifier(Enumerated):
, 'propertyList':371
, 'proportionalConstant':93
, 'proportionalConstantUnits':94
, 'protocolLevel':482
, 'protocolObjectTypesSupported':96
, 'protocolRevision':139
, 'protocolServicesSupported':97
@ -1376,6 +1425,7 @@ class PropertyIdentifier(Enumerated):
, 'recipientList':102
, 'recordsSinceNotification':140
, 'recordCount':141
, 'referencePort':483
, 'reliability':103
, 'reliabilityEvaluationInhibit':357
, 'relinquishDefault':104
@ -1565,11 +1615,107 @@ class WriteStatus(Enumerated):
, 'successful':2
, 'failed':3
}
class NetworkType(Enumerated):
enumerations = \
{ 'ethernet':0
, 'arcnet':1
, 'mstp':2
, 'ptp':3
, 'lontalk':4
, 'ipv4':5
, 'zigbee':6
, 'virtual': 7
# , 'non-bacnet': 8 Removed in Version 1, Revision 18
, 'ipv6':9
, 'serial':10
}
class ProtocolLevel(Enumerated):
enumerations = \
{ 'physical':0
, 'protocol':1
, 'bacnetApplication':2
, 'nonBacnetApplication':3
}
class NetworkNumberQuality(Enumerated):
enumerations = \
{ 'unknown':0
, 'learned':1
, 'learnedConfigured':2
, 'configured':3
}
class NetworkPortCommand(Enumerated):
enumerations = \
{ 'idle':0
, 'discardChanges':1
, 'renewFdDRegistration':2
, 'restartSlaveDiscovery':3
, 'renewDHCP':4
, 'restartAutonegotiation':5
, 'disconnect':6
, 'restartPort':7
}
class IPMode(Enumerated):
enumerations = \
{ 'normal':0
, 'foreign':1
, 'bbmd':2
}
class RouterEntryStatus(Enumerated):
# This was defined directly in the RouterEntry Sequence in the standard, but I moved it up here because
# I didn't see anywhere else you defined something that way.
enumerations = \
{ 'available':0
, 'busy':1
, 'disconnected':2
}
#
# Forward Sequences
#
class HostNPort(Sequence):
sequenceElements = \
[ Element('host', HostAddress)
, Element('port', Unsigned16)
]
class BDTEntry(Sequence):
sequenceElements = \
[ Element('bbmdAddress', HostNPort)
, Element('broadcastMask', OctetString) # shall be present if BACnet/IP, and absent for BACnet/IPv6
]
class FDTEntry(Sequence):
sequenceElements = \
[ Element('bacnetIPAddress', OctetString) # the 6-octet B/IP or 18-octet B/IPv6 address of the registrant
, Element('timeToLive', Unsigned16) # time to live in seconds at the time of registration
, Element('remainingTimeToLive', Unsigned16) # remaining time to live in seconds, incl. grace period
]
class VMACEntry(Sequence):
sequenceElements = \
[ Element('virtualMACAddress', OctetString) # maximum size 6 octets
, Element('nativeMACAddress', OctetString)
]
class RouterEntry(Sequence):
sequenceElements = \
[ Element('networkNumber', Unsigned16)
, Element('macAddress', OctetString)
, Element('status', RouterEntryStatus) # Defined Above
]
class NameValue(Sequence):
sequenceElements = \
[ Element('name', CharacterString)
, Element('value', Atomic) # IS ATOMIC CORRECT HERE? value is limited to primitive datatypes and BACnetDateTime
]
class DeviceAddress(Sequence):
sequenceElements = \
[ Element('networkNumber', Unsigned)
@ -1624,6 +1770,13 @@ class ObjectPropertyReference(Sequence):
, Element('propertyIdentifier', PropertyIdentifier, 1)
, Element('propertyArrayIndex', Unsigned, 2, True)
]
class HostAddress(Choice):
choiceElements = \
[ Element('none', Null)
, Element('ipAddress', OctetString) # 4 octets for B/IP or 16 octets for B/IPv6
, Element('name', CharacterString) # Internet host name (see RFC 1123)
]
class ProcessIdSelection(Choice):
choiceElements = \
@ -1730,7 +1883,8 @@ class AccessRule(Sequence):
]
class AccessThreatLevel(Unsigned):
pass
_low_limit = 0
_high_limit = 100
class AccumulatorRecord(Sequence):
sequenceElements = \

View File

@ -598,8 +598,10 @@ class Boolean(Atomic):
class Unsigned(Atomic):
_app_tag = Tag.unsignedAppTag
_low_limit = 0
_high_limit = None
def __init__(self,arg = None):
def __init__(self, arg=None):
self.value = 0
if arg is None:
@ -607,10 +609,12 @@ class Unsigned(Atomic):
elif isinstance(arg, Tag):
self.decode(arg)
elif isinstance(arg, int):
if (arg < 0):
raise ValueError("unsigned integer required")
if not self.is_valid(arg):
raise ValueError("value out of range")
self.value = arg
elif isinstance(arg, Unsigned):
if not self.is_valid(arg.value):
raise ValueError("value out of range")
self.value = arg.value
else:
raise TypeError("invalid constructor datatype")
@ -643,10 +647,24 @@ class Unsigned(Atomic):
@classmethod
def is_valid(cls, arg):
"""Return True if arg is valid value for the class."""
return isinstance(arg, int) and (not isinstance(arg, bool)) and (arg >= 0)
if not isinstance(arg, int) or isinstance(arg, bool):
return False
if (arg < cls._low_limit):
return False
if (cls._high_limit is not None) and (arg > cls._high_limit):
return False
return True
def __str__(self):
return "Unsigned(%s)" % (self.value, )
return "%s(%s)" % (self.__class__.__name__, self.value)
class Unsigned8(Unsigned):
_low_limit = 0
_high_limit = 255
class Unsigned16(Unsigned):
_low_limit = 0
_high_limit = 65535
#
# Integer

View File

@ -12,7 +12,7 @@ import unittest
from bacpypes.debugging import bacpypes_debugging, ModuleLogger, xtob
from bacpypes.errors import InvalidTag
from bacpypes.primitivedata import Unsigned, Tag
from bacpypes.primitivedata import Unsigned, Unsigned8, Unsigned16, Tag
# some debugging
_debug = 0
@ -104,6 +104,22 @@ class TestUnsigned(unittest.TestCase):
with self.assertRaises(ValueError):
Unsigned(-1)
def test_unsigned8(self):
if _debug: TestUnsigned._debug("test_unsigned8")
with self.assertRaises(ValueError):
Unsigned8(-1)
with self.assertRaises(ValueError):
Unsigned8(256)
def test_unsigned16(self):
if _debug: TestUnsigned._debug("test_unsigned16")
with self.assertRaises(ValueError):
Unsigned16(-1)
with self.assertRaises(ValueError):
Unsigned16(65536)
def test_unsigned_tag(self):
if _debug: TestUnsigned._debug("test_unsigned_tag")