PKF`j)1)1bacpypes/analysis.py#!/usr/bin/python """ Analysis - Decoding pcap files """ import sys import time import socket import struct pcap = None try: import pcap except: pass from .debugging import ModuleLogger, DebugContents, bacpypes_debugging from .pdu import PDU, Address from .bvll import BVLPDU, bvl_pdu_types, ForwardedNPDU, \ DistributeBroadcastToNetwork, OriginalUnicastNPDU, OriginalBroadcastNPDU from .npdu import NPDU, npdu_types from .apdu import APDU, apdu_types, confirmed_request_types, unconfirmed_request_types, complex_ack_types, error_types, \ ConfirmedRequestPDU, UnconfirmedRequestPDU, SimpleAckPDU, ComplexAckPDU, SegmentAckPDU, ErrorPDU, RejectPDU, AbortPDU # some debugging _debug = 0 _log = ModuleLogger(globals()) # protocol map _protocols={socket.IPPROTO_TCP:'tcp', socket.IPPROTO_UDP:'udp', socket.IPPROTO_ICMP:'icmp'} # # _hexify # def _hexify(s, sep='.'): return sep.join('%02X' % ord(c) for c in s) # # strftimestamp # def strftimestamp(ts): return time.strftime("%d-%b-%Y %H:%M:%S", time.localtime(ts)) \ + (".%06d" % ((ts - int(ts)) * 1000000,)) # # decode_ethernet # @bacpypes_debugging def decode_ethernet(s): if _debug: decode_ethernet._debug("decode_ethernet %s...", _hexify(s[:14])) d={} d['destination_address'] = _hexify(s[0:6], ':') d['source_address'] = _hexify(s[6:12], ':') d['type'] = struct.unpack('!H',s[12:14])[0] d['data'] = s[14:] return d # # decode_vlan # @bacpypes_debugging def decode_vlan(s): if _debug: decode_vlan._debug("decode_vlan %s...", _hexify(s[:4])) d = {} x = struct.unpack('!H',s[0:2])[0] d['priority'] = (x >> 13) & 0x07 d['cfi'] = (x >> 12) & 0x01 d['vlan'] = x & 0x0FFF d['type'] = struct.unpack('!H',s[2:4])[0] d['data'] = s[4:] return d # # decode_ip # @bacpypes_debugging def decode_ip(s): if _debug: decode_ip._debug("decode_ip %r", _hexify(s[:20])) d = {} d['version'] = (ord(s[0]) & 0xf0) >> 4 d['header_len'] = ord(s[0]) & 0x0f d['tos'] = ord(s[1]) d['total_len'] = struct.unpack('!H',s[2:4])[0] d['id'] = struct.unpack('!H',s[4:6])[0] d['flags'] = (ord(s[6]) & 0xe0) >> 5 d['fragment_offset'] = struct.unpack('!H',s[6:8])[0] & 0x1f d['ttl'] = ord(s[8]) d['protocol'] = _protocols.get(ord(s[9]), '0x%.2x ?' % ord(s[9])) d['checksum'] = struct.unpack('!H',s[10:12])[0] d['source_address'] = socket.inet_ntoa(s[12:16]) d['destination_address'] = socket.inet_ntoa(s[16:20]) if d['header_len'] > 5: d['options'] = s[20:4*(d['header_len']-5)] else: d['options'] = None d['data'] = s[4*d['header_len']:] return d # # decode_udp # @bacpypes_debugging def decode_udp(s): if _debug: decode_udp._debug("decode_udp %s...", _hexify(s[:8])) d = {} d['source_port'] = struct.unpack('!H',s[0:2])[0] d['destination_port'] = struct.unpack('!H',s[2:4])[0] d['length'] = struct.unpack('!H',s[4:6])[0] d['checksum'] = struct.unpack('!H',s[6:8])[0] d['data'] = s[8:8 + d['length'] - 8] return d # # decode_packet # @bacpypes_debugging def decode_packet(data): """decode the data, return some kind of PDU.""" if _debug: decode_packet._debug("decode_packet %r", data) # empty strings are some other kind of pcap content if not data: return None # assume it is ethernet for now d = decode_ethernet(data) data = d['data'] # there could be a VLAN header if (d['type'] == 0x8100): if _debug: decode_packet._debug(" - vlan found") d = decode_vlan(data) data = d['data'] # look for IP packets if (d['type'] == 0x0800): if _debug: decode_packet._debug(" - IP found") d = decode_ip(data) pduSource, pduDestination = d['source_address'], d['destination_address'] data = d['data'] if (d['protocol'] == 'udp'): if _debug: decode_packet._debug(" - UDP found") d = decode_udp(data) data = d['data'] pduSource = Address((pduSource, d['source_port'])) pduDestination = Address((pduDestination, d['destination_port'])) if _debug: decode_packet._debug(" - pduSource: %r", pduSource) decode_packet._debug(" - pduDestination: %r", pduDestination) else: if _debug: decode_packet._debug(" - not a UDP packet") return None else: if _debug: decode_packet._debug(" - not an IP packet") return None # check for empty if not data: if _debug: decode_packet._debug(" - empty packet") return None # build a PDU pdu = PDU(data, source=pduSource, destination=pduDestination) # check for a BVLL header if (pdu.pduData[0] == '\x81'): if _debug: decode_packet._debug(" - BVLL header found") xpdu = BVLPDU() xpdu.decode(pdu) pdu = xpdu # make a more focused interpretation atype = bvl_pdu_types.get(pdu.bvlciFunction) if not atype: if _debug: decode_packet._debug(" - unknown BVLL type: %r", pdu.bvlciFunction) return pdu # decode it as one of the basic types try: xpdu = pdu bpdu = atype() bpdu.decode(pdu) if _debug: decode_packet._debug(" - bpdu: %r", bpdu) pdu = bpdu # lift the address for forwarded NPDU's if atype is ForwardedNPDU: pdu.pduSource = bpdu.bvlciAddress # no deeper decoding for some elif atype not in (DistributeBroadcastToNetwork, OriginalUnicastNPDU, OriginalBroadcastNPDU): return pdu except Exception as err: if _debug: decode_packet._debug(" - decoding Error: %r", err) return xpdu # check for version number if (pdu.pduData[0] != '\x01'): if _debug: decode_packet._debug(" - not a version 1 packet: %s...", _hexify(pdu.pduData[:30])) return None # it's an NPDU try: npdu = NPDU() npdu.decode(pdu) except Exception as err: if _debug: decode_packet._debug(" - decoding Error: %r", err) return None # application or network layer message if npdu.npduNetMessage is None: if _debug: decode_packet._debug(" - not a network layer message, try as an APDU") # decode as a generic APDU try: xpdu = APDU() xpdu.decode(npdu) apdu = xpdu except Exception as err: if _debug: decode_packet._debug(" - decoding Error: %r", err) return npdu # "lift" the source and destination address if npdu.npduSADR: apdu.pduSource = npdu.npduSADR else: apdu.pduSource = npdu.pduSource if npdu.npduDADR: apdu.pduDestination = npdu.npduDADR else: apdu.pduDestination = npdu.pduDestination # make a more focused interpretation atype = apdu_types.get(apdu.apduType) if not atype: if _debug: decode_packet._debug(" - unknown APDU type: %r", apdu.apduType) return apdu # decode it as one of the basic types try: xpdu = apdu apdu = atype() apdu.decode(xpdu) except Exception as err: if _debug: decode_packet._debug(" - decoding Error: %r", err) return xpdu # decode it at the next level if isinstance(apdu, ConfirmedRequestPDU): atype = confirmed_request_types.get(apdu.apduService) if not atype: if _debug: decode_packet._debug(" - no confirmed request decoder: %r", apdu.apduService) return apdu elif isinstance(apdu, UnconfirmedRequestPDU): atype = unconfirmed_request_types.get(apdu.apduService) if not atype: if _debug: decode_packet._debug(" - no unconfirmed request decoder: %r", apdu.apduService) return apdu elif isinstance(apdu, SimpleAckPDU): atype = None elif isinstance(apdu, ComplexAckPDU): atype = complex_ack_types.get(apdu.apduService) if not atype: if _debug: decode_packet._debug(" - no complex ack decoder: %r", apdu.apduService) return apdu elif isinstance(apdu, SegmentAckPDU): atype = None elif isinstance(apdu, ErrorPDU): atype = error_types.get(apdu.apduService) if not atype: if _debug: decode_packet._debug(" - no error decoder: %r", apdu.apduService) return apdu elif isinstance(apdu, RejectPDU): atype = None elif isinstance(apdu, AbortPDU): atype = None if _debug: decode_packet._debug(" - atype: %r", atype) # deeper decoding try: if atype: xpdu = apdu apdu = atype() apdu.decode(xpdu) except Exception as err: if _debug: decode_packet._debug(" - decoding error: %r", err) return xpdu # success return apdu else: # make a more focused interpretation ntype = npdu_types.get(npdu.npduNetMessage) if not ntype: if _debug: decode_packet._debug(" - no network layer decoder: %r", npdu.npduNetMessage) return npdu if _debug: decode_packet._debug(" - ntype: %r", ntype) # deeper decoding try: xpdu = npdu npdu = ntype() npdu.decode(xpdu) except Exception as err: if _debug: decode_packet._debug(" - decoding error: %r", err) return xpdu # success return npdu # # decode_file # @bacpypes_debugging def decode_file(fname): """Given the name of a pcap file, open it, decode the contents and yield each packet.""" if _debug: decode_file._debug("decode_file %r", fname) if not pcap: raise RuntimeError("failed to import pcap") # create a pcap object p = pcap.pcapObject() p.open_offline(fname) i = 0 while 1: # the object acts like an iterator pkt = p.next() if not pkt: break # returns a tuple pktlen, data, timestamp = pkt pkt = decode_packet(data) if not pkt: continue # save the index and timestamp in the packet pkt._index = i pkt._timestamp = timestamp yield pkt i += 1 # # Tracer # @bacpypes_debugging class Tracer(DebugContents): def __init__(self, initialState=None): if _debug: Tracer._debug("__init__ initialState=%r", initialState) # set the current state to the initial state self.Next(initialState or self.Start) def Next(self, fn): if _debug: Tracer._debug("Next %r", fn) # a little error checking if fn: assert fn.im_self is self # set the state self.currentState = fn def Start(self, pkt): if _debug: Tracer._debug("Start %r", pkt) # # trace # @bacpypes_debugging def trace(fname, tracers): if _debug: trace._debug("trace %r %r", fname, tracers) # make a list of tracers currentTracers = [traceClass() for traceClass in tracers] # decode the file for pkt in decode_file(fname): for i, tracer in enumerate(currentTracers): # give the packet to the tracer tracer.currentState(pkt) # if there is no current state, make a new one if not tracer.currentState: currentTracers[i] = tracers[i]() # # __main__ # if __name__ == "__main__": try: from consolelogging import ConsoleLogHandler if ('--debug' in sys.argv): indx = sys.argv.index('--debug') for i in range(indx+1, len(sys.argv)): ConsoleLogHandler(sys.argv[i]) del sys.argv[indx:] _log.debug("initialization") for pkt in decode_file(sys.argv[1]): print(strftimestamp(pkt._timestamp), pkt.__class__.__name__) pkt.debug_contents() print('') except KeyboardInterrupt: pass except Exception as err: _log.exception("an error has occurred: %s", err) finally: _log.debug("finally") PK1HOJuPoPobacpypes/object.py#!/usr/bin/python """ Object """ import sys from .errors import ConfigurationError, ExecutionError from .debugging import function_debugging, ModuleLogger, Logging from .primitivedata import Atomic, BitString, Boolean, CharacterString, Date, \ Double, Integer, ObjectIdentifier, ObjectType, OctetString, Real, Time, \ Unsigned from .constructeddata import AnyAtomic, Array, ArrayOf, Choice, Element, \ Sequence, SequenceOf from .basetypes import AccessCredentialDisable, AccessCredentialDisableReason, \ AccessEvent, AccessPassbackMode, AccessRule, AccessThreatLevel, \ AccessUserType, AccessZoneOccupancyState, AccumulatorRecord, Action, \ ActionList, AddressBinding, AssignedAccessRights, AuthenticationFactor, \ AuthenticationFactorFormat, AuthenticationPolicy, AuthenticationStatus, \ AuthorizationException, AuthorizationMode, BackupState, BinaryPV, \ COVSubscription, CalendarEntry, ChannelValue, ClientCOV, \ CredentialAuthenticationFactor, DailySchedule, DateRange, DateTime, \ Destination, DeviceObjectPropertyReference, DeviceObjectReference, \ DeviceStatus, DoorAlarmState, DoorSecuredStatus, DoorStatus, DoorValue, \ EngineeringUnits, EventNotificationSubscription, EventParameter, \ EventState, EventTransitionBits, EventType, FaultParameter, FaultType, \ FileAccessMethod, LifeSafetyMode, LifeSafetyOperation, LifeSafetyState, \ LightingCommand, LightingInProgress, LightingTransition, LimitEnable, \ LockStatus, LogMultipleRecord, LogRecord, LogStatus, LoggingType, \ Maintenance, NetworkSecurityPolicy, NodeType, NotifyType, \ ObjectPropertyReference, ObjectTypesSupported, OptionalCharacterString, \ Polarity, PortPermission, Prescale, PriorityArray, ProcessIdSelection, \ ProgramError, ProgramRequest, ProgramState, PropertyAccessResult, \ PropertyIdentifier, Recipient, Reliability, RestartReason, Scale, \ SecurityKeySet, SecurityLevel, Segmentation, ServicesSupported, \ SetpointReference, ShedLevel, ShedState, SilencedState, SpecialEvent, \ StatusFlags, TimeStamp, VTClass, VTSession, WriteStatus from .apdu import EventNotificationParameters, ReadAccessSpecification, \ ReadAccessResult # some debugging _debug = 0 _log = ModuleLogger(globals()) # # PropertyError # class PropertyError(AttributeError): pass # a dictionary of object types and classes registered_object_types = {} # # register_object_type # @function_debugging def register_object_type(cls=None, vendor_id=0): if _debug: register_object_type._debug("register_object_type %s vendor_id=%s", repr(cls), vendor_id) # if cls isn't given, return a decorator if not cls: def _register(xcls): if _debug: register_object_type._debug("_register %s (vendor_id=%s)", repr(cls), vendor_id) return register_object_type(xcls, vendor_id) if _debug: register_object_type._debug(" - returning decorator") return _register # make sure it's an Object derived class if not issubclass(cls, Object): raise RuntimeError("Object derived class required") # build a property dictionary by going through the class and all its parents _properties = {} for c in cls.__mro__: for prop in getattr(c, 'properties', []): if prop.identifier not in _properties: _properties[prop.identifier] = prop # if the object type hasn't been provided, make an immutable one if 'objectType' not in _properties: _properties['objectType'] = ReadableProperty('objectType', ObjectType, cls.objectType, mutable=False) # store this in the class cls._properties = _properties # now save this in all our types registered_object_types[(cls.objectType, vendor_id)] = cls # return the class as a decorator return cls # # get_object_class # @function_debugging def get_object_class(object_type, vendor_id=0): """Return the class associated with an object type.""" if _debug: get_object_class._debug("get_object_class %r vendor_id=%r", object_type, vendor_id) # find the klass as given cls = registered_object_types.get((object_type, vendor_id)) if _debug: get_object_class._debug(" - direct lookup: %s", repr(cls)) # if the class isn't found and the vendor id is non-zero, try the standard class for the type if (not cls) and vendor_id: cls = registered_object_types.get((object_type, 0)) if _debug: get_object_class._debug(" - default lookup: %s", repr(cls)) return cls # # get_datatype # @function_debugging def get_datatype(object_type, propid, vendor_id=0): """Return the datatype for the property of an object.""" if _debug: get_datatype._debug("get_datatype %r %r vendor_id=%r", object_type, propid, vendor_id) # get the related class cls = get_object_class(object_type, vendor_id) if not cls: return None # get the property prop = cls._properties.get(propid) if not prop: return None # return the datatype return prop.datatype # # Property # class Property(Logging): def __init__(self, identifier, datatype, default=None, optional=True, mutable=True): if _debug: Property._debug("__init__ %s %s default=%r optional=%r mutable=%r", identifier, datatype, default, optional, mutable ) # keep the arguments self.identifier = identifier self.datatype = datatype self.optional = optional self.mutable = mutable self.default = default def ReadProperty(self, obj, arrayIndex=None): if _debug: Property._debug("ReadProperty(%s) %s arrayIndex=%r", self.identifier, obj, arrayIndex ) # get the value value = obj._values[self.identifier] # access an array if arrayIndex is not None: if not issubclass(self.datatype, Array): raise ExecutionError(errorClass='property', errorCode='propertyIsNotAnArray') if value is not None: # dive in, the water's fine value = value[arrayIndex] # all set return value def WriteProperty(self, obj, value, arrayIndex=None, priority=None, direct=False): if _debug: Property._debug("WriteProperty(%s) %s %r arrayIndex=%r priority=%r direct=%r", self.identifier, obj, value, arrayIndex, priority, direct ) if direct: if _debug: Property._debug(" - direct write") else: # see if it must be provided if not self.optional and value is None: raise ValueError("%s value required" % (self.identifier,)) # see if it can be changed if not self.mutable: raise ExecutionError(errorClass='property', errorCode='writeAccessDenied') # if it's atomic assume correct datatype if issubclass(self.datatype, Atomic): if _debug: Property._debug(" - property is atomic, assumed correct type") elif isinstance(value, self.datatype): if _debug: Property._debug(" - correct type") elif arrayIndex is not None: if not issubclass(self.datatype, Array): raise ExecutionError(errorClass='property', errorCode='propertyIsNotAnArray') # check the array arry = obj._values[self.identifier] if arry is None: raise RuntimeError("%s uninitialized array" % (self.identifier,)) # seems to be OK, let the array object take over if _debug: Property._debug(" - forwarding to array") arry[arrayIndex] = value return elif value is not None: # coerce the value value = self.datatype(value) if _debug: Property._debug(" - coerced the value: %r", value) # seems to be OK obj._values[self.identifier] = value # # StandardProperty # class StandardProperty(Property, Logging): def __init__(self, identifier, datatype, default=None, optional=True, mutable=True): if _debug: StandardProperty._debug("__init__ %s %s default=%r optional=%r mutable=%r", identifier, datatype, default, optional, mutable ) # use one of the subclasses if not isinstance(self, (OptionalProperty, ReadableProperty, WritableProperty)): raise ConfigurationError(self.__class__.__name__ + " must derive from OptionalProperty, ReadableProperty, or WritableProperty") # validate the identifier to be one of the standard property enumerations if identifier not in PropertyIdentifier.enumerations: raise ConfigurationError("unknown standard property identifier: %s" % (identifier,)) # continue with the initialization Property.__init__(self, identifier, datatype, default, optional, mutable) # # OptionalProperty # class OptionalProperty(StandardProperty, Logging): """The property is required to be present and readable using BACnet services.""" def __init__(self, identifier, datatype, default=None, optional=True, mutable=False): if _debug: OptionalProperty._debug("__init__ %s %s default=%r optional=%r mutable=%r", identifier, datatype, default, optional, mutable ) # continue with the initialization StandardProperty.__init__(self, identifier, datatype, default, optional, mutable) # # ReadableProperty # class ReadableProperty(StandardProperty, Logging): """The property is required to be present and readable using BACnet services.""" def __init__(self, identifier, datatype, default=None, optional=False, mutable=False): if _debug: ReadableProperty._debug("__init__ %s %s default=%r optional=%r mutable=%r", identifier, datatype, default, optional, mutable ) # continue with the initialization StandardProperty.__init__(self, identifier, datatype, default, optional, mutable) # # WritableProperty # class WritableProperty(StandardProperty, Logging): """The property is required to be present, readable, and writable using BACnet services.""" def __init__(self, identifier, datatype, default=None, optional=False, mutable=True): if _debug: WritableProperty._debug("__init__ %s %s default=%r optional=%r mutable=%r", identifier, datatype, default, optional, mutable ) # continue with the initialization StandardProperty.__init__(self, identifier, datatype, default, optional, mutable) # # ObjectIdentifierProperty # class ObjectIdentifierProperty(ReadableProperty, Logging): def WriteProperty(self, obj, value, arrayIndex=None, priority=None, direct=False): if _debug: ObjectIdentifierProperty._debug("WriteProperty %r %r arrayIndex=%r priority=%r", obj, value, arrayIndex, priority) # make it easy to default if value is None: pass elif isinstance(value, int): value = (obj.objectType, value) elif isinstance(value, tuple) and len(value) == 2: if value[0] != obj.objectType: raise ValueError("%s required" % (obj.objectType,)) else: raise TypeError("object identifier") return Property.WriteProperty( self, obj, value, arrayIndex, priority, direct ) # # Object # class Object(Logging): properties = \ [ ObjectIdentifierProperty('objectIdentifier', ObjectIdentifier, optional=False) , ReadableProperty('objectName', CharacterString, optional=False) , ReadableProperty('description', CharacterString) , OptionalProperty('profileName', CharacterString) , ReadableProperty('propertyList', ArrayOf(PropertyIdentifier)) ] _properties = {} def __init__(self, **kwargs): """Create an object, with default property values as needed.""" if _debug: Object._debug("__init__(%s) %r", self.__class__.__name__, kwargs) # map the python names into property names and make sure they # are appropriate for this object initargs = {} for key, value in kwargs.items(): if key not in self._properties: raise PropertyError(key) initargs[key] = value # object is detached from an application until it is added self._app = None # start with a clean dict of values self._values = {} # 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: if _debug: Object._debug(" - setting %s from initargs", propid) # 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) self._values[propid] = None if _debug: Object._debug(" - done __init__") def _attr_to_property(self, attr): """Common routine to translate a python attribute name to a property name and return the appropriate property.""" # get the property prop = self._properties.get(attr) if _debug: Object._debug(" - prop: %r", prop) if not prop: raise PropertyError(attr) # found it return prop def __getattr__(self, attr): if _debug: Object._debug("__getattr__ %r", attr) # do not redirect private attrs or functions if attr.startswith('_') or attr[0].isupper() or (attr == 'debug_contents'): return object.__getattribute__(self, attr) # defer to the property to get the value prop = self._attr_to_property(attr) if _debug: Object._debug(" - deferring to %r", prop) # defer to the property to get the value return prop.ReadProperty(self) def __setattr__(self, attr, value): if _debug: Object._debug("__setattr__ %r %r", attr, value) if attr.startswith('_') or attr[0].isupper() or (attr == 'debug_contents'): return object.__setattr__(self, attr, value) # defer to the property to normalize the value prop = self._attr_to_property(attr) if _debug: Object._debug(" - deferring to %r", prop) return prop.WriteProperty(self, value, direct=True) def ReadProperty(self, propid, arrayIndex=None): if _debug: Object._debug("ReadProperty %r arrayIndex=%r", propid, arrayIndex) # get the property prop = self._properties.get(propid) if not prop: raise PropertyError(propid) # defer to the property to get the value return prop.ReadProperty(self, arrayIndex) def WriteProperty(self, propid, value, arrayIndex=None, priority=None, direct=False): if _debug: Object._debug("WriteProperty %r %r arrayIndex=%r priority=%r", propid, value, arrayIndex, priority) # get the property prop = self._properties.get(propid) if not prop: raise PropertyError(propid) # defer to the property to set the value return prop.WriteProperty(self, value, arrayIndex, priority, direct) def get_datatype(self, propid): """Return the datatype for the property of an object.""" if _debug: Object._debug("get_datatype %r", propid) # get the property prop = self._properties.get(propid) if not prop: raise PropertyError(propid) # return the datatype return prop.datatype def _dict_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" if _debug: Object._debug("dict_contents use_dict=%r as_class=%r", use_dict, as_class) # make/extend the dictionary of content if use_dict is None: use_dict = as_class() klasses = list(self.__class__.__mro__) klasses.reverse() # print special attributes "bottom up" previous_attrs = () for c in klasses: attrs = getattr(c, '_debug_contents', ()) # if we have seen this list already, move to the next class if attrs is previous_attrs: continue for attr in attrs: file.write("%s%s = %s\n" % (" " * indent, attr, getattr(self, attr))) previous_attrs = attrs # build a list of properties "bottom up" properties = [] for c in klasses: properties.extend(getattr(c, 'properties', [])) # print out the values for prop in properties: value = prop.ReadProperty(self) if value is None: continue if hasattr(value, "dict_contents"): value = value.dict_contents(as_class=as_class) # save the value use_dict.__setitem__(prop.identifier, value) # return what we built/updated return use_dict def debug_contents(self, indent=1, file=sys.stdout, _ids=None): """Print out interesting things about the object.""" klasses = list(self.__class__.__mro__) klasses.reverse() # build a list of properties "bottom up" properties = [] for c in klasses: properties.extend(getattr(c, 'properties', [])) # print out the values for prop in properties: value = prop.ReadProperty(self) # printing out property values that are None is tedious if value is None: continue if hasattr(value, "debug_contents"): file.write("%s%s\n" % (" " * indent, prop.identifier)) value.debug_contents(indent+1, file, _ids) else: file.write("%s%s = %r\n" % (" " * indent, prop.identifier, value)) # # Standard Object Types # @register_object_type class AccessCredentialObject(Object): objectType = 'accessCredential' properties = \ [ WritableProperty('globalIdentifier', Unsigned) , ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('reliability', Reliability) , ReadableProperty('credentialStatus', BinaryPV) , ReadableProperty('reasonForDisable', SequenceOf(AccessCredentialDisableReason)) , ReadableProperty('authenticationFactors', ArrayOf(CredentialAuthenticationFactor)) , ReadableProperty('activationTime', DateTime) , ReadableProperty('expiryTime', DateTime) , ReadableProperty('credentialDisable', AccessCredentialDisable) , OptionalProperty('daysRemaining', Integer) , OptionalProperty('usesRemaining', Integer) , OptionalProperty('absenteeLimit', Unsigned) , OptionalProperty('belongsTo', DeviceObjectReference) , ReadableProperty('assignedAccessRights', ArrayOf(AssignedAccessRights)) , OptionalProperty('lastAccessPoint', DeviceObjectReference) , OptionalProperty('lastAccessEvent', AccessEvent) , OptionalProperty('lastUseTime', DateTime) , OptionalProperty('traceFlag', Boolean) , OptionalProperty('threatAuthority', AccessThreatLevel) , OptionalProperty('extendedTimeEnable', Boolean) , OptionalProperty('authorizationExemptions', SequenceOf(AuthorizationException)) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) , OptionalProperty('masterExemption', Boolean) , OptionalProperty('passbackExemption', Boolean) , OptionalProperty('occupancyExemption', Boolean) ] @register_object_type class AccessDoorObject(Object): objectType = 'accessDoor' properties = \ [ WritableProperty('presentValue', DoorValue) , ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('eventState', EventState) , ReadableProperty('reliability', Reliability) , ReadableProperty('outOfService', Boolean) , ReadableProperty('priorityArray', PriorityArray) , ReadableProperty('relinquishDefault', DoorValue) , OptionalProperty('doorStatus', DoorStatus) , OptionalProperty('lockStatus', LockStatus) , OptionalProperty('securedStatus', DoorSecuredStatus) , OptionalProperty('doorMembers', ArrayOf(DeviceObjectReference)) , ReadableProperty('doorPulseTime', Unsigned) , ReadableProperty('doorExtendedPulseTime', Unsigned) , OptionalProperty('doorUnlockDelayTime', Unsigned) , ReadableProperty('doorOpenTooLongTime', Unsigned) , OptionalProperty('doorAlarmState', DoorAlarmState) , OptionalProperty('maskedAlarmValues', SequenceOf(DoorAlarmState)) , OptionalProperty('maintenanceRequired', Maintenance) , OptionalProperty('timeDelay', Unsigned) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('alarmValues', SequenceOf(DoorAlarmState)) , OptionalProperty('faultValues', SequenceOf(DoorAlarmState)) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) ] @register_object_type class AccessPointObject(Object): objectType = 'accessPoint' properties = \ [ ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('eventState', EventState) , ReadableProperty('reliability', Reliability) , ReadableProperty('outOfService', Boolean) , ReadableProperty('authenticationStatus', AuthenticationStatus) , ReadableProperty('activeAuthenticationPolicy', Unsigned) , ReadableProperty('numberOfAuthenticationPolicies', Unsigned) , OptionalProperty('authenticationPolicyList', ArrayOf(AuthenticationPolicy)) , OptionalProperty('authenticationPolicyNames', ArrayOf(CharacterString)) , ReadableProperty('authorizationMode', AuthorizationMode) , OptionalProperty('verificationTime', Unsigned) , OptionalProperty('lockout', Boolean) , OptionalProperty('lockoutRelinquishTime', Unsigned) , OptionalProperty('failedAttempts', Unsigned) , OptionalProperty('failedAttemptEvents', SequenceOf(AccessEvent)) , OptionalProperty('maxFailedAttempts', Unsigned) , OptionalProperty('failedAttemptsTime', Unsigned) , OptionalProperty('threatLevel', AccessThreatLevel) , OptionalProperty('occupancyUpperLimitEnforced', Boolean) , OptionalProperty('occupancyLowerLimitEnforced', Boolean) , OptionalProperty('occupancyCountAdjust', Boolean) , OptionalProperty('accompanimentTime', Unsigned) , ReadableProperty('accessEvent', AccessEvent) , ReadableProperty('accessEventTag', Unsigned) , ReadableProperty('accessEventTime', TimeStamp) , ReadableProperty('accessEventCredential', DeviceObjectReference) , OptionalProperty('accessEventAuthenticationFactor', AuthenticationFactor) , ReadableProperty('accessDoors', ArrayOf(DeviceObjectReference)) , ReadableProperty('priorityForWriting', Unsigned) , OptionalProperty('musterPoint', Boolean) , OptionalProperty('zoneTo', DeviceObjectReference) , OptionalProperty('zoneFrom', DeviceObjectReference) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('transactionNotificationClass', Unsigned) , OptionalProperty('accessAlarmEvents', SequenceOf(AccessEvent)) , OptionalProperty('accessTransactionEvents', SequenceOf(AccessEvent)) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @register_object_type class AccessRightsObject(Object): objectType = 'accessRights' properties = \ [ WritableProperty('globalIdentifier', Unsigned) , ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('reliability', Reliability) , ReadableProperty('enable', Boolean) , ReadableProperty('negativeAccessRules', ArrayOf(AccessRule)) , ReadableProperty('positiveAccessRules', ArrayOf(AccessRule)) , OptionalProperty('accompaniment', DeviceObjectReference) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @register_object_type class AccessUserObject(Object): objectType = 'accessUser' properties = \ [ WritableProperty('globalIdentifier', Unsigned) , ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('reliability', Reliability) , ReadableProperty('userType', AccessUserType) , OptionalProperty('userName', CharacterString) , OptionalProperty('userExternalIdentifier', CharacterString) , OptionalProperty('userInformationReference', CharacterString) , OptionalProperty('members', SequenceOf(DeviceObjectReference)) , OptionalProperty('memberOf', SequenceOf(DeviceObjectReference)) , ReadableProperty('credentials', SequenceOf(DeviceObjectReference)) ] @register_object_type class AccessZoneObject(Object): objectType = 'accessZone' properties = \ [ WritableProperty('globalIdentifier', Unsigned) , ReadableProperty('occupancyState', AccessZoneOccupancyState) , ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('eventState', EventState) , ReadableProperty('reliability', Reliability) , ReadableProperty('outOfService', Boolean) , OptionalProperty('occupancyCount', Unsigned) , OptionalProperty('occupancyCountEnable', Boolean) , OptionalProperty('adjustValue', Integer) , OptionalProperty('occupancyUpperLimit', Unsigned) , OptionalProperty('occupancyLowerLimit', Unsigned) , OptionalProperty('credentialsInZone', SequenceOf(DeviceObjectReference) ) , OptionalProperty('lastCredentialAdded', DeviceObjectReference) , OptionalProperty('lastCredentialAddedTime', DateTime) , OptionalProperty('lastCredentialRemoved', DeviceObjectReference) , OptionalProperty('lastCredentialRemovedTime', DateTime) , OptionalProperty('passbackMode', AccessPassbackMode) , OptionalProperty('passbackTimeout', Unsigned) , ReadableProperty('entryPoints', SequenceOf(DeviceObjectReference)) , ReadableProperty('exitPoints', SequenceOf(DeviceObjectReference)) , OptionalProperty('timeDelay', Unsigned) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('alarmValues', SequenceOf(AccessZoneOccupancyState)) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) , OptionalProperty('timeDelayNormal', Unsigned) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @register_object_type class AccumulatorObject(Object): objectType = 'accumulator' properties = \ [ ReadableProperty('presentValue', Unsigned) , OptionalProperty('deviceType', CharacterString) , ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , ReadableProperty('outOfService', Boolean) , ReadableProperty('scale', Scale) , ReadableProperty('units', EngineeringUnits) , OptionalProperty('prescale', Prescale) , ReadableProperty('maxPresValue', Unsigned) , OptionalProperty('valueChangeTime', DateTime) , OptionalProperty('valueBeforeChange', Unsigned) , OptionalProperty('valueSet', Unsigned) , OptionalProperty('loggingRecord', AccumulatorRecord) , OptionalProperty('loggingObject', ObjectIdentifier) , OptionalProperty('pulseRate', Unsigned) , OptionalProperty('highLimit', Unsigned) , OptionalProperty('lowLimit', Unsigned) , OptionalProperty('limitMonitoringInterval', Unsigned) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('timeDelay', Unsigned) , OptionalProperty('limitEnable', LimitEnable) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) , OptionalProperty('timeDelayNormal', Unsigned) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @register_object_type class AlertEnrollmentObject(Object): objectType = 'alertEnrollment' properties = \ [ ReadableProperty('presentValue', ObjectIdentifier) , ReadableProperty('eventState', EventState) , OptionalProperty('eventDetectionEnable', Boolean) , ReadableProperty('notificationClass', Unsigned) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) ] @register_object_type class AnalogInputObject(Object): objectType = 'analogInput' properties = \ [ ReadableProperty('presentValue', Real) , OptionalProperty('deviceType', CharacterString) , ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , ReadableProperty('outOfService', Boolean) , OptionalProperty('updateInterval', Unsigned) , ReadableProperty('units', EngineeringUnits) , OptionalProperty('minPresValue', Real) , OptionalProperty('maxPresValue', Real) , OptionalProperty('resolution', Real) , OptionalProperty('covIncrement', Real) , OptionalProperty('timeDelay', Unsigned) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('highLimit', Real) , OptionalProperty('lowLimit', Real) , OptionalProperty('deadband', Real) , OptionalProperty('limitEnable', LimitEnable) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) , OptionalProperty('timeDelayNormal', Unsigned) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @register_object_type class AnalogOutputObject(Object): objectType = 'analogOutput' properties = \ [ WritableProperty('presentValue', Real) , OptionalProperty('deviceType', CharacterString) , ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , ReadableProperty('outOfService', Boolean) , ReadableProperty('units', EngineeringUnits) , OptionalProperty('minPresValue', Real) , OptionalProperty('maxPresValue', Real) , OptionalProperty('resolution', Real) , ReadableProperty('priorityArray', PriorityArray) , ReadableProperty('relinquishDefault', Real) , OptionalProperty('covIncrement', Real) , OptionalProperty('timeDelay', Unsigned) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('highLimit', Real) , OptionalProperty('lowLimit', Real) , OptionalProperty('deadband', Real) , OptionalProperty('limitEnable', LimitEnable) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) , OptionalProperty('timeDelayNormal', Unsigned) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @register_object_type class AnalogValueObject(Object): objectType = 'analogValue' properties = \ [ ReadableProperty('presentValue', Real) , ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , ReadableProperty('outOfService', Boolean) , ReadableProperty('units', EngineeringUnits) , OptionalProperty('minPresValue', Real) , OptionalProperty('maxPresValue', Real) , OptionalProperty('resolution', Real) , OptionalProperty('priorityArray', PriorityArray) , OptionalProperty('relinquishDefault', Real) , OptionalProperty('covIncrement', Real) , OptionalProperty('timeDelay', Unsigned) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('highLimit', Real) , OptionalProperty('lowLimit', Real) , OptionalProperty('deadband', Real) , OptionalProperty('limitEnable', LimitEnable) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) , OptionalProperty('timeDelayNormal', Unsigned) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @register_object_type class AveragingObject(Object): objectType = 'averaging' properties = \ [ ReadableProperty('minimumValue', Real) , OptionalProperty('minimumValueTimestamp', DateTime) , ReadableProperty('averageValue', Real) , OptionalProperty('varianceValue', Real) , ReadableProperty('maximumValue', Real) , OptionalProperty('maximumValueTimestamp', DateTime) , WritableProperty('attemptedSamples', Unsigned) , ReadableProperty('validSamples', Unsigned) , ReadableProperty('objectPropertyReference', DeviceObjectPropertyReference) , WritableProperty('windowInterval', Unsigned) , WritableProperty('windowSamples', Unsigned) ] @register_object_type class BinaryInputObject(Object): objectType = 'binaryInput' properties = \ [ ReadableProperty('presentValue', BinaryPV) , OptionalProperty('deviceType', CharacterString) , ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , ReadableProperty('outOfService', Boolean) , ReadableProperty('polarity', Polarity) , OptionalProperty('inactiveText', CharacterString) , OptionalProperty('activeText', CharacterString) , OptionalProperty('changeOfStateTime', DateTime) , OptionalProperty('changeOfStateCount', Unsigned) , OptionalProperty('timeOfStateCountReset', DateTime) , OptionalProperty('elapsedActiveTime', Unsigned) , OptionalProperty('timeOfActiveTimeReset', DateTime) , OptionalProperty('timeDelay', Unsigned) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('alarmValue', BinaryPV) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) , OptionalProperty('timeDelayNormal', Unsigned) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @register_object_type class BinaryOutputObject(Object): objectType = 'binaryOutput' properties = \ [ WritableProperty('presentValue', BinaryPV) , OptionalProperty('deviceType', CharacterString) , ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , ReadableProperty('outOfService', Boolean) , ReadableProperty('polarity', Polarity) , OptionalProperty('inactiveText', CharacterString) , OptionalProperty('activeText', CharacterString) , OptionalProperty('changeOfStateTime', DateTime) , OptionalProperty('changeOfStateCount', Unsigned) , OptionalProperty('timeOfStateCountReset', DateTime) , OptionalProperty('elapsedActiveTime', Unsigned) , OptionalProperty('timeOfActiveTimeReset', DateTime) , OptionalProperty('minimumOffTime', Unsigned) , OptionalProperty('minimumOnTime', Unsigned) , ReadableProperty('priorityArray', PriorityArray) , ReadableProperty('relinquishDefault', BinaryPV) , OptionalProperty('timeDelay', Unsigned) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('feedbackValue', BinaryPV) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) , OptionalProperty('timeDelayNormal', Unsigned) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @register_object_type class BinaryValueObject(Object): objectType = 'binaryValue' properties = \ [ WritableProperty('presentValue', BinaryPV) , ReadableProperty('statusFlags',StatusFlags) , ReadableProperty('eventState',EventState) , OptionalProperty('reliability',Reliability) , ReadableProperty('outOfService',Boolean) , OptionalProperty('inactiveText',CharacterString) , OptionalProperty('activeText',CharacterString) , OptionalProperty('changeOfStateTime',DateTime) , OptionalProperty('changeOfStateCount',Unsigned) , OptionalProperty('timeOfStateCountReset',DateTime) , OptionalProperty('elapsedActiveTime',Unsigned) , OptionalProperty('timeOfActiveTimeReset',DateTime) , OptionalProperty('minimumOffTime',Unsigned) , OptionalProperty('minimumOnTime',Unsigned) , OptionalProperty('priorityArray',PriorityArray) , OptionalProperty('relinquishDefault',BinaryPV) , OptionalProperty('timeDelay',Unsigned) , OptionalProperty('notificationClass',Unsigned) , OptionalProperty('alarmValue',BinaryPV) , OptionalProperty('eventEnable',EventTransitionBits) , OptionalProperty('ackedTransitions',EventTransitionBits) , OptionalProperty('notifyType',NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) , OptionalProperty('timeDelayNormal', Unsigned) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @register_object_type class BitStringValueObject(Object): objectType = 'bitstringValue' properties = \ [ ReadableProperty('presentValue', BitString) , OptionalProperty('bitText', ArrayOf(CharacterString)) , ReadableProperty('statusFlags', StatusFlags) , OptionalProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , OptionalProperty('outOfService', Boolean) , OptionalProperty('priorityArray', PriorityArray) , OptionalProperty('relinquishDefault', BitString) , OptionalProperty('timeDelay', Unsigned) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('alarmValues', ArrayOf(BitString)) , OptionalProperty('bitMask', BitString) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) ] @register_object_type class CalendarObject(Object): objectType = 'calendar' properties = \ [ ReadableProperty('presentValue', Boolean) , ReadableProperty('dateList', SequenceOf(CalendarEntry)) ] @register_object_type class ChannelObject(Object): objectType = 'channel' properties = \ [ WritableProperty('presentValue', ChannelValue) , ReadableProperty('lastPriority', Unsigned) , ReadableProperty('writeStatus', WriteStatus) , ReadableProperty('statusFlags', StatusFlags) , OptionalProperty('reliability', Reliability) , ReadableProperty('outOfService', Boolean) , WritableProperty('listOfObjectPropertyReferences', ArrayOf(DeviceObjectPropertyReference)) , OptionalProperty('executionDelay', ArrayOf(Unsigned)) , OptionalProperty('allowGroupDelayInhibit', Boolean) , WritableProperty('channelNumber', Unsigned) , WritableProperty('controlGroups', ArrayOf(Unsigned)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('eventState', EventState) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @register_object_type class CharacterStringValueObject(Object): objectType = 'characterstringValue' properties = \ [ ReadableProperty('presentValue', CharacterString) , ReadableProperty('statusFlags', StatusFlags) , OptionalProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , OptionalProperty('outOfService', Boolean) , OptionalProperty('priorityArray', PriorityArray) , OptionalProperty('relinquishDefault', CharacterString) , OptionalProperty('timeDelay', Unsigned) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('alarmValues', ArrayOf(OptionalCharacterString)) , OptionalProperty('faultValues', ArrayOf(OptionalCharacterString)) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) , OptionalProperty('timeDelayNormal', Unsigned) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @register_object_type class CommandObject(Object): objectType = 'command' properties = \ [ WritableProperty('presentValue', Unsigned) , ReadableProperty('inProcess', Boolean) , ReadableProperty('allWritesSuccessful', Boolean) , ReadableProperty('action', ArrayOf(ActionList)) , OptionalProperty('actionText', ArrayOf(CharacterString)) ] @register_object_type class CredentialDataInputObject(Object): objectType = 'credentialDataInput' properties = \ [ ReadableProperty('presentValue', AuthenticationFactor) , ReadableProperty('statusFlags', StatusFlags) , OptionalProperty('reliability', Reliability) , ReadableProperty('outOfService', Boolean) , ReadableProperty('supportedFormats', ArrayOf(AuthenticationFactorFormat)) , ReadableProperty('supportedFormatClasses', ArrayOf(Unsigned)) , ReadableProperty('updateTime', TimeStamp) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @register_object_type class DatePatternValueObject(Object): objectType = 'datePatternValue' properties = \ [ ReadableProperty('presentValue', Date) , ReadableProperty('statusFlags', StatusFlags) , OptionalProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , OptionalProperty('outOfService', Boolean) , OptionalProperty('priorityArray', PriorityArray) , OptionalProperty('relinquishDefault', Date) ] @register_object_type class DateValueObject(Object): objectType = 'dateValue' properties = \ [ ReadableProperty('presentValue', Date) , ReadableProperty('statusFlags', StatusFlags) , OptionalProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , OptionalProperty('outOfService', Boolean) , OptionalProperty('priorityArray', PriorityArray) , OptionalProperty('relinquishDefault', Date) ] @register_object_type class DateTimePatternValueObject(Object): objectType = 'datetimePatternValue' properties = \ [ ReadableProperty('presentValue', DateTime) , ReadableProperty('statusFlags', StatusFlags) , OptionalProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , OptionalProperty('outOfService', Boolean) , OptionalProperty('priorityArray', PriorityArray) , OptionalProperty('relinquishDefault', DateTime) , OptionalProperty('isUtc', Boolean) ] @register_object_type class DateTimeValueObject(Object): objectType = 'datetimeValue' properties = \ [ ReadableProperty('presentValue', DateTime) , ReadableProperty('statusFlags', StatusFlags) , OptionalProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , OptionalProperty('outOfService', Boolean) , OptionalProperty('priorityArray', PriorityArray) , OptionalProperty('relinquishDefault', DateTime) , OptionalProperty('isUtc', Boolean) ] @register_object_type class DeviceObject(Object): objectType = 'device' properties = \ [ ReadableProperty('systemStatus', DeviceStatus) , ReadableProperty('vendorName', CharacterString) , ReadableProperty('vendorIdentifier', Unsigned) , ReadableProperty('modelName', CharacterString) , ReadableProperty('firmwareRevision', CharacterString) , ReadableProperty('applicationSoftwareVersion', CharacterString) , OptionalProperty('location', CharacterString) , ReadableProperty('protocolVersion', Unsigned) , ReadableProperty('protocolRevision', Unsigned) , ReadableProperty('protocolServicesSupported', ServicesSupported) , ReadableProperty('protocolObjectTypesSupported', ObjectTypesSupported) , ReadableProperty('objectList', ArrayOf(ObjectIdentifier)) , OptionalProperty('structuredObjectList', ArrayOf(ObjectIdentifier)) , ReadableProperty('maxApduLengthAccepted', Unsigned) , ReadableProperty('segmentationSupported', Segmentation) , OptionalProperty('vtClassesSupported', SequenceOf(VTClass)) , OptionalProperty('activeVtSessions', SequenceOf(VTSession)) , OptionalProperty('localTime', Time) , OptionalProperty('localDate', Date) , OptionalProperty('utcOffset', Integer) , OptionalProperty('daylightSavingsStatus', Boolean) , OptionalProperty('apduSegmentTimeout', Unsigned) , ReadableProperty('apduTimeout', Unsigned) , ReadableProperty('numberOfApduRetries', Unsigned) , OptionalProperty('timeSynchronizationRecipients', SequenceOf(Recipient)) , OptionalProperty('maxMaster', Unsigned) , OptionalProperty('maxInfoFrames', Unsigned) , ReadableProperty('deviceAddressBinding', SequenceOf(AddressBinding)) , ReadableProperty('databaseRevision', Unsigned) , OptionalProperty('configurationFiles', ArrayOf(ObjectIdentifier)) , OptionalProperty('lastRestoreTime', TimeStamp) , OptionalProperty('backupFailureTimeout', Unsigned) , OptionalProperty('backupPreparationTime', Unsigned) , OptionalProperty('restorePreparationTime', Unsigned) , OptionalProperty('restoreCompletionTime', Unsigned) , OptionalProperty('backupAndRestoreState', BackupState) , OptionalProperty('activeCovSubscriptions', SequenceOf(COVSubscription)) , OptionalProperty('maxSegmentsAccepted', Unsigned) , OptionalProperty('slaveProxyEnable', ArrayOf(Boolean)) , OptionalProperty('autoSlaveDiscovery', ArrayOf(Boolean)) , OptionalProperty('slaveAddressBinding', SequenceOf(AddressBinding)) , OptionalProperty('manualSlaveAddressBinding', SequenceOf(AddressBinding)) , OptionalProperty('lastRestartReason', RestartReason) , OptionalProperty('timeOfDeviceRestart', TimeStamp) , OptionalProperty('restartNotificationRecipients', SequenceOf(Recipient)) , OptionalProperty('utcTimeSynchronizationRecipients', SequenceOf(Recipient)) , OptionalProperty('timeSynchronizationInterval', Unsigned) , OptionalProperty('alignIntervals', Boolean) , OptionalProperty('intervalOffset', Unsigned) ] @register_object_type class EventEnrollmentObject(Object): objectType = 'eventEnrollment' properties = \ [ ReadableProperty('eventType', EventType) , ReadableProperty('notifyType', NotifyType) , ReadableProperty('eventParameters', EventParameter) , ReadableProperty('objectPropertyReference', DeviceObjectPropertyReference) , ReadableProperty('eventState', EventState) , ReadableProperty('eventEnable', EventTransitionBits) , ReadableProperty('ackedTransitions', EventTransitionBits) , ReadableProperty('notificationClass', Unsigned) , ReadableProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) , OptionalProperty('timeDelayNormal', Unsigned) , ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('reliability', Reliability) , OptionalProperty('faultType', FaultType) , OptionalProperty('faultParameters', FaultParameter) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] #----- class EventLogRecordLogDatum(Choice): choiceElements = \ [ Element('logStatus', LogStatus, 0) , Element('notification', EventNotificationParameters, 1) , Element('timeChange', Real, 2) ] class EventLogRecord(Sequence): sequenceElements = \ [ Element('timestamp', DateTime, 0) , Element('logDatum', EventLogRecordLogDatum, 1) ] @register_object_type class EventLogObject(Object): objectType = 'eventLog' properties = \ [ ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , WritableProperty('enable', Boolean) , OptionalProperty('startTime', DateTime) , OptionalProperty('stopTime', DateTime) , ReadableProperty('stopWhenFull', Boolean) , ReadableProperty('bufferSize', Unsigned) , ReadableProperty('logBuffer', SequenceOf(EventLogRecord)) , WritableProperty('recordCount', Unsigned) , ReadableProperty('totalRecordCount', Unsigned) , OptionalProperty('notificationThreshold', Unsigned) , OptionalProperty('recordsSinceNotification', Unsigned) , OptionalProperty('lastNotifyRecord', Unsigned) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) ] #----- @register_object_type class FileObject(Object): objectType = 'file' properties = \ [ ReadableProperty('fileType', CharacterString) , ReadableProperty('fileSize', Unsigned) , ReadableProperty('modificationDate', DateTime) , WritableProperty('archive', Boolean) , ReadableProperty('readOnly', Boolean) , ReadableProperty('fileAccessMethod', FileAccessMethod) , OptionalProperty('recordCount', Unsigned) ] #----- @register_object_type class GlobalGroupObject(Object): objectType = 'globalGroup' properties = \ [ ReadableProperty('groupMembers', ArrayOf(DeviceObjectPropertyReference)) , OptionalProperty('groupMemberNames', ArrayOf(CharacterString)) , ReadableProperty('presentValue', ArrayOf(PropertyAccessResult)) , ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('eventState', EventState) , ReadableProperty('memberStatusFlags', StatusFlags) , OptionalProperty('reliability', Reliability) , ReadableProperty('outOfService', Boolean) , OptionalProperty('updateInterval', Unsigned) , OptionalProperty('requestedUpdateInterval', Unsigned) , OptionalProperty('covResubscriptionInterval', Unsigned) , OptionalProperty('clientCovIncrement', ClientCOV) , OptionalProperty('timeDelay', Unsigned) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) , OptionalProperty('timeDelayNormal', Unsigned) , OptionalProperty('covuPeriod', Unsigned) , OptionalProperty('covuRecipients', SequenceOf(Recipient)) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @register_object_type class GroupObject(Object): objectType = 'group' properties = \ [ ReadableProperty('listOfGroupMembers', SequenceOf(ReadAccessSpecification)) , ReadableProperty('presentValue', SequenceOf(ReadAccessResult)) ] @register_object_type class IntegerValueObject(Object): objectType = 'integerValue' properties = \ [ ReadableProperty('presentValue', Integer) , ReadableProperty('statusFlags', StatusFlags) , OptionalProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , OptionalProperty('outOfService', Boolean) , ReadableProperty('units', EngineeringUnits) , OptionalProperty('priorityArray', PriorityArray) , OptionalProperty('relinquishDefault', Integer) , OptionalProperty('covIncrement', Unsigned) , OptionalProperty('timeDelay', Unsigned) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('highLimit', Integer) , OptionalProperty('lowLimit', Integer) , OptionalProperty('deadband', Unsigned) , OptionalProperty('limitEnable', LimitEnable) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) , OptionalProperty('timeDelayNormal', Unsigned) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) , OptionalProperty('minPresValue', Integer) , OptionalProperty('maxPresValue', Integer) , OptionalProperty('resolution', Integer) ] @register_object_type class LargeAnalogValueObject(Object): objectType = 'largeAnalogValue' properties = \ [ ReadableProperty('presentValue', Double) , ReadableProperty('statusFlags', StatusFlags) , OptionalProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , OptionalProperty('outOfService', Boolean) , ReadableProperty('units', EngineeringUnits) , OptionalProperty('priorityArray', PriorityArray) , OptionalProperty('relinquishDefault', Integer) , OptionalProperty('covIncrement', Unsigned) , OptionalProperty('timeDelay', Unsigned) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('highLimit', Double) , OptionalProperty('lowLimit', Double) , OptionalProperty('deadband', Double) , OptionalProperty('limitEnable', LimitEnable) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) , OptionalProperty('timeDelayNormal', Unsigned) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) , OptionalProperty('minPresValue', Double) , OptionalProperty('maxPresValue', Double) , OptionalProperty('resolution', Double) ] @register_object_type class LifeSafetyPointObject(Object): objectType = 'lifeSafetyPoint' properties = \ [ ReadableProperty('presentValue', LifeSafetyState) , ReadableProperty('trackingValue', LifeSafetyState) , OptionalProperty('deviceType', CharacterString) , ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('eventState', EventState) , ReadableProperty('reliability', Reliability) , ReadableProperty('outOfService', Boolean) , WritableProperty('mode', LifeSafetyMode) , ReadableProperty('acceptedModes', SequenceOf(LifeSafetyMode)) , OptionalProperty('timeDelay', Unsigned) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('lifeSafetyAlarmValues', SequenceOf(LifeSafetyState)) , OptionalProperty('alarmValues', SequenceOf(LifeSafetyState)) , OptionalProperty('faultValues', SequenceOf(LifeSafetyState)) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) , OptionalProperty('timeDelayNormal', Unsigned) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) , ReadableProperty('silenced', SilencedState) , ReadableProperty('operationExpected', LifeSafetyOperation) , OptionalProperty('maintenanceRequired', Maintenance) , OptionalProperty('setting', Unsigned) , OptionalProperty('directReading', Real) , OptionalProperty('units', EngineeringUnits) , OptionalProperty('memberOf', SequenceOf(DeviceObjectReference)) ] @register_object_type class LifeSafetyZoneObject(Object): objectType = 'lifeSafetyZone' properties = \ [ ReadableProperty('presentValue', LifeSafetyState) , ReadableProperty('trackingValue', LifeSafetyState) , OptionalProperty('deviceType', CharacterString) , ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('eventState', EventState) , ReadableProperty('reliability', Reliability) , ReadableProperty('outOfService', Boolean) , WritableProperty('mode', LifeSafetyMode) , ReadableProperty('acceptedModes', SequenceOf(LifeSafetyMode)) , OptionalProperty('timeDelay', Unsigned) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('lifeSafetyAlarmValues', SequenceOf(LifeSafetyState)) , OptionalProperty('alarmValues', SequenceOf(LifeSafetyState)) , OptionalProperty('faultValues', SequenceOf(LifeSafetyState)) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) , OptionalProperty('timeDelayNormal', Unsigned) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) , ReadableProperty('silenced', SilencedState) , ReadableProperty('operationExpected', LifeSafetyOperation) , OptionalProperty('maintenanceRequired', Boolean) , ReadableProperty('zoneMembers', SequenceOf(DeviceObjectReference)) , OptionalProperty('memberOf', SequenceOf(DeviceObjectReference)) ] @register_object_type class LightingOutputObject(Object): objectType = 'lightingOutput' properties = \ [ WritableProperty('presentValue', Real) , ReadableProperty('trackingValue', Real) , WritableProperty('lightingCommand', LightingCommand) , ReadableProperty('inProgress', LightingInProgress) , ReadableProperty('statusFlags', StatusFlags) , OptionalProperty('reliability', Reliability) , ReadableProperty('outOfService', Boolean) , ReadableProperty('blinkWarnEnable', Boolean) , ReadableProperty('egressTime', Unsigned) , ReadableProperty('egressActive', Boolean) , ReadableProperty('defaultFadeTime', Unsigned) , ReadableProperty('defaultRampRate', Real) , ReadableProperty('defaultStepIncrement', Real) , OptionalProperty('transition', LightingTransition) , OptionalProperty('feedbackValue', Real) , ReadableProperty('priorityArray', PriorityArray) , ReadableProperty('relinquishDefault', Real) , OptionalProperty('power', Real) , OptionalProperty('instantaneousPower', Real) , OptionalProperty('minActualValue', Real) , OptionalProperty('maxActualValue', Real) , ReadableProperty('lightingCommandDefaultPriority', Unsigned) , OptionalProperty('covIncrement', Real) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @register_object_type class LoadControlObject(Object): objectType = 'loadControl' properties = \ [ ReadableProperty('presentValue', ShedState) , OptionalProperty('stateDescription', CharacterString) , ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , WritableProperty('requestedShedLevel', ShedLevel) , WritableProperty('startTime', DateTime) , WritableProperty('shedDuration', Unsigned) , WritableProperty('dutyWindow', Unsigned) , WritableProperty('enable', Boolean) , OptionalProperty('fullDutyBaseline', Real) , ReadableProperty('expectedShedLevel', ShedLevel) , ReadableProperty('actualShedLevel', ShedLevel) , WritableProperty('shedLevels', ArrayOf(Unsigned)) , ReadableProperty('shedLevelDescriptions', ArrayOf(CharacterString)) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('timeDelay', Unsigned) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) , OptionalProperty('timeDelayNormal', Unsigned) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @register_object_type class LoopObject(Object): objectType = 'loop' properties = \ [ ReadableProperty('presentValue', Real) , ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , ReadableProperty('outOfService', Boolean) , ReadableProperty('updateInterval', Unsigned) , ReadableProperty('outputUnits', EngineeringUnits) , ReadableProperty('manipulatedVariableReference', ObjectPropertyReference) , ReadableProperty('controlledVariableReference', ObjectPropertyReference) , ReadableProperty('controlledVariableValue', Real) , ReadableProperty('controlledVariableUnits', EngineeringUnits) , ReadableProperty('setpointReference', SetpointReference) , ReadableProperty('setpoint', Real) , ReadableProperty('action', Action) , OptionalProperty('proportionalConstant', Real) , OptionalProperty('proportionalConstantUnits', EngineeringUnits) , OptionalProperty('integralConstant', Real) , OptionalProperty('integralConstantUnits', EngineeringUnits) , OptionalProperty('derivativeConstant', Real) , OptionalProperty('derivativeConstantUnits', EngineeringUnits) , OptionalProperty('bias', Real) , OptionalProperty('maximumOutput', Real) , OptionalProperty('minimumOutput', Real) , ReadableProperty('priorityForWriting', Unsigned) , OptionalProperty('covIncrement', Real) , OptionalProperty('timeDelay', Unsigned) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('errorLimit', Real) , OptionalProperty('deadband', Real) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) , OptionalProperty('timeDelayNormal', Unsigned) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @register_object_type class MultiStateInputObject(Object): objectType = 'multiStateInput' properties = \ [ ReadableProperty('presentValue', Unsigned) , OptionalProperty('deviceType', CharacterString) , ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , ReadableProperty('outOfService', Boolean) , ReadableProperty('numberOfStates', Unsigned) , OptionalProperty('stateText', ArrayOf(CharacterString)) , OptionalProperty('timeDelay', Unsigned) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('alarmValues', SequenceOf(Unsigned)) , OptionalProperty('faultValues', SequenceOf(Unsigned)) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) , OptionalProperty('timeDelayNormal', Unsigned) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @register_object_type class MultiStateOutputObject(Object): objectType = 'multiStateOutput' properties = \ [ WritableProperty('presentValue', Unsigned) , OptionalProperty('deviceType', CharacterString) , ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , ReadableProperty('outOfService', Boolean) , ReadableProperty('numberOfStates', Unsigned) , OptionalProperty('stateText', ArrayOf(CharacterString)) , ReadableProperty('priorityArray', PriorityArray) , OptionalProperty('relinquishDefault', Unsigned) , OptionalProperty('timeDelay', Unsigned) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('feedbackValue', Unsigned) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) , OptionalProperty('timeDelayNormal', Unsigned) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @register_object_type class MultiStateValueObject(Object): objectType = 'multiStateValue' properties = \ [ ReadableProperty('presentValue', Unsigned) , ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , ReadableProperty('outOfService', Boolean) , ReadableProperty('numberOfStates', Unsigned) , OptionalProperty('stateText', ArrayOf(CharacterString)) , OptionalProperty('priorityArray', PriorityArray) , OptionalProperty('relinquishDefault', Unsigned) , OptionalProperty('timeDelay', Unsigned) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('alarmValues', SequenceOf(Unsigned)) , OptionalProperty('faultValues', SequenceOf(Unsigned)) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) , OptionalProperty('timeDelayNormal', Unsigned) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @register_object_type class NetworkSecurityObject(Object): objectType = 'networkSecurity' properties = \ [ WritableProperty('baseDeviceSecurityPolicy', SecurityLevel) , WritableProperty('networkAccessSecurityPolicies', ArrayOf(NetworkSecurityPolicy)) , WritableProperty('securityTimeWindow', Unsigned) , WritableProperty('packetReorderTime', Unsigned) , ReadableProperty('distributionKeyRevision', Unsigned) , ReadableProperty('keySets', ArrayOf(SecurityKeySet)) , WritableProperty('lastKeyServer', AddressBinding) , WritableProperty('securityPDUTimeout', Unsigned) , ReadableProperty('updateKeySetTimeout', Unsigned) , ReadableProperty('supportedSecurityAlgorithms', SequenceOf(Unsigned)) , WritableProperty('doNotHide', Boolean) ] @register_object_type class NotificationClassObject(Object): objectType = 'notificationClass' properties = \ [ ReadableProperty('notificationClass', Unsigned) , ReadableProperty('priority', ArrayOf(Unsigned)) , ReadableProperty('ackRequired', EventTransitionBits) , ReadableProperty('recipientList', SequenceOf(Destination)) ] @register_object_type class NotificationForwarderObject(Object): objectType = 'notificationForwarder' properties = \ [ ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('reliability', Reliability) , ReadableProperty('outOfService', Boolean) , ReadableProperty('recipientList', SequenceOf(Destination)) , WritableProperty('subscribedRecipients', SequenceOf(EventNotificationSubscription)) , ReadableProperty('processIdentifierFilter', ProcessIdSelection) , OptionalProperty('portFilter', ArrayOf(PortPermission)) , ReadableProperty('localForwardingOnly', Boolean) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @register_object_type class OctetStringValueObject(Object): objectType = 'octetstringValue' properties = \ [ ReadableProperty('presentValue', CharacterString) , ReadableProperty('statusFlags', StatusFlags) , OptionalProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , OptionalProperty('outOfService', Boolean) , OptionalProperty('priorityArray', PriorityArray) , OptionalProperty('relinquishDefault', OctetString) ] @register_object_type class PositiveIntegerValueObject(Object): objectType = 'positiveIntegerValue' properties = \ [ ReadableProperty('presentValue', Unsigned) , ReadableProperty('statusFlags', StatusFlags) , OptionalProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , OptionalProperty('outOfService', Boolean) , ReadableProperty('units', EngineeringUnits) , OptionalProperty('priorityArray', PriorityArray) , OptionalProperty('relinquishDefault', Unsigned) , OptionalProperty('covIncrement', Unsigned) , OptionalProperty('timeDelay', Unsigned) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('highLimit', Unsigned) , OptionalProperty('lowLimit', Unsigned) , OptionalProperty('deadband', Unsigned) , OptionalProperty('limitEnable', LimitEnable) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) , OptionalProperty('timeDelayNormal', Unsigned) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) , OptionalProperty('minPresValue', Unsigned) , OptionalProperty('maxPresValue', Unsigned) , OptionalProperty('resolution', Unsigned) ] @register_object_type class ProgramObject(Object): objectType = 'program' properties = \ [ ReadableProperty('programState', ProgramState) , WritableProperty('programChange', ProgramRequest) , OptionalProperty('reasonForHalt', ProgramError) , OptionalProperty('descriptionOfHalt', CharacterString) , OptionalProperty('programLocation', CharacterString) , OptionalProperty('instanceOf', CharacterString) , ReadableProperty('statusFlags', StatusFlags) , OptionalProperty('reliability', Reliability) , ReadableProperty('outOfService', Boolean) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @register_object_type class PulseConverterObject(Object): objectType = 'pulseConverter' properties = \ [ ReadableProperty('presentValue', Real) , OptionalProperty('inputReference', ObjectPropertyReference) , ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , ReadableProperty('outOfService', Boolean) , ReadableProperty('units', EngineeringUnits) , ReadableProperty('scaleFactor', Real) , WritableProperty('adjustValue', Real) , ReadableProperty('count', Unsigned) , ReadableProperty('updateTime', DateTime) , ReadableProperty('countChangeTime', DateTime) , ReadableProperty('countBeforeChange', Unsigned) , OptionalProperty('covIncrement', Real) , OptionalProperty('covPeriod', Unsigned) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('timeDelay', Unsigned) , OptionalProperty('highLimit', Real) , OptionalProperty('lowLimit', Real) , OptionalProperty('deadband', Real) , OptionalProperty('limitEnable', LimitEnable) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) , OptionalProperty('timeDelayNormal', Unsigned) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @register_object_type class ScheduleObject(Object): objectType = 'schedule' properties = \ [ ReadableProperty('presentValue', AnyAtomic) , ReadableProperty('effectivePeriod', DateRange) , OptionalProperty('weeklySchedule', ArrayOf(DailySchedule)) , OptionalProperty('exceptionSchedule', ArrayOf(SpecialEvent)) , ReadableProperty('scheduleDefault', AnyAtomic) , ReadableProperty('listOfObjectPropertyReferences', SequenceOf(DeviceObjectPropertyReference)) , ReadableProperty('priorityForWriting', Unsigned) , ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('reliability', Reliability) , ReadableProperty('outOfService', Boolean) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('eventEnable', EventTransitionBits) , ReadableProperty('eventState', EventState) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @register_object_type class StructuredViewObject(Object): objectType = 'structuredView' properties = \ [ ReadableProperty('nodeType', NodeType) , OptionalProperty('nodeSubtype', CharacterString) , ReadableProperty('subordinateList', ArrayOf(DeviceObjectReference)) , OptionalProperty('subordinateAnnotations', ArrayOf(CharacterString)) ] @register_object_type class TimePatternValueObject(Object): objectType = 'timePatternValue' properties = \ [ ReadableProperty('presentValue', Time) , ReadableProperty('statusFlags', StatusFlags) , OptionalProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , OptionalProperty('outOfService', Boolean) , OptionalProperty('priorityArray', PriorityArray) , OptionalProperty('relinquishDefault', Time) ] @register_object_type class TimeValueObject(Object): objectType = 'timeValue' properties = \ [ ReadableProperty('presentValue', Time) , ReadableProperty('statusFlags', StatusFlags) , OptionalProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , OptionalProperty('outOfService', Boolean) , OptionalProperty('priorityArray', PriorityArray) , OptionalProperty('relinquishDefault', Time) ] @register_object_type class TrendLogObject(Object): objectType = 'trendLog' properties = \ [ ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , WritableProperty('enable', Boolean) , OptionalProperty('startTime', DateTime) , OptionalProperty('stopTime', DateTime) , OptionalProperty('logDeviceObjectProperty', DeviceObjectPropertyReference) , OptionalProperty('logInterval', Unsigned) , OptionalProperty('covResubscriptionInterval', Unsigned) , OptionalProperty('clientCovIncrement', ClientCOV) , ReadableProperty('stopWhenFull', Boolean) , ReadableProperty('bufferSize', Unsigned) , ReadableProperty('logBuffer', SequenceOf(LogRecord)) , WritableProperty('recordCount', Unsigned) , ReadableProperty('totalRecordCount', Unsigned) , ReadableProperty('loggingType', LoggingType) , OptionalProperty('alignIntervals', Boolean) , OptionalProperty('intervalOffset', Unsigned) , OptionalProperty('trigger', Boolean) , ReadableProperty('statusFlags', StatusFlags) , OptionalProperty('reliability', Reliability) , OptionalProperty('notificationThreshold', Unsigned) , OptionalProperty('recordsSinceNotification', Unsigned) , OptionalProperty('lastNotifyRecord', Unsigned) , ReadableProperty('eventState', EventState) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] @register_object_type class TrendLogMultipleObject(Object): objectType = 'trendLogMultiple' properties = \ [ ReadableProperty('statusFlags', StatusFlags) , ReadableProperty('eventState', EventState) , OptionalProperty('reliability', Reliability) , WritableProperty('enable', Boolean) , OptionalProperty('startTime', DateTime) , OptionalProperty('stopTime', DateTime) , ReadableProperty('logDeviceObjectProperty', ArrayOf(DeviceObjectPropertyReference)) , ReadableProperty('loggingType', LoggingType) , ReadableProperty('logInterval', Unsigned) , OptionalProperty('alignIntervals', Boolean) , OptionalProperty('intervalOffset', Unsigned) , OptionalProperty('trigger', Boolean) , ReadableProperty('stopWhenFull', Boolean) , ReadableProperty('bufferSize', Unsigned) , ReadableProperty('logBuffer', SequenceOf(LogMultipleRecord)) , WritableProperty('recordCount', Unsigned) , ReadableProperty('totalRecordCount', Unsigned) , OptionalProperty('notificationThreshold', Unsigned) , OptionalProperty('recordsSinceNotification', Unsigned) , OptionalProperty('lastNotifyRecord', Unsigned) , OptionalProperty('notificationClass', Unsigned) , OptionalProperty('eventEnable', EventTransitionBits) , OptionalProperty('ackedTransitions', EventTransitionBits) , OptionalProperty('notifyType', NotifyType) , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp)) , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString)) , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString)) , OptionalProperty('eventDetectionEnable', Boolean) , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference) , OptionalProperty('eventAlgorithmInhibit', Boolean) , OptionalProperty('reliabilityEvaluationInhibit', Boolean) ] PKHx3&3&bacpypes/consolecmd.py#!/usr/bin/env python """ Console Command """ import sys import types import os import gc import signal import cmd import logging from threading import Thread from .debugging import bacpypes_debugging, function_debugging, Logging, ModuleLogger from .consolelogging import ConsoleLogHandler from . import core # readline is used for history files try: import readline except ImportError: readline = None # some debugging _debug = 0 _log = ModuleLogger(globals()) # # console_interrupt # @function_debugging def console_interrupt(*args): if _debug: console_interrupt._debug("console_interrupt %r", args) sys.stderr.write("Keyboard interrupt trapped - use EOF to end\n") # # ConsoleCmd # @bacpypes_debugging class ConsoleCmd(cmd.Cmd, Thread, Logging): def __init__(self, prompt="> ", stdin=None, stdout=None): if _debug: ConsoleCmd._debug("__init__") cmd.Cmd.__init__(self, stdin=stdin, stdout=stdout) Thread.__init__(self, name="ConsoleCmd") # check to see if this is running interactive self.interactive = sys.__stdin__.isatty() # save the prompt for interactive sessions, otherwise be quiet if self.interactive: self.prompt = prompt else: self.prompt = '' # gc counters self.type2count = {} self.type2all = {} # logging handlers self.handlers = {} # set a INT signal handler, ^C will only get sent to the # main thread and there's no way to break the readline # call initiated by this thread - sigh if hasattr(signal, 'SIGINT'): signal.signal(signal.SIGINT, console_interrupt) # start the thread self.start() def run(self): if _debug: ConsoleCmd._debug("run") # run the command loop self.cmdloop() if _debug: ConsoleCmd._debug(" - done cmdloop") # tell the main thread to stop, this thread will exit core.deferred(core.stop) def onecmd(self, cmdString): if _debug: ConsoleCmd._debug('onecmd %r', cmdString) rslt = "" # let the real command run, trapping errors try: rslt = cmd.Cmd.onecmd(self, cmdString) except Exception as err: ConsoleCmd._exception("exception: %r", err) # return what the command returned return rslt #----- def do_gc(self, args): """gc - print out garbage collection information""" ### humm... instance_type = getattr(types, 'InstanceType', object) # snapshot of counts type2count = {} type2all = {} for o in gc.get_objects(): if type(o) == instance_type: type2count[o.__class__] = type2count.get(o.__class__,0) + 1 type2all[o.__class__] = type2all.get(o.__class__,0) + sys.getrefcount(o) # count the things that have changed ct = [ ( t.__module__ , t.__name__ , type2count[t] , type2count[t] - self.type2count.get(t,0) , type2all[t] - self.type2all.get(t,0) ) for t in type2count.keys() ] # ready for the next time self.type2count = type2count self.type2all = type2all fmt = "%-30s %-30s %6s %6s %6s\n" self.stdout.write(fmt % ("Module", "Type", "Count", "dCount", "dRef")) # sorted by count ct.sort(key=lambda x: x[2]) for i in range(min(10,len(ct))): m, n, c, delta1, delta2 = ct[i] self.stdout.write(fmt % (m, n, c, delta1, delta2)) self.stdout.write("\n") self.stdout.write(fmt % ("Module", "Type", "Count", "dCount", "dRef")) # sorted by module and class ct.sort() for m, n, c, delta1, delta2 in ct: if delta1 or delta2: self.stdout.write(fmt % (m, n, c, delta1, delta2)) self.stdout.write("\n") def do_bugin(self, args): """bugin [ ] - add a console logging handler to a logger""" args = args.split() if _debug: ConsoleCmd._debug("do_bugin %r", args) # get the logger name and logger if args: loggerName = args[0] if loggerName in logging.Logger.manager.loggerDict: logger = logging.getLogger(loggerName) else: logger = None else: loggerName = '__root__' logger = logging.getLogger() # add a logging handler if not logger: self.stdout.write("not a valid logger name\n") elif loggerName in self.handlers: self.stdout.write("%s already has a handler\n" % loggerName) else: handler = ConsoleLogHandler(logger) self.handlers[loggerName] = handler self.stdout.write("handler to %s added\n" % loggerName) self.stdout.write("\n") def do_bugout(self, args): """bugout [ ] - remove a console logging handler from a logger""" args = args.split() if _debug: ConsoleCmd._debug("do_bugout %r", args) # get the logger name and logger if args: loggerName = args[0] if loggerName in logging.Logger.manager.loggerDict: logger = logging.getLogger(loggerName) else: logger = None else: loggerName = '__root__' logger = logging.getLogger() # remove the logging handler if not logger: self.stdout.write("not a valid logger name\n") elif not loggerName in self.handlers: self.stdout.write("no handler for %s\n" % loggerName) else: handler = self.handlers[loggerName] del self.handlers[loggerName] # see if this (or its parent) is a module level logger if hasattr(logger, 'globs'): logger.globs['_debug'] -= 1 elif hasattr(logger.parent, 'globs'): logger.parent.globs['_debug'] -= 1 # remove it from the logger logger.removeHandler(handler) self.stdout.write("handler to %s removed\n" % loggerName) self.stdout.write("\n") def do_buggers(self, args): """buggers - list the console logging handlers""" args = args.split() if _debug: ConsoleCmd._debug("do_buggers %r", args) if not self.handlers: self.stdout.write("no handlers\n") else: self.stdout.write("handlers: ") self.stdout.write(', '.join(loggerName or '__root__' for loggerName in self.handlers)) self.stdout.write("\n") loggers = logging.Logger.manager.loggerDict.keys() for loggerName in sorted(loggers): if args and (not args[0] in loggerName): continue if loggerName in self.handlers: self.stdout.write("* %s\n" % loggerName) else: self.stdout.write(" %s\n" % loggerName) self.stdout.write("\n") #----- def do_exit(self, args): """Exits from the console.""" if _debug: ConsoleCmd._debug("do_exit %r", args) return -1 def do_EOF(self, args): """Exit on system end of file character""" if _debug: ConsoleCmd._debug("do_EOF %r", args) return self.do_exit(args) def do_shell(self, args): """Pass command to a system shell when line begins with '!'""" if _debug: ConsoleCmd._debug("do_shell %r", args) os.system(args) def do_help(self, args): """Get help on commands 'help' or '?' with no arguments prints a list of commands for which help is available 'help ' or '? ' gives help on """ if _debug: ConsoleCmd._debug("do_help %r", args) # the only reason to define this method is for the help text in the doc string cmd.Cmd.do_help(self, args) def preloop(self): """Initialization before prompting user for commands. Despite the claims in the Cmd documentaion, Cmd.preloop() is not a stub. """ cmd.Cmd.preloop(self) ## sets up command completion try: if readline: readline.read_history_file(sys.argv[0] + ".history") except Exception as err: if not isinstance(err, IOError): self.stdout.write("history error: %s\n" % err) def postloop(self): """Take care of any unfinished business. Despite the claims in the Cmd documentaion, Cmd.postloop() is not a stub. """ try: if readline: readline.write_history_file(sys.argv[0] + ".history") except Exception as err: if not isinstance(err, IOError): self.stdout.write("history error: %s\n" % err) # clean up command completion cmd.Cmd.postloop(self) if self.interactive: self.stdout.write("Exiting...\n") # tell the core we have stopped core.deferred(core.stop) def precmd(self, line): """ This method is called after the line has been input but before it has been interpreted. If you want to modify the input line before execution (for example, variable substitution) do it here. """ return line.strip() def postcmd(self, stop, line): """If you want to stop the console, return something that evaluates to true. If you want to do some post command processing, do it here. """ return stop def emptyline(self): """Do nothing on empty input line""" pass PK1H== self.segmentCount: raise RuntimeError("invalid segment number {0}, APDU has {1} segments".format(indx, self.segmentCount)) if self.segmentAPDU.apduType == ConfirmedRequestPDU.pduType: if _debug: SSM._debug(" - confirmed request context") segAPDU = ConfirmedRequestPDU(self.segmentAPDU.apduService) segAPDU.apduMaxSegs = self.maxSegmentsAccepted segAPDU.apduMaxResp = self.ssmSAP.maxApduLengthAccepted segAPDU.apduInvokeID = self.invokeID; # segmented response accepted? segAPDU.apduSA = ((self.ssmSAP.segmentationSupported == 'segmentedBoth') \ or (self.ssmSAP.segmentationSupported == 'segmentedReceive')) if _debug: SSM._debug(" - segmented response accepted: %r", segAPDU.apduSA) SSM._debug(" - self.ssmSAP.segmentationSupported: %r", self.ssmSAP.segmentationSupported) elif self.segmentAPDU.apduType == ComplexAckPDU.pduType: if _debug: SSM._debug(" - complex ack context") segAPDU = ComplexAckPDU(self.segmentAPDU.apduService, self.segmentAPDU.apduInvokeID) else: raise RuntimeError("invalid APDU type for segmentation context") # maintain the the user data reference segAPDU.pduUserData = self.segmentAPDU.pduUserData # make sure the destination is set segAPDU.pduDestination = self.remoteDevice.address # segmented message? if (self.segmentCount != 1): segAPDU.apduSeg = True segAPDU.apduMor = (indx < (self.segmentCount - 1)) # more follows segAPDU.apduSeq = indx % 256 # sequence number segAPDU.apduWin = self.proposedWindowSize # window size else: segAPDU.apduSeg = False segAPDU.apduMor = False # add the content offset = indx * self.segmentSize segAPDU.put_data( self.segmentAPDU.pduData[offset:offset+self.segmentSize] ) # success return segAPDU def append_segment(self, apdu): """This function appends the apdu content to the end of the current APDU being built. The segmentAPDU is the context.""" if _debug: SSM._debug("append_segment %r", apdu) # check for no context if not self.segmentAPDU: raise RuntimeError("no segmentation context established") # append the data self.segmentAPDU.put_data(apdu.pduData) def in_window(self, seqA, seqB): if _debug: SSM._debug("in_window %r %r", seqA, seqB) rslt = ((seqA - seqB + 256) % 256) < self.actualWindowSize if _debug: SSM._debug(" - rslt: %r", rslt) return rslt def FillWindow(self, seqNum): """This function sends all of the packets necessary to fill out the segmentation window.""" if _debug: SSM._debug("FillWindow %r", seqNum) for ix in range(self.actualWindowSize): apdu = self.get_segment(seqNum + ix) # send the message self.ssmSAP.request(apdu) # check for no more follows if not apdu.apduMor: self.sentAllSegments = True break # # ClientSSM - Client Segmentation State Machine # @bacpypes_debugging class ClientSSM(SSM): def __init__(self, sap): SSM.__init__(self, sap) # initialize the retry count self.retryCount = 0 def set_state(self, newState, timer=0): """This function is called when the client wants to change state.""" if _debug: ClientSSM._debug("set_state %r (%s) timer=%r", newState, SSM.transactionLabels[newState], timer) # pass the change down SSM.set_state(self, newState, timer) # completed or aborted, remove tracking if (newState == COMPLETED) or (newState == ABORTED): self.ssmSAP.clientTransactions.remove(self) def request(self, apdu): """This function is called by client transaction functions when it wants to send a message to the device.""" if _debug: ClientSSM._debug("request %r", apdu) # make sure it has a good source and destination apdu.pduSource = None apdu.pduDestination = self.remoteDevice.address # send it via the device self.ssmSAP.request(apdu) def indication(self, apdu): """This function is called after the device has bound a new transaction and wants to start the process rolling.""" if _debug: ClientSSM._debug("indication %r", apdu) # make sure we're getting confirmed requests if (apdu.apduType != ConfirmedRequestPDU.pduType): raise RuntimeError("invalid APDU (1)") # save the request and set the segmentation context self.set_segmentation_context(apdu) # save the maximum number of segments acceptable in the reply if apdu.apduMaxSegs is not None: # this request overrides the default self.maxSegmentsAccepted = apdu.apduMaxSegs else: # use the default in the device definition self.maxSegmentsAccepted = self.ssmSAP.maxSegmentsAccepted # save the invoke ID self.invokeID = apdu.apduInvokeID if _debug: ClientSSM._debug(" - invoke ID: %r", self.invokeID) # get information about the device self.remoteDevice = self.ssmSAP.get_device_info(apdu.pduDestination) # the segment size is the minimum of what I want to transmit and # what the device can receive self.segmentSize = min(self.ssmSAP.maxApduLengthAccepted, self.remoteDevice.maxApduLengthAccepted) if _debug: ClientSSM._debug(" - segment size: %r", self.segmentSize) # compute the segment count ### minus the header? if not apdu.pduData: # always at least one segment self.segmentCount = 1 else: # split into chunks, maybe need one more self.segmentCount, more = divmod(len(apdu.pduData), self.segmentSize) if more: self.segmentCount += 1 if _debug: ClientSSM._debug(" - segment count: %r", self.segmentCount) # make sure we support segmented transmit if we need to if self.segmentCount > 1: if (self.ssmSAP.segmentationSupported != 'segmentedTransmit') and (self.ssmSAP.segmentationSupported != 'segmentedBoth'): if _debug: ClientSSM._debug(" - local device can't send segmented messages") abort = self.abort(AbortReason.segmentationNotSupported) self.response(abort) return if (self.remoteDevice.segmentationSupported != 'segmentedReceive') and (self.remoteDevice.segmentationSupported != 'segmentedBoth'): if _debug: ClientSSM._debug(" - remote device can't receive segmented messages") abort = self.abort(AbortReason.segmentationNotSupported) self.response(abort) return # send out the first segment (or the whole thing) if self.segmentCount == 1: # SendConfirmedUnsegmented self.sentAllSegments = True self.retryCount = 0 self.set_state(AWAIT_CONFIRMATION, self.ssmSAP.retryTimeout) else: # SendConfirmedSegmented self.sentAllSegments = False self.retryCount = 0 self.segmentRetryCount = 0 self.initialSequenceNumber = 0 self.proposedWindowSize = self.ssmSAP.maxSegmentsAccepted self.actualWindowSize = 1 self.set_state(SEGMENTED_REQUEST, self.ssmSAP.segmentTimeout) # deliver to the device self.request(self.get_segment(0)) def response(self, apdu): """This function is called by client transaction functions when they want to send a message to the application.""" if _debug: ClientSSM._debug("response %r", apdu) # make sure it has a good source and destination apdu.pduSource = self.remoteDevice.address apdu.pduDestination = None # send it to the application self.ssmSAP.sap_response(apdu) def confirmation(self, apdu): """This function is called by the device for all upstream messages related to the transaction.""" if _debug: ClientSSM._debug("confirmation %r", apdu) if self.state == SEGMENTED_REQUEST: self.segmented_request(apdu) elif self.state == AWAIT_CONFIRMATION: self.await_confirmation(apdu) elif self.state == SEGMENTED_CONFIRMATION: self.segmented_confirmation(apdu) else: raise RuntimeError("invalid state") def process_task(self): """This function is called when something has taken too long.""" if _debug: ClientSSM._debug("process_task") if self.state == SEGMENTED_REQUEST: self.segmented_request_timeout() elif self.state == AWAIT_CONFIRMATION: self.await_confirmation_timeout() elif self.state == SEGMENTED_CONFIRMATION: self.segmented_confirmation_timeout() elif self.state == COMPLETED: pass elif self.state == ABORTED: pass else: e = RuntimeError("invalid state") ClientSSM._exception("exception: %r", e) raise e def abort(self, reason): """This function is called when the transaction should be aborted.""" if _debug: ClientSSM._debug("abort %r", reason) # change the state to aborted self.set_state(ABORTED) # build an abort PDU to return abort_pdu = AbortPDU(False, self.invokeID, reason) # return it return abort_pdu def segmented_request(self, apdu): """This function is called when the client is sending a segmented request and receives an apdu.""" if _debug: ClientSSM._debug("segmented_request %r", apdu) # client is ready for the next segment if apdu.apduType == SegmentAckPDU.pduType: if _debug: ClientSSM._debug(" - segment ack") # duplicate ack received? if not self.in_window(apdu.apduSeq, self.initialSequenceNumber): if _debug: ClientSSM._debug(" - not in window") self.restart_timer(self.ssmSAP.segmentTimeout) # final ack received? elif self.sentAllSegments: if _debug: ClientSSM._debug(" - all done sending request") self.set_state(AWAIT_CONFIRMATION, self.ssmSAP.retryTimeout) # more segments to send else: if _debug: ClientSSM._debug(" - more segments to send") self.initialSequenceNumber = (apdu.apduSeq + 1) % 256 self.actualWindowSize = apdu.apduWin self.segmentRetryCount = 0 self.FillWindow(self.initialSequenceNumber) self.restart_timer(self.ssmSAP.segmentTimeout) # simple ack elif (apdu.apduType == SimpleAckPDU.pduType): if _debug: ClientSSM._debug(" - simple ack") if not self.sentAllSegments: abort = self.abort(AbortReason.invalidApduInThisState) self.request(abort) # send it to the device self.response(abort) # send it to the application else: self.set_state(COMPLETED) self.response(apdu) elif (apdu.apduType == ComplexAckPDU.pduType): if _debug: ClientSSM._debug(" - complex ack") if not self.sentAllSegments: abort = self.abort(AbortReason.invalidApduInThisState) self.request(abort) # send it to the device self.response(abort) # send it to the application elif not apdu.apduSeg: self.set_state(COMPLETED) self.response(apdu) else: # set the segmented response context self.set_segmentation_context(apdu) self.actualWindowSize = min(apdu.apduWin, self.ssmSAP.maxSegmentsAccepted) self.lastSequenceNumber = 0 self.initialSequenceNumber = 0 self.set_state(SEGMENTED_CONFIRMATION, self.ssmSAP.segmentTimeout) # some kind of problem elif (apdu.apduType == ErrorPDU.pduType) or (apdu.apduType == RejectPDU.pduType) or (apdu.apduType == AbortPDU.pduType): if _debug: ClientSSM._debug(" - error/reject/abort") self.set_state(COMPLETED) self.response = apdu self.response(apdu) else: raise RuntimeError("invalid APDU (2)") def segmented_request_timeout(self): if _debug: ClientSSM._debug("segmented_request_timeout") # try again if self.segmentRetryCount < self.ssmSAP.retryCount: if _debug: ClientSSM._debug(" - retry segmented request") self.segmentRetryCount += 1 self.start_timer(self.ssmSAP.segmentTimeout) self.FillWindow(self.initialSequenceNumber) else: if _debug: ClientSSM._debug(" - abort, no response from the device") abort = self.abort(AbortReason.noResponse) self.response(abort) def await_confirmation(self, apdu): if _debug: ClientSSM._debug("await_confirmation %r", apdu) if (apdu.apduType == AbortPDU.pduType): if _debug: ClientSSM._debug(" - server aborted") self.set_state(ABORTED) self.response(apdu) elif (apdu.apduType == SimpleAckPDU.pduType) or (apdu.apduType == ErrorPDU.pduType) or (apdu.apduType == RejectPDU.pduType): if _debug: ClientSSM._debug(" - simple ack, error, or reject") self.set_state(COMPLETED) self.response(apdu) elif (apdu.apduType == ComplexAckPDU.pduType): if _debug: ClientSSM._debug(" - complex ack") # if the response is not segmented, we're done if not apdu.apduSeg: if _debug: ClientSSM._debug(" - unsegmented") self.set_state(COMPLETED) self.response(apdu) elif (self.ssmSAP.segmentationSupported != 'segmentedReceive') and (self.ssmSAP.segmentationSupported != 'segmentedBoth'): if _debug: ClientSSM._debug(" - local device can't receive segmented messages") abort = self.abort(AbortReason.segmentationNotSupported) self.response(abort) elif apdu.apduSeq == 0: if _debug: ClientSSM._debug(" - segmented response") # set the segmented response context self.set_segmentation_context(apdu) self.actualWindowSize = min(apdu.apduWin, self.ssmSAP.maxSegmentsAccepted) self.lastSequenceNumber = 0 self.initialSequenceNumber = 0 self.set_state(SEGMENTED_CONFIRMATION, self.ssmSAP.segmentTimeout) # send back a segment ack segack = SegmentAckPDU( 0, 0, self.invokeID, self.initialSequenceNumber, self.actualWindowSize ) self.request(segack) else: if _debug: ClientSSM._debug(" - invalid APDU in this state") abort = self.abort(AbortReason.invalidApduInThisState) self.request(abort) # send it to the device self.response(abort) # send it to the application elif (apdu.apduType == SegmentAckPDU.pduType): if _debug: ClientSSM._debug(" - segment ack(!?)") self.restart_timer(self.ssmSAP.segmentTimeout) else: raise RuntimeError("invalid APDU (3)") def await_confirmation_timeout(self): if _debug: ClientSSM._debug("await_confirmation_timeout") self.retryCount += 1 if self.retryCount < self.ssmSAP.retryCount: if _debug: ClientSSM._debug(" - no response, try again (%d < %d)", self.retryCount, self.ssmSAP.retryCount) # save the retry count, indication acts like the request is coming # from the application so the retryCount gets re-initialized. saveCount = self.retryCount self.indication(self.segmentAPDU) self.retryCount = saveCount else: if _debug: ClientSSM._debug(" - retry count exceeded") abort = self.abort(AbortReason.noResponse) self.response(abort) def segmented_confirmation(self, apdu): if _debug: ClientSSM._debug("segmented_confirmation %r", apdu) # the only messages we should be getting are complex acks if (apdu.apduType != ComplexAckPDU.pduType): if _debug: ClientSSM._debug(" - complex ack required") abort = self.abort(AbortReason.invalidApduInThisState) self.request(abort) # send it to the device self.response(abort) # send it to the application return # it must be segmented if not apdu.apduSeg: if _debug: ClientSSM._debug(" - must be segmented") abort = self.abort(AbortReason.invalidApduInThisState) self.request(abort) # send it to the device self.response(abort) # send it to the application return # proper segment number if apdu.apduSeq != (self.lastSequenceNumber + 1) % 256: if _debug: ClientSSM._debug(" - segment %s received out of order, should be %s", apdu.apduSeq, (self.lastSequenceNumber + 1) % 256) # segment received out of order self.restart_timer(self.ssmSAP.segmentTimeout) segack = SegmentAckPDU( 1, 0, self.invokeID, self.lastSequenceNumber, self.actualWindowSize ) self.request(segack) return # add the data self.append_segment(apdu) # update the sequence number self.lastSequenceNumber = (self.lastSequenceNumber + 1) % 256 # last segment received if not apdu.apduMor: if _debug: ClientSSM._debug(" - no more follows") # send a final ack segack = SegmentAckPDU( 0, 0, self.invokeID, self.lastSequenceNumber, self.actualWindowSize ) self.request(segack) self.set_state(COMPLETED) self.response(self.segmentAPDU) elif apdu.apduSeq == ((self.initialSequenceNumber + self.actualWindowSize) % 256): if _debug: ClientSSM._debug(" - last segment in the group") self.initialSequenceNumber = self.lastSequenceNumber self.restart_timer(self.ssmSAP.segmentTimeout) segack = SegmentAckPDU( 0, 0, self.invokeID, self.lastSequenceNumber, self.actualWindowSize ) self.request(segack) else: # wait for more segments if _debug: ClientSSM._debug(" - wait for more segments") self.restart_timer(self.ssmSAP.segmentTimeout) def segmented_confirmation_timeout(self): if _debug: ClientSSM._debug("segmented_confirmation_timeout") abort = self.abort(AbortReason.noResponse) self.response(abort) # # ServerSSM - Server Segmentation State Machine # @bacpypes_debugging class ServerSSM(SSM): def __init__(self, sap): SSM.__init__(self, sap) def set_state(self, newState, timer=0): """This function is called when the client wants to change state.""" if _debug: ServerSSM._debug("set_state %r (%s) timer=%r", newState, SSM.transactionLabels[newState], timer) # pass the change down SSM.set_state(self, newState, timer) # completed or aborted, remove tracking if (newState == COMPLETED) or (newState == ABORTED): self.ssmSAP.serverTransactions.remove(self) def request(self, apdu): """This function is called by transaction functions to send to the application.""" if _debug: ServerSSM._debug("request %r", apdu) # make sure it has a good source and destination apdu.pduSource = self.remoteDevice.address apdu.pduDestination = None # send it via the device self.ssmSAP.sap_request(apdu) def indication(self, apdu): """This function is called for each downstream packet related to the transaction.""" if _debug: ServerSSM._debug("indication %r", apdu) if self.state == IDLE: self.Idle(apdu) elif self.state == SEGMENTED_REQUEST: self.segmented_request(apdu) elif self.state == AWAIT_RESPONSE: self.await_response(apdu) elif self.state == SEGMENTED_RESPONSE: self.segmented_response(apdu) else: if _debug: ServerSSM._debug(" - invalid state") def response(self, apdu): """This function is called by transaction functions when they want to send a message to the device.""" if _debug: ServerSSM._debug("response %r", apdu) # make sure it has a good source and destination apdu.pduSource = None apdu.pduDestination = self.remoteDevice.address # send it via the device self.ssmSAP.request(apdu) def confirmation(self, apdu): """This function is called when the application has provided a response and needs it to be sent to the client.""" if _debug: ServerSSM._debug("confirmation %r", apdu) if (apdu.apduType == AbortPDU.pduType): if _debug: ServerSSM._debug(" - abort") self.set_state(ABORTED) # send the response to the device self.response(apdu) return if self.state != AWAIT_RESPONSE: if _debug: ServerSSM._debug(" - warning: not expecting a response") # simple response if (apdu.apduType == SimpleAckPDU.pduType) or (apdu.apduType == ErrorPDU.pduType) or (apdu.apduType == RejectPDU.pduType): if _debug: ServerSSM._debug(" - simple ack, error, or reject") # transaction completed self.set_state(COMPLETED) # send the response to the device self.response(apdu) return if (apdu.apduType == ComplexAckPDU.pduType): if _debug: ServerSSM._debug(" - complex ack") # save the response and set the segmentation context self.set_segmentation_context(apdu) # the segment size is the minimum of what I want to transmit and # what the device can receive self.segmentSize = min(self.ssmSAP.maxApduLengthAccepted, self.remoteDevice.maxApduLengthAccepted) if _debug: ServerSSM._debug(" - segment size: %r", self.segmentSize) # compute the segment count ### minus the header? if not apdu.pduData: # always at least one segment self.segmentCount = 1 else: # split into chunks, maybe need one more self.segmentCount, more = divmod(len(apdu.pduData), self.segmentSize) if more: self.segmentCount += 1 if _debug: ServerSSM._debug(" - segment count: %r", self.segmentCount) # make sure we support segmented transmit if we need to if self.segmentCount > 1: if _debug: ServerSSM._debug(" - segmentation required, %d segemnts", self.segmentCount) if (self.ssmSAP.segmentationSupported != 'segmentedTransmit') and (self.ssmSAP.segmentationSupported != 'segmentedBoth'): abort = self.abort(AbortReason.segmentationNotSupported) self.request(abort) return if (self.remoteDevice.segmentationSupported != 'segmentedReceive') and (self.remoteDevice.segmentationSupported != 'segmentedBoth'): abort = self.abort(AbortReason.segmentationNotSupported) self.request(abort) return ### check to make sure the client can receive that many ### look at apduMaxSegs # initialize the state self.segmentRetryCount = 0 self.initialSequenceNumber = 0 self.proposedWindowSize = self.ssmSAP.maxSegmentsAccepted self.actualWindowSize = 1 # send out the first segment (or the whole thing) if self.segmentCount == 1: self.response(apdu) self.set_state(COMPLETED) else: self.response(self.get_segment(0)) self.set_state(SEGMENTED_RESPONSE, self.ssmSAP.segmentTimeout) else: raise RuntimeError("invalid APDU (4)") def process_task(self): """This function is called when the client has failed to send all of the segments of a segmented request, the application has taken too long to complete the request, or the client failed to ack the segments of a segmented response.""" if _debug: ServerSSM._debug("process_task") if self.state == SEGMENTED_REQUEST: self.segmented_request_timeout() elif self.state == AWAIT_RESPONSE: self.await_response_timeout() elif self.state == SEGMENTED_RESPONSE: self.segmented_response_timeout() elif self.state == COMPLETED: pass elif self.state == ABORTED: pass else: if _debug: ServerSSM._debug("invalid state") raise RuntimeError("invalid state") def abort(self, reason): """This function is called when the application would like to abort the transaction. There is no notification back to the application.""" if _debug: ServerSSM._debug("abort %r", reason) # change the state to aborted self.set_state(ABORTED) # return an abort APDU return AbortPDU(True, self.invokeID, reason) def Idle(self, apdu): if _debug: ServerSSM._debug("Idle %r", apdu) # make sure we're getting confirmed requests if not isinstance(apdu, ConfirmedRequestPDU): raise RuntimeError("invalid APDU (5)") # save the invoke ID self.invokeID = apdu.apduInvokeID if _debug: ServerSSM._debug(" - invoke ID: %r", self.invokeID) # get information about the device self.remoteDevice = self.ssmSAP.get_device_info(apdu.pduSource) # save the number of segments the client is willing to accept in the ack self.maxSegmentsAccepted = apdu.apduMaxSegs # unsegmented request if not apdu.apduSeg: self.set_state(AWAIT_RESPONSE, self.ssmSAP.applicationTimeout) self.request(apdu) return # make sure we support segmented requests if (self.ssmSAP.segmentationSupported != 'segmentedReceive') and (self.ssmSAP.segmentationSupported != 'segmentedBoth'): abort = self.abort(AbortReason.segmentationNotSupported) self.response(abort) return # save the request and set the segmentation context self.set_segmentation_context(apdu) # the window size is the minimum of what I'm willing to receive and # what the device has said it would like to send self.actualWindowSize = min(apdu.apduWin, self.ssmSAP.maxSegmentsAccepted) # initialize the state self.lastSequenceNumber = 0 self.initialSequenceNumber = 0 self.set_state(SEGMENTED_REQUEST, self.ssmSAP.segmentTimeout) # send back a segment ack segack = SegmentAckPDU( 0, 1, self.invokeID, self.initialSequenceNumber, self.actualWindowSize ) if _debug: ServerSSM._debug(" - segAck: %r", segack) self.response(segack) def segmented_request(self, apdu): if _debug: ServerSSM._debug("segmented_request %r", apdu) # some kind of problem if (apdu.apduType == AbortPDU.pduType): self.set_state(COMPLETED) self.response(apdu) return # the only messages we should be getting are confirmed requests elif (apdu.apduType != ConfirmedRequestPDU.pduType): abort = self.abort(AbortReason.invalidApduInThisState) self.request(abort) # send it to the device self.response(abort) # send it to the application return # it must be segmented elif not apdu.apduSeg: abort = self.abort(AbortReason.invalidApduInThisState) self.request(abort) # send it to the application self.response(abort) # send it to the device return # proper segment number if apdu.apduSeq != (self.lastSequenceNumber + 1) % 256: if _debug: ServerSSM._debug(" - segment %d received out of order, should be %d", apdu.apduSeq, (self.lastSequenceNumber + 1) % 256) # segment received out of order self.restart_timer(self.ssmSAP.segmentTimeout) # send back a segment ack segack = SegmentAckPDU( 1, 1, self.invokeID, self.initialSequenceNumber, self.actualWindowSize ) self.response(segack) return # add the data self.append_segment(apdu) # update the sequence number self.lastSequenceNumber = (self.lastSequenceNumber + 1) % 256 # last segment? if not apdu.apduMor: if _debug: ServerSSM._debug(" - no more follows") # send back a final segment ack segack = SegmentAckPDU( 0, 1, self.invokeID, self.lastSequenceNumber, self.actualWindowSize ) self.response(segack) # forward the whole thing to the application self.set_state(AWAIT_RESPONSE, self.ssmSAP.applicationTimeout) self.request(self.segmentAPDU) elif apdu.apduSeq == ((self.initialSequenceNumber + self.actualWindowSize) % 256): if _debug: ServerSSM._debug(" - last segment in the group") self.initialSequenceNumber = self.lastSequenceNumber self.restart_timer(self.ssmSAP.segmentTimeout) # send back a segment ack segack = SegmentAckPDU( 0, 1, self.invokeID, self.initialSequenceNumber, self.actualWindowSize ) self.response(segack) else: # wait for more segments if _debug: ServerSSM._debug(" - wait for more segments") self.restart_timer(self.ssmSAP.segmentTimeout) def segmented_request_timeout(self): if _debug: ServerSSM._debug("segmented_request_timeout") # give up self.set_state(ABORTED) def await_response(self, apdu): if _debug: ServerSSM._debug("await_response %r", apdu) if isinstance(apdu, ConfirmedRequestPDU): if _debug: ServerSSM._debug(" - client is trying this request again") elif isinstance(apdu, AbortPDU): if _debug: ServerSSM._debug(" - client aborting this request") # forward abort to the application self.set_state(ABORTED) self.request(apdu) else: raise RuntimeError("invalid APDU (6)") def await_response_timeout(self): """This function is called when the application has taken too long to respond to a clients request. The client has probably long since given up.""" if _debug: ServerSSM._debug("await_response_timeout") abort = self.abort(AbortReason.serverTimeout) self.request(abort) def segmented_response(self, apdu): if _debug: ServerSSM._debug("segmented_response %r", apdu) # client is ready for the next segment if (apdu.apduType == SegmentAckPDU.pduType): if _debug: ServerSSM._debug(" - segment ack") # duplicate ack received? if not self.in_window(apdu.apduSeq, self.initialSequenceNumber): if _debug: ServerSSM._debug(" - not in window") self.restart_timer(self.ssmSAP.segmentTimeout) # final ack received? elif self.sentAllSegments: if _debug: ServerSSM._debug(" - all done sending response") self.set_state(COMPLETED) else: if _debug: ServerSSM._debug(" - more segments to send") self.initialSequenceNumber = (apdu.apduSeq + 1) % 256 self.actualWindowSize = apdu.apduWin self.segmentRetryCount = 0 self.FillWindow(self.initialSequenceNumber) self.restart_timer(self.ssmSAP.segmentTimeout) # some kind of problem elif (apdu.apduType == AbortPDU.pduType): self.set_state(COMPLETED) self.response(apdu) else: raise RuntimeError("invalid APDU (7)") def segmented_response_timeout(self): if _debug: ServerSSM._debug("segmented_response_timeout") # try again if self.segmentRetryCount < self.ssmSAP.retryCount: self.segmentRetryCount += 1 self.start_timer(self.ssmSAP.segmentTimeout) self.FillWindow(self.initialSequenceNumber) else: # give up self.set_state(ABORTED) # # StateMachineAccessPoint # @bacpypes_debugging class StateMachineAccessPoint(DeviceInfo, Client, ServiceAccessPoint): def __init__(self, device, sap=None, cid=None): if _debug: StateMachineAccessPoint._debug("__init__ %r sap=%r cid=%r", device, sap, cid) # basic initialization DeviceInfo.__init__(self) Client.__init__(self, cid) ServiceAccessPoint.__init__(self, sap) # device information from the device object self.segmentationSupported = device.segmentationSupported # normally no segmentation self.segmentTimeout = device.apduSegmentTimeout # how long to wait for a segAck self.maxApduLengthAccepted = device.maxApduLengthAccepted # how big to divide up apdu's self.maxSegmentsAccepted = device.maxSegmentsAccepted # limit on how many segments to recieve # client settings self.clientTransactions = [] self.retryCount = device.numberOfApduRetries # how many times to repeat the request self.retryTimeout = device.apduTimeout # how long between retrying the request self.nextInvokeID = 1 # server settings self.serverTransactions = [] self.applicationTimeout = device.apduTimeout # how long the application has to respond def get_next_invoke_id(self, addr): """Called by clients to get an unused invoke ID.""" if _debug: StateMachineAccessPoint._debug("get_next_invoke_id") initialID = self.nextInvokeID while 1: invokeID = self.nextInvokeID self.nextInvokeID = (self.nextInvokeID + 1) % 256 # see if we've checked for them all if initialID == self.nextInvokeID: raise RuntimeError("no available invoke ID") for tr in self.clientTransactions: if (invokeID == tr.invokeID) and (addr == tr.remoteDevice.address): break else: break return invokeID def get_device_info(self, addr): """get the segmentation supported and max APDU length accepted for a device.""" if _debug: StateMachineAccessPoint._debug("get_device_info %r", addr) # return a generic info object return DeviceInfo(addr) def confirmation(self, pdu): """Packets coming up the stack are APDU's.""" if _debug: StateMachineAccessPoint._debug("confirmation %r", pdu) # make a more focused interpretation atype = apdu_types.get(pdu.apduType) if not atype: StateMachineAccessPoint._warning(" - unknown apduType: %r", pdu.apduType) return # decode it apdu = atype() apdu.decode(pdu) if _debug: StateMachineAccessPoint._debug(" - apdu: %r", apdu) if isinstance(apdu, ConfirmedRequestPDU): # find duplicates of this request for tr in self.serverTransactions: if (apdu.pduSource == tr.remoteDevice.address) and (apdu.apduInvokeID == tr.invokeID): break else: # build a server transaction tr = ServerSSM(self) # add it to our transactions to track it self.serverTransactions.append(tr) # let it run with the apdu tr.indication(apdu) elif isinstance(apdu, UnconfirmedRequestPDU): # deliver directly to the application self.sap_request(apdu) elif isinstance(apdu, SimpleAckPDU) \ or isinstance(apdu, ComplexAckPDU) \ or isinstance(apdu, ErrorPDU) \ or isinstance(apdu, RejectPDU): # find the client transaction this is acking for tr in self.clientTransactions: if (apdu.apduInvokeID == tr.invokeID) and (apdu.pduSource == tr.remoteDevice.address): break else: return # send the packet on to the transaction tr.confirmation(apdu) elif isinstance(apdu, AbortPDU): # find the transaction being aborted if apdu.apduSrv: for tr in self.clientTransactions: if (apdu.apduInvokeID == tr.invokeID) and (apdu.pduSource == tr.remoteDevice.address): break else: return # send the packet on to the transaction tr.confirmation(apdu) else: for tr in self.serverTransactions: if (apdu.apduInvokeID == tr.invokeID) and (apdu.pduSource == tr.remoteDevice.address): break else: return # send the packet on to the transaction tr.indication(apdu) elif isinstance(apdu, SegmentAckPDU): # find the transaction being aborted if apdu.apduSrv: for tr in self.clientTransactions: if (apdu.apduInvokeID == tr.invokeID) and (apdu.pduSource == tr.remoteDevice.address): break else: return # send the packet on to the transaction tr.confirmation(apdu) else: for tr in self.serverTransactions: if (apdu.apduInvokeID == tr.invokeID) and (apdu.pduSource == tr.remoteDevice.address): break else: return # send the packet on to the transaction tr.indication(apdu) else: raise RuntimeError("invalid APDU (8)") def sap_indication(self, apdu): """This function is called when the application is requesting a new transaction as a client.""" if _debug: StateMachineAccessPoint._debug("sap_indication %r", apdu) if isinstance(apdu, UnconfirmedRequestPDU): # deliver to the device self.request(apdu) elif isinstance(apdu, ConfirmedRequestPDU): # make sure it has an invoke ID if apdu.apduInvokeID is None: apdu.apduInvokeID = self.get_next_invoke_id(apdu.pduDestination) else: # verify the invoke ID isn't already being used for tr in self.clientTransactions: if (apdu.apduInvokeID == tr.invokeID) and (apdu.pduDestination == tr.remoteDevice.address): raise RuntimeError("invoke ID in use") # warning for bogus requests if (apdu.pduDestination.addrType != Address.localStationAddr) and (apdu.pduDestination.addrType != Address.remoteStationAddr): StateMachineAccessPoint._warning("%s is not a local or remote station", apdu.pduDestination) # create a client transaction state machine tr = ClientSSM(self) # add it to our transactions to track it self.clientTransactions.append(tr) # let it run tr.indication(apdu) else: raise RuntimeError("invalid APDU (9)") def sap_confirmation(self, apdu): """This function is called when the application is responding to a request, the apdu may be a simple ack, complex ack, error, reject or abort.""" if _debug: StateMachineAccessPoint._debug("sap_confirmation %r", apdu) if isinstance(apdu, SimpleAckPDU) \ or isinstance(apdu, ComplexAckPDU) \ or isinstance(apdu, ErrorPDU) \ or isinstance(apdu, RejectPDU) \ or isinstance(apdu, AbortPDU): # find the appropriate server transaction for tr in self.serverTransactions: if (apdu.apduInvokeID == tr.invokeID) and (apdu.pduDestination == tr.remoteDevice.address): break else: return # pass control to the transaction tr.confirmation(apdu) else: raise RuntimeError("invalid APDU (10)") # # ApplicationServiceAccessPoint # @bacpypes_debugging class ApplicationServiceAccessPoint(ApplicationServiceElement, ServiceAccessPoint): def __init__(self, aseID=None, sapID=None): if _debug: ApplicationServiceAccessPoint._debug("__init__ aseID=%r sapID=%r", aseID, sapID) ApplicationServiceElement.__init__(self, aseID) ServiceAccessPoint.__init__(self, sapID) def indication(self, apdu): if _debug: ApplicationServiceAccessPoint._debug("indication %r", apdu) if isinstance(apdu, ConfirmedRequestPDU): atype = confirmed_request_types.get(apdu.apduService) if not atype: if _debug: ApplicationServiceAccessPoint._debug(" - no confirmed request decoder") return try: xpdu = atype() xpdu.decode(apdu) except Exception as err: ApplicationServiceAccessPoint._exception("confirmed request decoding error: %r", err) return elif isinstance(apdu, UnconfirmedRequestPDU): atype = unconfirmed_request_types.get(apdu.apduService) if not atype: if _debug: ApplicationServiceAccessPoint._debug(" - no unconfirmed request decoder") return try: xpdu = atype() xpdu.decode(apdu) except Exception as err: ApplicationServiceAccessPoint._exception("unconfirmed request decoding error: %r", err) return else: if _debug: ApplicationServiceAccessPoint._debug(" - unknown PDU type?!") return if _debug: ApplicationServiceAccessPoint._debug(" - xpdu: %r", xpdu) # forward the decoded packet self.sap_request(xpdu) def sap_indication(self, apdu): if _debug: ApplicationServiceAccessPoint._debug("sap_indication %r", apdu) if isinstance(apdu, ConfirmedRequestPDU): try: xpdu = ConfirmedRequestPDU() apdu.encode(xpdu) apdu._xpdu = xpdu except Exception as err: ApplicationServiceAccessPoint._exception("confirmed request encoding error: %r", err) return elif isinstance(apdu, UnconfirmedRequestPDU): try: xpdu = UnconfirmedRequestPDU() apdu.encode(xpdu) apdu._xpdu = xpdu except Exception as err: ApplicationServiceAccessPoint._exception("unconfirmed request encoding error: %r", err) return else: if _debug: ApplicationServiceAccessPoint._debug(" - unknown PDU type?!") return if _debug: ApplicationServiceAccessPoint._debug(" - xpdu %r", xpdu) # forward the encoded packet self.request(xpdu) def confirmation(self, apdu): if _debug: ApplicationServiceAccessPoint._debug("confirmation %r", apdu) if isinstance(apdu, SimpleAckPDU): xpdu = apdu elif isinstance(apdu, ComplexAckPDU): atype = complex_ack_types.get(apdu.apduService) if not atype: if _debug: ApplicationServiceAccessPoint._debug(" - no complex ack decoder") return try: xpdu = atype() xpdu.decode(apdu) except Exception as err: ApplicationServiceAccessPoint._exception("unconfirmed request decoding error: %r", err) return elif isinstance(apdu, ErrorPDU): atype = error_types.get(apdu.apduService) if not atype: if _debug: ApplicationServiceAccessPoint._debug(" - no special error decoder") atype = Error try: xpdu = atype() xpdu.decode(apdu) except Exception as err: ApplicationServiceAccessPoint._exception("error PDU decoding error: %r", err) xpdu = Error(errorClass=0, errorCode=0) elif isinstance(apdu, RejectPDU): xpdu = apdu elif isinstance(apdu, AbortPDU): xpdu = apdu else: if _debug: ApplicationServiceAccessPoint._debug(" - unknown PDU type") return if _debug: ApplicationServiceAccessPoint._debug(" - xpdu %r", xpdu) # forward the decoded packet self.sap_response(xpdu) def sap_confirmation(self, apdu): if _debug: ApplicationServiceAccessPoint._debug("sap_confirmation %r", apdu) if isinstance(apdu, SimpleAckPDU): xpdu = apdu elif isinstance(apdu, ComplexAckPDU): xpdu = ComplexAckPDU() apdu.encode(xpdu) elif isinstance(apdu, ErrorPDU): xpdu = ErrorPDU() apdu.encode(xpdu) elif isinstance(apdu, RejectPDU): xpdu = apdu elif isinstance(apdu, AbortPDU): xpdu = apdu else: if _debug: ApplicationServiceAccessPoint._debug(" - unknown PDU type") return if _debug: ApplicationServiceAccessPoint._debug(" - xpdu %r", xpdu) # forward the encoded packet self.response(xpdu) PKFJbacpypes/event.py#/usr/bin/python """ Event """ import asyncore import os import select from .debugging import Logging, bacpypes_debugging, ModuleLogger # some debugging _debug = 0 _log = ModuleLogger(globals()) # # WaitableEvent # # An instance of this class can be used like a Threading.Event, but will # break the asyncore.loop(). # @bacpypes_debugging class WaitableEvent(asyncore.file_dispatcher, Logging): def __init__(self): if _debug: WaitableEvent._debug("__init__") # make a pipe self._read_fd, self._write_fd = os.pipe() # continue with init asyncore.file_dispatcher.__init__(self, self._read_fd) def __del__(self): if _debug: WaitableEvent._debug("__del__") # close the file descriptors os.close(self._read_fd) os.close(self._write_fd) #----- file methods def readable(self): # we are always happy to read return True def writable(self): # we never have anything to write return False def handle_read(self): if _debug: WaitableEvent._debug("handle_read") def handle_write(self): if _debug: WaitableEvent._debug("handle_write") def handle_close(self): if _debug: WaitableEvent._debug("handle_close") self.close() #----- event methods def wait(self, timeout=None): rfds, wfds, efds = select.select([self._read_fd], [], [], timeout) return self._read_fd in rfds def isSet(self): return self.wait(0) def set(self): if _debug: WaitableEvent._debug("set") if not self.isSet(): os.write(self._write_fd, b'1') def clear(self): if _debug: WaitableEvent._debug("clear") if self.isSet(): os.read(self._read_fd, 1) PKF1??bacpypes/udp.py#!/usr/bin/python """ UDP Communications Module """ import asyncore import socket import pickle import queue from time import time as _time from .debugging import ModuleLogger, bacpypes_debugging from .core import deferred from .task import FunctionTask from .comm import PDU, Server from .comm import ServiceAccessPoint # some debugging _debug = 0 _log = ModuleLogger(globals()) # # UDPActor # # Actors are helper objects for a director. There is one actor for # each peer. # @bacpypes_debugging class UDPActor: def __init__(self, director, peer): if _debug: UDPActor._debug("__init__ %r %r", director, peer) # keep track of the director self.director = director # associated with a peer self.peer = peer # add a timer self.timeout = director.timeout if self.timeout > 0: self.timer = FunctionTask(self.IdleTimeout) self.timer.install_task(_time() + self.timeout) else: self.timer = None # tell the director this is a new actor self.director.AddActor(self) def IdleTimeout(self): if _debug: UDPActor._debug("IdleTimeout") # tell the director this is gone self.director.RemoveActor(self) def indication(self, pdu): if _debug: UDPActor._debug("indication %r", pdu) # reschedule the timer if self.timer: self.timer.install_task(_time() + self.timeout) # put it in the outbound queue for the director self.director.request.put(pdu) def response(self, pdu): if _debug: UDPActor._debug("response %r", pdu) # reschedule the timer if self.timer: self.timer.install_task(_time() + self.timeout) # process this as a response from the director self.director.response(pdu) # # UDPPickleActor # @bacpypes_debugging class UDPPickleActor(UDPActor): def __init__(self, *args): if _debug: UDPPickleActor._debug("__init__ %r", args) UDPActor.__init__(self, *args) def indication(self, pdu): if _debug: UDPPickleActor._debug("indication %r", pdu) # pickle the data pdu.pduData = pickle.dumps(pdu.pduData) # continue as usual UDPActor.indication(self, pdu) def response(self, pdu): if _debug: UDPPickleActor._debug("response %r", pdu) # unpickle the data try: pdu.pduData = pickle.loads(pdu.pduData) except: UDPPickleActor._exception("pickle error") return # continue as usual UDPActor.response(self, pdu) # # UDPDirector # @bacpypes_debugging class UDPDirector(asyncore.dispatcher, Server, ServiceAccessPoint): def __init__(self, address, timeout=0, reuse=False, actorClass=UDPActor, sid=None, sapID=None): if _debug: UDPDirector._debug("__init__ %r timeout=%r reuse=%r actorClass=%r sid=%r sapID=%r", address, timeout, reuse, actorClass, sid, sapID) Server.__init__(self, sid) ServiceAccessPoint.__init__(self, sapID) # check the actor class if not issubclass(actorClass, UDPActor): raise TypeError("actorClass must be a subclass of UDPActor") self.actorClass = actorClass # save the timeout for actors self.timeout = timeout # save the address self.address = address asyncore.dispatcher.__init__(self) # ask the dispatcher for a socket self.create_socket(socket.AF_INET, socket.SOCK_DGRAM) # if the reuse parameter is provided, set the socket option if reuse: self.set_reuse_addr() # proceed with the bind self.bind(address) if _debug: UDPDirector._debug(" - getsockname: %r", self.socket.getsockname()) # allow it to send broadcasts self.socket.setsockopt( socket.SOL_SOCKET, socket.SO_BROADCAST, 1 ) # create the request queue self.request = queue.Queue() # start with an empty peer pool self.peers = {} def AddActor(self, actor): """Add an actor when a new one is connected.""" if _debug: UDPDirector._debug("AddActor %r", actor) self.peers[actor.peer] = actor # tell the ASE there is a new client if self.serviceElement: self.sap_request(addPeer=actor.peer) def RemoveActor(self, actor): """Remove an actor when the socket is closed.""" if _debug: UDPDirector._debug("RemoveActor %r", actor) del self.peers[actor.peer] # tell the ASE the client has gone away if self.serviceElement: self.sap_request(delPeer=actor.peer) def GetActor(self, address): return self.peers.get(address, None) def handle_connect(self): if _debug: deferred(UDPDirector._debug, "handle_connect") def readable(self): return 1 def handle_read(self): if _debug: deferred(UDPDirector._debug, "handle_read") try: msg, addr = self.socket.recvfrom(65536) if _debug: deferred(UDPDirector._debug, " - received %d octets from %s", len(msg), addr) # send the PDU up to the client deferred(self._response, PDU(msg, source=addr)) except socket.timeout as err: deferred(UDPDirector._error, "handle_read socket timeout: %s", err) except OSError as err: if err.args[0] == 11: pass else: deferred(UDPDirector._error, "handle_read socket error: %s", err) def writable(self): """Return true iff there is a request pending.""" return (not self.request.empty()) def handle_write(self): """get a PDU from the queue and send it.""" if _debug: deferred(UDPDirector._debug, "handle_write") try: pdu = self.request.get() sent = self.socket.sendto(pdu.pduData, pdu.pduDestination) if _debug: deferred(UDPDirector._debug, " - sent %d octets to %s", sent, pdu.pduDestination) except OSError as err: deferred(UDPDirector._error, "handle_write socket error: %s", err) def handle_close(self): """Remove this from the monitor when it's closed.""" if _debug: deferred(UDPDirector._debug, "handle_close") self.close() self.socket = None def indication(self, pdu): """Client requests are queued for delivery.""" if _debug: UDPDirector._debug("indication %r", pdu) # get the destination addr = pdu.pduDestination # get the peer peer = self.peers.get(addr, None) if not peer: peer = self.actorClass(self, addr) # send the message peer.indication(pdu) def _response(self, pdu): """Incoming datagrams are routed through an actor.""" if _debug: UDPDirector._debug("_response %r", pdu) # get the destination addr = pdu.pduSource # get the peer peer = self.peers.get(addr, None) if not peer: peer = self.actorClass(self, addr) # send the message peer.response(pdu) PKG5H,bacpypes/basetypes.py#!/usr/bin/python """ Base Types """ from .debugging import ModuleLogger from .primitivedata import BitString, Boolean, CharacterString, Date, Double, \ Enumerated, Integer, Null, ObjectIdentifier, OctetString, Real, Time, \ Unsigned from .constructeddata import Any, AnyAtomic, ArrayOf, Choice, Element, \ Sequence, SequenceOf # some debugging _debug = 0 _log = ModuleLogger(globals()) # # Bit Strings # class DaysOfWeek(BitString): bitNames = \ { 'monday':0 , 'tuesday':1 , 'wednesday':2 , 'thursday':3 , 'friday':4 , 'saturday':5 , 'sunday':6 } class EventTransitionBits(BitString): bitNames = \ { 'toOffnormal':0 , 'toFault':1 , 'toNormal':2 } class LimitEnable(BitString): bitNames = \ { 'lowLimitEnable':0 , 'highLimitEnable':1 } class LogStatus(BitString): bitNames = \ { 'logDisabled':0 , 'bufferPurged':1 , 'logInterrupted':2 } bitLen = 3 class ObjectTypesSupported(BitString): bitNames = \ { 'analogInput':0 , 'analogOutput':1 , 'analogValue':2 , 'binaryInput':3 , 'binaryOutput':4 , 'binaryValue':5 , 'calendar':6 , 'command':7 , 'device':8 , 'eventEnrollment':9 , 'file':10 , 'group':11 , 'loop':12 , 'multiStateInput':13 , 'multiStateOutput':14 , 'notificationClass':15 , 'program':16 , 'schedule':17 , 'averaging':18 , 'multiStateValue':19 , 'trendLog':20 , 'lifeSafetyPoint':21 , 'lifeSafetyZone':22 , 'accumulator':23 , 'pulseConverter':24 , 'eventLog':25 , 'globalGroup':26 , 'trendLogMultiple':27 , 'loadControl':28 , 'structuredView':29 , 'accessDoor':30 , 'accessCredential':32 , 'accessPoint':33 , 'accessRights':34 , 'accessUser':35 , 'accessZone':36 , 'credentialDataInput':37 , 'networkSecurity':38 , 'bitstringValue':39 , 'characterstringValue':40 , 'datePatternValue':41 , 'dateValue':42 , 'datetimePatternValue':43 , 'datetimeValue':44 , 'integerValue':45 , 'largeAnalogValue':46 , 'octetstringValue':47 , 'positiveIntegerValue':48 , 'timePatternValue':49 , 'timeValue':50 } bitLen = 51 class ResultFlags(BitString): bitNames = \ { 'firstItem':0 , 'lastItem':1 , 'moreItems':2 } bitLen = 3 class ServicesSupported(BitString): bitNames = \ { 'acknowledgeAlarm':0 , 'confirmedCOVNotification':1 , 'confirmedEventNotification':2 , 'getAlarmSummary':3 , 'getEnrollmentSummary':4 , 'subscribeCOV':5 , 'atomicReadFile':6 , 'atomicWriteFile':7 , 'addListElement':8 , 'removeListElement':9 , 'createObject':10 , 'deleteObject':11 , 'readProperty':12 # , 'readPropertyConditional':13 # removed in version 1 revision 12 , 'readPropertyMultiple':14 , 'writeProperty':15 , 'writePropertyMultiple':16 , 'deviceCommunicationControl':17 , 'confirmedPrivateTransfer':18 , 'confirmedTextMessage':19 , 'reinitializeDevice':20 , 'vtOpen':21 , 'vtClose':22 , 'vtData':23 # , 'authenticate':24 # removed in version 1 revision 11 # , 'requestKey':25 # removed in version 1 revision 11 , 'iAm':26 , 'iHave':27 , 'unconfirmedCOVNotification':28 , 'unconfirmedEventNotification':29 , 'unconfirmedPrivateTransfer':30 , 'unconfirmedTextMessage':31 , 'timeSynchronization':32 , 'whoHas':33 , 'whoIs':34 , 'readRange':35 , 'utcTimeSynchronization':36 , 'lifeSafetyOperation':37 , 'subscribeCOVProperty':38 , 'getEventInformation':39 , 'writeGroup':40 } bitLen = 41 class StatusFlags(BitString): bitNames = \ { 'inAlarm':0 , 'fault':1 , 'overridden':2 , 'outOfService':3 } bitLen = 4 # # Enumerations # class AccessAuthenticationFactorDisable(Enumerated): vendor_range = (64, 65535) enumerations = \ { 'none':0 , 'disabled':1 , 'disabledLost':2 , 'disabledStolen':3 , 'disabledDamaged':4 , 'disabledDestroyed':5 } class AccessCredentialDisable(Enumerated): vendor_range = (64, 65535) enumerations = \ { 'none':0 , 'disable':1 , 'disableManual':2 , 'disableLockout':3 } class AccessCredentialDisableReason(Enumerated): enumerations = \ { 'disabled':0 , 'disabledNeedsProvisioning':1 , 'disabledUnassigned':2 , 'disabledNotYetActive':3 , 'disabledExpired':4 , 'disabledLockout':5 , 'disabledMaxDays':6 , 'disabledMaxUses':7 , 'disabledInactivity':8 , 'disabledManual':9 } class AccessEvent(Enumerated): vendor_range = (512, 65535) enumerations = \ { 'none':0 , 'granted':1 , 'muster':2 , 'passbackDetected':3 , 'duress':4 , 'trace':5 , 'lockoutMaxAttempts':6 , 'lockoutOther':7 , 'lockoutRelinquished':8 , 'lockedByHigherPriority':9 , 'outOfService':10 , 'outOfServiceRelinquished':11 , 'accompanimentBy':12 , 'authenticationFactorRead':13 , 'authorizationDelayed':14 , 'verificationRequired':15 , 'deniedDenyAll':128 , 'deniedUnknownCredential':129 , 'deniedAuthenticationUnavailable':130 , 'deniedAuthenticationFactorTimeout':131 , 'deniedIncorrectAuthenticationFactor':132 , 'deniedZoneNoAccessRights':133 , 'deniedPointNoAccessRights':134 , 'deniedNoAccessRights':135 , 'deniedOutOfTimeRange':136 , 'deniedThreatLevel':137 , 'deniedPassback':138 , 'deniedUnexpectedLocationUsage':139 , 'deniedMaxAttempts':140 , 'deniedLowerOccupancyLimit':141 , 'deniedUpperOccupancyLimit':142 , 'deniedAuthenticationFactorLost':143 , 'deniedAuthenticationFactorStolen':144 , 'deniedAuthenticationFactorDamaged':145 , 'deniedAuthenticationFactorDestroyed':146 , 'deniedAuthenticationFactorDisabled':147 , 'deniedAuthenticationFactorError':148 , 'deniedCredentialUnassigned':149 , 'deniedCredentialNotProvisioned':150 , 'deniedCredentialNotYetActive':151 , 'deniedCredentialExpired':152 , 'deniedCredentialManualDisable':153 , 'deniedCredentialLockout':154 , 'deniedCredentialMaxDays':155 , 'deniedCredentialMaxUses':156 , 'deniedCredentialInactivity':157 , 'deniedCredentialDisabled':158 , 'deniedNoAccompaniment':159 , 'deniedIncorrectAccompaniment':160 , 'deniedLockout':161 , 'deniedVerificationFailed':162 , 'deniedVerificationTimeout':163 , 'deniedOther':164 } class AccessPassbackMode(Enumerated): enumerations = \ { 'passbackOff':0 , 'hardPassback':1 , 'softPassback':2 } class AccessRuleTimeRangeSpecifier(Enumerated): enumerations = \ { 'specified':0 , 'always':1 } class AccessRuleLocationSpecifier(Enumerated): enumerations = \ { 'specified':0 , 'all':1 } class AccessUserType(Enumerated): vendor_range = (64, 65535) enumerations = \ { 'asset':0 , 'group':1 , 'person':2 } class AccessZoneOccupancyState(Enumerated): vendor_range = (64, 65535) enumerations = \ { 'normal':0 , 'belowLowerLimit':1 , 'atLowerLimit':2 , 'atUpperLimit':3 , 'aboveUpperLimit':4 , 'disabled':5 , 'notSupported':6 } class AccumulatorRecordAccumulatorStatus(Enumerated): enumerations = \ { 'normal':0 , 'starting':1 , 'recovered':2 , 'abnormal':3 , 'failed':4 } class Action(Enumerated): enumerations = \ { 'direct':0 , 'reverse':1 } class AuthenticationFactorType(Enumerated): enumerations = \ { 'undefined':0 , 'error':1 , 'custom':2 , 'simpleNumber16':3 , 'simpleNumber32':4 , 'simpleNumber56':5 , 'simpleAlphaNumeric':6 , 'abaTrack2':7 , 'wiegand26':8 , 'wiegand37':9 , 'wiegand37facility':10 , 'facility16card32':11 , 'facility32card32':12 , 'fascN':13 , 'fascNbcd':14 , 'fascNlarge':15 , 'fascNlargeBcd':16 , 'gsa75':17 , 'chuid':18 , 'chuidFull':19 , 'guid':20 , 'cbeffA':21 , 'cbeffB':22 , 'cbeffC':23 , 'userPassword':24 } class AuthenticationStatus(Enumerated): enumerations = \ { 'notReady':0 , 'ready':1 , 'disabled':2 , 'waitingForAuthenticationFactor':3 , 'waitingForAccompaniment':4 , 'waitingForVerification':5 , 'inProgress':6 } class AuthorizationException(Enumerated): vendor_range = (64, 255) enumerations = \ { 'passback':0 , 'occupancyCheck':1 , 'accessRights':2 , 'lockout':3 , 'deny':4 , 'verification':5 , 'authorizationDelay':6 } class AuthorizationMode(Enumerated): vendor_range = (64, 65536) enumerations = \ { 'authorize':0 , 'grantActive':1 , 'denyAll':2 , 'verificationRequired':3 , 'authorizationDelayed':4 , 'none':5 } class BackupState(Enumerated): enumerations = \ { 'idle':0 , 'preparingForBackup':1 , 'preparingForRestore':2 , 'performingABackup':3 , 'performingARestore':4 , 'backupFailure':5 , 'restoreFailure':6 } class BinaryPV(Enumerated): enumerations = \ { 'inactive':0 , 'active':1 } class DeviceStatus(Enumerated): vendor_range = (64, 65535) enumerations = \ { 'operational':0 , 'operationalReadOnly':1 , 'downloadRequired':2 , 'downloadInProgress':3 , 'nonOperational':4 , 'backupInProgress':5 } class DoorAlarmState(Enumerated): vendor_range = (256, 65535) enumerations = \ { 'normal':0 , 'alarm':1 , 'doorOpenTooLong':2 , 'forcedOpen':3 , 'tamper':4 , 'doorFault':5 , 'lockDown':6 , 'freeAccess':7 , 'egressOpen':8 } class DoorSecuredStatus(Enumerated): enumerations = \ { 'secured':0 , 'unsecured':1 , 'unknown':2 } class DoorStatus(Enumerated): enumerations = \ { 'closed':0 , 'opened':1 , 'unknown':2 } class DoorValue(Enumerated): enumerations = \ { 'lock':0 , 'unlock':1 , 'pulseUnlock':2 , 'extendedPulseUnlock':3 } class EngineeringUnits(Enumerated): vendor_range = (256, 65535) enumerations = \ { #Acceleration 'metersPerSecondPerSecond':166 , 'squareMeters':0 , 'squareCentimeters':116 , 'squareFeet':1 , 'squareInches':115 #Currency , 'currency1':105 , 'currency2':106 , 'currency3':107 , 'currency4':108 , 'currency5':109 , 'currency6':110 , 'currency7':111 , 'currency8':112 , 'currency9':113 , 'currency10':114 #Electrical , 'milliamperes':2 , 'amperes':3 , 'amperesPerMeter':167 , 'amperesPerSquareMeter':168 , 'ampereSquareMeters':169 , 'decibels':199 , 'decibelsMillivolt':200 , 'decibelsVolt':201 , 'farads':170 , 'henrys':171 , 'ohms':4 , 'ohmMeters':172 , 'milliohms':145 , 'kilohms':122 , 'megohms':123 , 'microSiemens':190 , 'millisiemens':202 , 'siemens':173 , 'siemensPerMeter':174 , 'teslas':175 , 'volts':5 , 'millivolts':124 , 'kilovolts':6 , 'megavolts':7 , 'voltAmperes':8 , 'kilovoltAmperes':9 , 'megavoltAmperes':10 , 'voltAmperesReactive':11 , 'kilovoltAmperesReactive':12 , 'megavoltAmperesReactive':13 , 'voltsPerDegreeKelvin':176 , 'voltsPerMeter':177 , 'degreesPhase':14 , 'powerFactor':15 , 'webers':178 # Energy , 'joules':16 , 'kilojoules':17 , 'kilojoulesPerKilogram':125 , 'megajoules':126 , 'wattHours':18 , 'kilowattHours':19 , 'megawattHours':146 , 'wattHoursReactive':203 , 'kilowattHoursReactive':204 , 'megawattHoursReactive':205 , 'btus':20 , 'kiloBtus':147 , 'megaBtus':148 , 'therms':21 , 'tonHours':22 # Enthalpy , 'joulesPerKilogramDryAir':23 , 'kilojoulesPerKilogramDryAir':149 , 'megajoulesPerKilogramDryAir':150 , 'btusPerPoundDryAir':24 , 'btusPerPound':117 , 'joulesPerDegreeKelvin':127 # Entropy , 'kilojoulesPerDegreeKelvin':151 , 'megajoulesPerDegreeKelvin':152 , 'joulesPerKilogramDegreeKelvin':128 # Force , 'newton':153 # Frequency , 'cyclesPerHour':25 , 'cyclesPerMinute':26 , 'hertz':27 , 'kilohertz':129 , 'megahertz':130 , 'perHour':131 , 'gramsOfWaterPerKilogramDryAir':28 , 'percentRelativeHumidity':29 , 'micrometers':194 , 'millimeters':30 , 'centimeters':118 , 'kilometers':193 , 'meters':31 , 'inches':32 , 'feet':33 , 'candelas':179 , 'candelasPerSquareMeter':180 , 'wattsPerSquareFoot':34 , 'wattsPerSquareMeter':35 , 'lumens':36 , 'luxes':37 , 'footCandles':38 , 'milligrams':196 , 'grams':195 , 'kilograms':39 , 'poundsMass':40 , 'tons':41 , 'gramsPerSecond':154 , 'gramsPerMinute':155 , 'kilogramsPerSecond':42 , 'kilogramsPerMinute':43 , 'kilogramsPerHour':44 , 'poundsMassPerSecond':119 , 'poundsMassPerMinute':45 , 'poundsMassPerHour':46 , 'tonsPerHour':156 , 'milliwatts':132 , 'watts':47 , 'kilowatts':48 , 'megawatts':49 , 'btusPerHour':50 , 'kiloBtusPerHour':157 , 'horsepower':51 , 'tonsRefrigeration':52 , 'pascals':53 , 'hectopascals':133 , 'kilopascals':54 , 'millibars':134 , 'bars':55 , 'poundsForcePerSquareInch':56 , 'millimetersOfWater':206 , 'centimetersOfWater':57 , 'inchesOfWater':58 , 'millimetersOfMercury':59 , 'centimetersOfMercury':60 , 'inchesOfMercury':61 , 'degreesCelsius':62 , 'degreesKelvin':63 , 'degreesKelvinPerHour':181 , 'degreesKelvinPerMinute':182 , 'degreesFahrenheit':64 , 'degreeDaysCelsius':65 , 'degreeDaysFahrenheit':66 , 'deltaDegreesFahrenheit':120 , 'deltaDegreesKelvin':121 , 'years':67 , 'months':68 , 'weeks':69 , 'days':70 , 'hours':71 , 'minutes':72 , 'seconds':73 , 'hundredthsSeconds':158 , 'milliseconds':159 , 'newtonMeters':160 , 'millimetersPerSecond':161 , 'millimetersPerMinute':162 , 'metersPerSecond':74 , 'metersPerMinute':163 , 'metersPerHour':164 , 'kilometersPerHour':75 , 'feetPerSecond':76 , 'feetPerMinute':77 , 'milesPerHour':78 , 'cubicFeet':79 , 'cubicMeters':80 , 'imperialGallons':81 , 'milliliters':197 , 'liters':82 , 'usGallons':83 , 'cubicFeetPerSecond':142 , 'cubicFeetPerMinute':84 , 'cubicFeetPerHour':191 , 'cubicMetersPerSecond':85 , 'cubicMetersPerMinute':165 , 'cubicMetersPerHour':135 , 'imperialGallonsPerMinute':86 , 'millilitersPerSecond':198 , 'litersPerSecond':87 , 'litersPerMinute':88 , 'litersPerHour':136 , 'usGallonsPerMinute':89 , 'usGallonsPerHour':192 , 'degreesAngular':90 , 'degreesCelsiusPerHour':91 , 'degreesCelsiusPerMinute':92 , 'degreesFahrenheitPerHour':93 , 'degreesFahrenheitPerMinute':94 , 'jouleSeconds':183 , 'kilogramsPerCubicMeter':186 , 'kilowattHoursPerSquareMeter':137 , 'kilowattHoursPerSquareFoot':138 , 'megajoulesPerSquareMeter':139 , 'megajoulesPerSquareFoot':140 , 'noUnits':95 , 'newtonSeconds':187 , 'newtonsPerMeter':188 , 'partsPerMillion':96 , 'partsPerBillion':97 , 'percent':98 , 'percentObscurationPerFoot':143 , 'percentObscurationPerMeter':144 , 'percentPerSecond':99 , 'perMinute':100 , 'perSecond':101 , 'psiPerDegreeFahrenheit':102 , 'radians':103 , 'radiansPerSecond':184 , 'revolutionsPerMinute':104 , 'squareMetersPerNewton':185 , 'wattsPerMeterPerDegreeKelvin':189 , 'wattsPerSquareMeterDegreeKelvin':141 , 'perMille':207 , 'gramsPerGram':208 , 'kilogramsPerKilogram':209 , 'gramsPerKilogram':210 , 'milligramsPerGram':211 , 'milligramsPerKilogram':212 , 'gramsPerMilliliter':213 , 'gramsPerLiter':214 , 'milligramsPerLiter':215 , 'microgramsPerLiter':216 , 'gramsPerCubicMeter':217 , 'milligramsPerCubicMeter':218 , 'microgramsPerCubicMeter':219 , 'nanogramsPerCubicMeter':220 , 'gramsPerCubicCentimeter':221 , 'becquerels':222 , 'kilobecquerels':223 , 'megabecquerels':224 , 'gray':225 , 'milligray':226 , 'microgray':227 , 'sieverts':228 , 'millisieverts':229 , 'microsieverts':230 , 'microsievertsPerHour':231 , 'decibelsA':232 , 'nephelometricTurbidityUnit':233 , 'pH':234 , 'gramsPerSquareMeter':235 , 'minutesPerDegreeKelvin':236 } class ErrorClass(Enumerated): enumerations = \ { 'device':0 , 'object':1 , 'property':2 , 'resources':3 , 'security':4 , 'services':5 , 'vt':6 } class ErrorCode(Enumerated): enumerations = \ { 'abortApduTooLong':123 , 'abortApplicationExceededReplyTime':124 , 'abortBufferOverflow':51 , 'abortInsufficientSecurity':135 , 'abortInvalidApduInThisState':52 , 'abortOther':56 , 'abortOutOfResources':125 , 'abortPreemptedByHigherPriorityTask':53 , 'abortProprietary':55 , 'abortSecurityError':136 , 'abortSegmentationNotSupported':54 , 'abortProprietary':55 , 'abortOther':56 , 'abortTsmTimeout':126 , 'abortWindowSizeOutOfRange':127 , 'accessDenied':85 , 'addressingError':115 , 'badDestinationAddress':86 , 'badDestinationDeviceId':87 , 'badSignature':88 , 'badSourceAddress':89 , 'badTimestamp':90 , 'busy':82 , 'cannotUseKey':91 , 'cannotVerifyMessageId':92 , 'characterSetNotSupported':41 , 'communicationDisabled':83 , 'configurationInProgress':2 , 'correctKeyRevision':93 , 'covSubscriptionFailed':43 , 'datatypeNotSupported':47 , 'deleteFdtEntryFailed':120 , 'deviceBusy':3 , 'destinationDeviceIdRequired':94 , 'distributeBroadcastFailed':121 , 'duplicateMessage':95 , 'duplicateName':48 , 'duplicateObjectId':49 , 'dynamicCreationNotSupported':4 , 'encryptionNotConfigured':96 , 'encryptionRequired':97 , 'fileAccessDenied':5 , 'fileFull':128 , 'inconsistentConfiguration':129 , 'inconsistentObjectType':130 , 'inconsistentParameters':7 , 'inconsistentSelectionCriterion':8 , 'incorrectKey':98 , 'internalError':131 , 'invalidArrayIndex':42 , 'invalidConfigurationData':46 , 'invalidDataType':9 , 'invalidEventState':73 , 'invalidFileAccessMethod':10 , 'invalidFileStartPosition':11 , 'invalidKeyData':99 , 'invalidParameterDataType':13 , 'invalidTag':57 , 'invalidTimeStamp':14 , 'keyUpdateInProgress':100 , 'listElementNotFound':81 , 'logBufferFull':75 , 'loggedValuePurged':76 , 'malformedMessage':101 , 'messageTooLong':113 , 'missingRequiredParameter':16 , 'networkDown':58 , 'noAlarmConfigured':74 , 'noObjectsOfSpecifiedType':17 , 'noPropertySpecified':77 , 'noSpaceForObject':18 , 'noSpaceToAddListElement':19 , 'noSpaceToWriteProperty':20 , 'noVtSessionsAvailable':21 , 'notConfigured':132 , 'notConfiguredForTriggeredLogging':78 , 'notCovProperty':44 , 'notKeyServer':102 , 'notRouterToDnet':110 , 'objectDeletionNotPermitted':23 , 'objectIdentifierAlreadyExists':24 , 'other':0 , 'operationalProblem':25 , 'optionalFunctionalityNotSupported':45 , 'outOfMemory':133 , 'parameterOutOfRange':80 , 'passwordFailure':26 , 'propertyIsNotAList':22 , 'propertyIsNotAnArray':50 , 'readAccessDenied':27 , 'readBdtFailed':117 , 'readFdtFailed':119 , 'registerForeignDeviceFailed':118 , 'rejectBufferOverflow':59 , 'rejectInconsistentParameters':60 , 'rejectInvalidParameterDataType':61 , 'rejectInvalidTag':62 , 'rejectMissingRequiredParameter':63 , 'rejectParameterOutOfRange':64 , 'rejectTooManyArguments':65 , 'rejectUndefinedEnumeration':66 , 'rejectUnrecognizedService':67 , 'rejectProprietary':68 , 'rejectOther':69 , 'routerBusy':111 , 'securityError':114 , 'securityNotConfigured':103 , 'serviceRequestDenied':29 , 'sourceSecurityRequired':104 , 'success':84 , 'timeout':30 , 'tooManyKeys':105 , 'unknownAuthenticationType':106 , 'unknownDevice':70 , 'unknownFileSize':122 , 'unknownKey':107 , 'unknownKeyRevision':108 , 'unknownNetworkMessage':112 , 'unknownObject':31 , 'unknownProperty':32 , 'unknownSubscription':79 , 'umknownRoute':71 , 'unknownSourceMessage':109 , 'unknownVtClass':34 , 'unknownVtSession':35 , 'unsupportedObjectType':36 , 'valueNotInitialized':72 , 'valueOutOfRange':37 , 'valueTooLong':134 , 'vtSessionAlreadyClosed':38 , 'vtSessionTerminationFailure':39 , 'writeAccessDenied':40 , 'writeBdtFailed':116 } class EventState(Enumerated): vendor_range = (64, 65535) enumerations = \ { 'normal':0 , 'fault':1 , 'offnormal':2 , 'highLimit':3 , 'lowLimit':4 , 'lifeSafetyAlarm':5 } class EventType(Enumerated): vendor_range = (64, 65535) enumerations = \ { 'changeOfBitstring':0 , 'changeOfState':1 , 'changeOfValue':2 , 'commandFailure':3 , 'floatingLimit':4 , 'outOfRange':5 # -- context tag 7 is deprecated , 'changeOfLifeSafety':8 , 'extended':9 , 'bufferReady':10 , 'unsignedRange':11 # -- enumeration value 12 is reserved for future addenda , 'accessEvent':13 , 'doubleOutOfRange':14 , 'signedOutOfRange':15 , 'unsignedOutOfRange':16 , 'changeOfCharacterstring':17 , 'changeOfStatusFlags':18 } class FaultType(Enumerated): enumerations = \ { 'none':0 , 'fault-characterstring':1 , 'fault-extended':2 , 'fault-life-safety':3 , 'fault-state':4 , 'fault-status-flags':5 } class FileAccessMethod(Enumerated): enumerations = \ { 'recordAccess':0 , 'streamAccess':1 } class LifeSafetyMode(Enumerated): enumerations = \ { 'off':0 , 'on':1 , 'test':2 , 'manned':3 , 'unmanned':4 , 'armed':5 , 'disarmed':6 , 'prearmed':7 , 'slow':8 , 'fast':9 , 'disconnected':10 , 'enabled':11 , 'disabled':12 , 'automaticReleaseDisabled':13 , 'default':14 } class LifeSafetyOperation(Enumerated): vendor_range = (64, 65535) enumerations = \ { 'none':0 , 'silence':1 , 'silenceAudible':2 , 'silenceVisual':3 , 'reset':4 , 'resetAlarm':5 , 'resetFault':6 , 'unsilence':7 , 'unsilenceAudible':8 , 'unsilenceVisual':9 } class LifeSafetyState(Enumerated): enumerations = \ { 'quiet':0 , 'preAlarm':1 , 'alarm':2 , 'fault':3 , 'faultPreAlarm':4 , 'faultAlarm':5 , 'notReady':6 , 'active':7 , 'tamper':8 , 'testAlarm':9 , 'testActive':10 , 'testFault':11 , 'testFaultAlarm':12 , 'holdup':13 , 'duress':14 , 'tamperAlarm':15 , 'abnormal':16 , 'emergencyPower':17 , 'delayed':18 , 'blocked':19 , 'localAlarm':20 , 'generalAlarm':21 , 'supervisory':22 , 'testSupervisory':23 } class LightingInProgress(Enumerated): enumerations = \ { 'idle':0 , 'fadeActive':1 , 'rampActive':2 , 'notControlled':3 , 'other':4 } class LightingOperation(Enumerated): vendor_range = (256, 65535) enumerations = \ { 'none':0 , 'fadeTo':1 , 'rampTo':2 , 'stepUp':3 , 'stepDown':4 , 'stepOn':5 , 'stepOff':6 , 'warn':7 , 'warnOff':8 , 'warnRelinquish':9 , 'stop':10 } class LightingTransition(Enumerated): vendor_range = (64, 255) enumerations = \ { 'none':0 , 'fade':1 , 'ramp':2 } class LockStatus(Enumerated): enumerations = \ { 'locked':0 , 'unlocked':1 , 'fault':2 , 'unknown':3 } class LoggingType(Enumerated): vendor_range = (64, 255) enumerations = \ { 'polled':0 , 'cov':1 , 'triggered':2 } class Maintenance(Enumerated): vendor_range = (256, 65535) enumerations = \ { 'none':0 , 'periodicTest':1 , 'needServiceOperational':2 , 'needServiceInoperative':3 } class NodeType(Enumerated): enumerations = \ { 'unknown':0 , 'system':1 , 'network':2 , 'device':3 , 'organizational':4 , 'area':5 , 'equipment':6 , 'point':7 , 'collection':8 , 'property':9 , 'functional':10 , 'other':11 } class NotifyType(Enumerated): enumerations = \ { 'alarm':0 , 'event':1 , 'ackNotification':2 } class Polarity(Enumerated): enumerations = \ { 'normal':0 , 'reverse':1 } class ProgramError(Enumerated): vendor_range = (64, 65535) enumerations = \ { 'normal':0 , 'loadFailed':1 , 'internal':2 , 'program':3 , 'other':4 } class ProgramRequest(Enumerated): enumerations = \ { 'ready':0 , 'load':1 , 'run':2 , 'halt':3 , 'restart':4 , 'unload':5 } class ProgramState(Enumerated): enumerations = \ { 'idle':0 , 'loading':1 , 'running':2 , 'waiting':3 , 'halted':4 , 'unloading':5 } class PropertyIdentifier(Enumerated): vendor_range = (512, 4194303) enumerations = \ { 'absenteeLimit':244 , 'acceptedModes':175 , 'accessAlarmEvents':245 , 'accessDoors':246 , 'accessEvent':247 , 'accessEventAuthenticationFactor':248 , 'accessEventCredential':249 , 'accessEventTag':322 , 'accessEventTime':250 , 'accessTransactionEvents':251 , 'accompaniment':252 , 'accompanimentTime':253 , 'ackRequired':1 , 'ackedTransitions':0 , 'action':2 , 'actionText':3 , 'activationTime':254 , 'activeAuthenticationPolicy':255 , 'activeCovSubscriptions':152 , 'activeText':4 , 'activeVtSessions':5 , 'actualShedLevel':212 , 'adjustValue':176 , 'alarmValue':6 , 'alarmValues':7 , 'alignIntervals':193 , 'all':8 , 'allWritesSuccessful':9 , 'allowGroupDelayInhibit':365 , 'apduSegmentTimeout':10 , 'apduTimeout':11 , 'applicationSoftwareVersion':12 , 'archive':13 , 'assignedAccessRights':256 , 'attemptedSamples':124 , 'authenticationFactors':257 , 'authenticationPolicyList':258 , 'authenticationPolicyNames':259 , 'authenticationStatus':260 , 'authorizationExemptions':364 , 'authorizationMode':261 , 'autoSlaveDiscovery':169 , 'averageValue':125 , 'backupAndRestoreState':338 , 'backupFailureTimeout':153 , 'backupPreparationTime':339 , 'baseDeviceSecurityPolicy':327 , 'belongsTo':262 , 'bias':14 , 'bitMask':342 , 'bitText':343 , 'blinkWarnEnable':373 , 'bufferSize':126 , 'changeOfStateCount':15 , 'changeOfStateTime':16 , 'channelNumber':366 , 'clientCovIncrement':127 , 'configurationFiles':154 , 'controlGroups':367 , 'controlledVariableReference':19 , 'controlledVariableUnits':20 , 'controlledVariableValue':21 , 'count':177 , 'countBeforeChange':178 , 'countChangeTime':179 , 'covIncrement':22 , 'covPeriod':180 , 'covResubscriptionInterval':128 , 'covuPeriod':349 , 'covuRecipients':350 , 'credentialDisable':263 , 'credentialStatus':264 , 'credentials':265 , 'credentialsInZone':266 , 'databaseRevision':155 , 'dateList':23 , 'daylightSavingsStatus':24 , 'daysRemaining':267 , 'deadband':25 , 'defaultFadeTime':374 , 'defaultRampRate':375 , 'defaultStepIncrement':376 , 'derivativeConstant':26 , 'derivativeConstantUnits':27 , 'description':28 , 'descriptionOfHalt':29 , 'deviceAddressBinding':30 , 'deviceType':31 , 'directReading':156 , 'distributionKeyRevision':328 , 'doNotHide':329 , 'doorAlarmState':226 , 'doorExtendedPulseTime':227 , 'doorMembers':228 , 'doorOpenTooLongTime':229 , 'doorPulseTime':230 , 'doorStatus':231 , 'doorUnlockDelayTime':232 , 'dutyWindow':213 , 'effectivePeriod':32 , 'egressActive':386 , 'egressTime':377 , 'elapsedActiveTime':33 , 'entryPoints':268 , 'enable':133 , 'errorLimit':34 , 'eventAlgorithmInhibit':354 , 'eventAlgorithmInhibitRef':355 , 'eventDetectionEnable':353 , 'eventEnable':35 , 'eventMessageTexts':351 , 'eventMessageTextsConfig':352 , 'eventState':36 , 'eventTimeStamps':130 , 'eventType':37 , 'eventParameters':83 , 'exceptionSchedule':38 , 'executionDelay':368 , 'exitPoints':269 , 'expectedShedLevel':214 , 'expiryTime':270 , 'extendedTimeEnable':271 , 'failedAttemptEvents':272 , 'failedAttempts':273 , 'failedAttemptsTime':274 , 'faultParameters':358 , 'faultType':359 , 'faultValues':39 , 'feedbackValue':40 , 'fileAccessMethod':41 , 'fileSize':42 , 'fileType':43 , 'firmwareRevision':44 , 'fullDutyBaseline':215 , 'globalIdentifier':323 , 'groupMembers':345 , 'groupMemberNames':346 , 'highLimit':45 , 'inactiveText':46 , 'inProcess':47 , 'inProgress':378 , 'inputReference':181 , 'instanceOf':48 , 'instantaneousPower':379 , 'integralConstant':49 , 'integralConstantUnits':50 , 'intervalOffset':195 , 'isUtc':344 , 'keySets':330 , 'lastAccessEvent':275 , 'lastAccessPoint':276 , 'lastCredentialAdded':277 , 'lastCredentialAddedTime':278 , 'lastCredentialRemoved':279 , 'lastCredentialRemovedTime':280 , 'lastKeyServer':331 , 'lastNotifyRecord':173 , 'lastPriority':369 , 'lastRestartReason':196 , 'lastRestoreTime':157 , 'lastUseTime':281 , 'lifeSafetyAlarmValues':166 , 'lightingCommand':380 , 'lightingCommandDefaultPriority':381 , 'limitEnable':52 , 'limitMonitoringInterval':182 , 'listOfGroupMembers':53 , 'listOfObjectPropertyReferences':54 , 'listOfSessionKeys':55 , 'localDate':56 , 'localForwardingOnly':360 , 'localTime':57 , 'location':58 , 'lockStatus':233 , 'lockout':282 , 'lockoutRelinquishTime':283 , 'logBuffer':131 , 'logDeviceObjectProperty':132 , 'logInterval':134 , 'loggingObject':183 , 'loggingRecord':184 , 'loggingType':197 , 'lowLimit':59 , 'maintenanceRequired':158 , 'manipulatedVariableReference':60 , 'manualSlaveAddressBinding':170 , 'maskedAlarmValues':234 , 'masterExemption':284 , 'maximumOutput':61 , 'maximumValue':135 , 'maximumValueTimestamp':149 , 'maxActualValue':382 , 'maxApduLengthAccepted':62 , 'maxFailedAttempts':285 , 'maxInfoFrames':63 , 'maxMaster':64 , 'maxPresValue':65 , 'maxSegmentsAccepted':167 , 'memberOf':159 , 'memberStatusFlags':347 , 'members':286 , 'minimumOffTime':66 , 'minimumOnTime':67 , 'minimumOutput':68 , 'minimumValue':136 , 'minimumValueTimestamp':150 , 'minActualValue':383 , 'minPresValue':69 , 'mode':160 , 'modelName':70 , 'modificationDate':71 , 'musterPoint':287 , 'negativeAccessRules':288 , 'networkAccessSecurityPolicies':332 , 'nodeSubtype':207 , 'nodeType':208 , 'notificationClass':17 , 'notificationThreshold':137 , 'notifyType':72 , 'numberOfApduRetries':73 , 'numberOfAuthenticationPolicies':289 , 'numberOfStates':74 , 'objectIdentifier':75 , 'objectList':76 , 'objectName':77 , 'objectPropertyReference':78 , 'objectType':79 , 'occupancyCount':290 , 'occupancyCountAdjust':291 , 'occupancyCountEnable':292 , 'occupancyExemption':293 , 'occupancyLowerLimit':294 , 'occupancyLowerLimitEnforced':295 , 'occupancyState':296 , 'occupancyUpperLimit':297 , 'occupancyUpperLimitEnforced':298 , 'operationExpected':161 , 'optional':80 , 'outOfService':81 , 'outputUnits':82 , 'packetReorderTime':333 , 'passbackExemption':299 , 'passbackMode':300 , 'passbackTimeout':301 , 'polarity':84 , 'portFilter':363 , 'positiveAccessRules':302 , 'power':384 , 'prescale':185 , 'presentValue':85 , 'priority':86 , 'priorityArray':87 , 'priorityForWriting':88 , 'processIdentifier':89 , 'processIdentifierFilter':361 , 'profileName':168 , 'programChange':90 , 'programLocation':91 , 'programState':92 , 'propertyList':371 , 'proportionalConstant':93 , 'proportionalConstantUnits':94 , 'protocolObjectTypesSupported':96 , 'protocolRevision':139 , 'protocolServicesSupported':97 , 'protocolVersion':98 , 'pulseRate':186 , 'readOnly':99 , 'reasonForDisable':303 , 'reasonForHalt':100 , 'recipientList':102 , 'recordsSinceNotification':140 , 'recordCount':141 , 'reliability':103 , 'reliabilityEvaluationInhibit':357 , 'relinquishDefault':104 , 'requestedShedLevel':218 , 'requestedUpdateInterval':348 , 'required':105 , 'resolution':106 , 'restartNotificationRecipients':202 , 'restoreCompletionTime':340 , 'restorePreparationTime':341 , 'scale':187 , 'scaleFactor':188 , 'scheduleDefault':174 , 'securedStatus':235 , 'securityPDUTimeout':334 , 'securityTimeWindow':335 , 'segmentationSupported':107 , 'serialNumber':372 , 'setpoint':108 , 'setpointReference':109 , 'setting':162 , 'shedDuration':219 , 'shedLevelDescriptions':220 , 'shedLevels':221 , 'silenced':163 , 'slaveAddressBinding':171 , 'slaveProxyEnable':172 , 'startTime':142 , 'stateDescription':222 , 'stateText':110 , 'statusFlags':111 , 'stopTime':143 , 'stopWhenFull':144 , 'structuredObjectList':209 , 'subordinateAnnotations':210 , 'subordinateList':211 , 'subscribedRecipients':362 , 'supportedFormats':304 , 'supportedFormatClasses':305 , 'supportedSecurityAlgorithms':336 , 'systemStatus':112 , 'threatAuthority':306 , 'threatLevel':307 , 'timeDelay':113 , 'timeDelayNormal':356 , 'timeOfActiveTimeReset':114 , 'timeOfDeviceRestart':203 , 'timeOfStateCountReset':115 , 'timeSynchronizationInterval':204 , 'timeSynchronizationRecipients':116 , 'totalRecordCount':145 , 'traceFlag':308 , 'trackingValue':164 , 'transactionNotificationClass':309 , 'transition':385 , 'trigger':205 , 'units':117 , 'updateInterval':118 , 'updateKeySetTimeout':337 , 'updateTime':189 , 'userExternalIdentifier':310 , 'userInformationReference':311 , 'userName':317 , 'userType':318 , 'usesRemaining':319 , 'utcOffset':119 , 'utcTimeSynchronizationRecipients':206 , 'validSamples':146 , 'valueBeforeChange':190 , 'valueSet':191 , 'valueChangeTime':192 , 'varianceValue':151 , 'vendorIdentifier':120 , 'vendorName':121 , 'verificationTime':326 , 'vtClassesSupported':122 , 'weeklySchedule':123 , 'windowInterval':147 , 'windowSamples':148 , 'writeStatus':370 , 'zoneFrom':320 , 'zoneMembers':165 , 'zoneTo':321 } class Reliability(Enumerated): vendor_range = (64, 65535) enumerations = \ { 'noFaultDetected':0 , 'noSensor':1 , 'overRange':2 , 'underRange':3 , 'openLoop':4 , 'shortedLoop':5 , 'noOutput':6 , 'unreliableOther':7 , 'processError':8 , 'multiStateFault':9 , 'configurationError':10 , 'communicationFailure':12 , 'numberFault':13 } class RestartReason(Enumerated): vendor_range = (64, 255) enumerations = \ { 'unknown':0 , 'coldstart':1 , 'warmstart':2 , 'detectedPowerLost':3 , 'detectedPoweredOff':4 , 'hardwareWatchdog':5 , 'softwareWatchdog':6 , 'suspended':7 } class SecurityLevel(Enumerated): enumerations = \ { 'incapable':0 , 'plain':1 , 'signed':2 , 'encrypted':3 , 'signedEndToEnd':4 , 'encryptedEndToEnd':4 } class SecurityPolicy(Enumerated): enumerations = \ { 'plainNonTrusted':0 , 'plainTrusted':1 , 'signedTrusted':2 , 'encryptedTrusted':3 } class ShedState(Enumerated): enumerations = \ { 'shedInactive':0 , 'shedRequestPending':1 , 'shedCompliant':2 , 'shedNonCompliant':3 } class Segmentation(Enumerated): enumerations = \ { 'segmentedBoth':0 , 'segmentedTransmit':1 , 'segmentedReceive':2 , 'noSegmentation':3 } class SilencedState(Enumerated): vendor_range = (64, 65535) enumerations = \ { 'unsilenced':0 , 'audibleSilenced':1 , 'visibleSilenced':2 , 'allSilenced':3 } class VTClass(Enumerated): vendor_range = (64, 65535) enumerations = \ { 'defaultTerminal':0 , 'ansiX3-64':1 , 'decVt52':2 , 'decVt100':3 , 'decVt220':4 , 'hp-700-94':5 , 'ibm-3130':6 } class WriteStatus(Enumerated): enumerations = \ { 'idle':0 , 'inProgress':1 , 'successful':2 , 'failed':3 } # # Forward Sequences # class DeviceAddress(Sequence): sequenceElements = \ [ Element('networkNumber', Unsigned) , Element('macAddress', OctetString) ] class DeviceObjectPropertyReference(Sequence): sequenceElements = \ [ Element('objectIdentifier', ObjectIdentifier, 0) , Element('propertyIdentifier', PropertyIdentifier, 1) , Element('propertyArrayIndex', Unsigned, 2, True) , Element('deviceIdentifier', ObjectIdentifier, 3, True) ] class DeviceObjectReference(Sequence): sequenceElements = \ [ Element('deviceIdentifier', ObjectIdentifier, 0, True) , Element('objectIdentifier', ObjectIdentifier, 1) ] class DateTime(Sequence): sequenceElements = \ [ Element('date', Date) , Element('time', Time) ] class DateRange(Sequence): sequenceElements = \ [ Element('startDate', Date) , Element('endDate', Date) ] class ErrorType(Sequence): sequenceElements = \ [ Element('errorClass', ErrorClass) , Element('errorCode', ErrorCode) ] class ObjectPropertyReference(Sequence): sequenceElements = \ [ Element('objectIdentifier', ObjectIdentifier, 0) , Element('propertyIdentifier', PropertyIdentifier, 1) , Element('propertyArrayIndex', Unsigned, 2, True) ] class ProcessIdSelection(Choice): choiceElements = \ [ Element('processIdentifier', Unsigned) , Element('nullValue', Null) ] class PropertyStates(Choice): vendor_range = (64, 254) choiceElements = \ [ Element('booleanValue', Boolean, 0) , Element('binaryValue', BinaryPV, 1) , Element('eventType', EventType, 2) , Element('polarity', Polarity, 3) , Element('programChange', ProgramRequest, 4) , Element('programState', ProgramState, 5) , Element('reasonForHalt', ProgramError, 6) , Element('reliability', Reliability, 7) , Element('state', EventState, 8) , Element('systemStatus', DeviceStatus, 9) , Element('units', EngineeringUnits, 10) , Element('unsignedValue', Unsigned, 11) , Element('lifeSafetyMode', LifeSafetyMode, 12) , Element('lifeSafetyState', LifeSafetyState, 13) , Element('restartReason', RestartReason, 14) , Element('doorAlarmState', DoorAlarmState, 15) , Element('action', Action, 16) , Element('doorSecuredStatus', DoorSecuredStatus, 17) , Element('doorStatus', DoorStatus, 18) , Element('doorValue', DoorValue, 19) , Element('fileAccessMethod', FileAccessMethod, 20) , Element('lockStatus', LockStatus, 21) , Element('lifeSafetyOperation', LifeSafetyOperation, 22) , Element('maintenance', Maintenance, 23) , Element('nodeType', NodeType, 24) , Element('notifyType', NotifyType, 25) , Element('securityLevel', SecurityLevel, 26) , Element('shedState', ShedState, 27) , Element('silencedState', SilencedState, 28) , Element('accessEvent', AccessEvent, 30) , Element('zoneOccupancyState', AccessZoneOccupancyState, 31) , Element('accessCredentialDisableReason', AccessCredentialDisableReason, 32) , Element('accessCredentialDisable', AccessCredentialDisable, 33) , Element('authenticationStatus', AuthenticationStatus, 34) , Element('backupState', BackupState, 36) , Element('writeStatus', WriteStatus, 370) , Element('lightingInProgress', LightingInProgress, 38) , Element('lightingOperation', LightingOperation, 39) , Element('lightingTransition', LightingTransition, 40) ] class PropertyValue(Sequence): sequenceElements = \ [ Element('propertyIdentifier', PropertyIdentifier, 0) , Element('propertyArrayIndex', Unsigned, 1, True) , Element('value', Any, 2) , Element('priority', Unsigned, 3, True) ] class Recipient(Choice): choiceElements = \ [ Element('device', ObjectIdentifier, 0) , Element('address', DeviceAddress, 1) ] class RecipientProcess(Sequence): sequenceElements = \ [ Element('recipient', Recipient, 0) , Element('processIdentifier', Unsigned, 1) ] class TimeStamp(Choice): choiceElements = \ [ Element('time', Time, 0) , Element('sequenceNumber', Unsigned, 1) , Element('dateTime', DateTime, 2) ] class TimeValue(Sequence): sequenceElements = \ [ Element('time', Time) , Element('value', AnyAtomic) ] class WeekNDay(OctetString): def __str__(self): if len(self.value) != 3: return "WeekNDay(?): " + OctetString.__str__(self) else: return "WeekNDay(%d, %d, %d)" % (ord(self.value[0]), ord(self.value[1]), ord(self.value[2])) # # Sequences # class AccessRule(Sequence): sequenceElements = \ [ Element('timeRangeSpecifier', AccessRuleTimeRangeSpecifier, 0) , Element('timeRange', DeviceObjectPropertyReference, 1, True) , Element('locationSpecifier', AccessRuleLocationSpecifier, 2) , Element('location', DeviceObjectReference, 3, True) , Element('enable', Boolean, 4) ] class AccessThreatLevel(Unsigned): pass class AccumulatorRecord(Sequence): sequenceElements = \ [ Element('timestamp', DateTime, 0) , Element('presentValue', Unsigned, 1) , Element('accumulatedValue', Unsigned, 2) , Element('accumulatorStatus', AccumulatorRecordAccumulatorStatus, 3) ] class ActionCommand(Sequence): sequenceElements = \ [ Element('deviceIdentifier', ObjectIdentifier, 0, True) , Element('objectIdentifier', ObjectIdentifier, 1) , Element('propertyIdentifier', PropertyIdentifier, 2) , Element('propertyArrayIndex', Unsigned, 3, True) , Element('propertyValue', Any, 4) , Element('priority', Unsigned, 5, True) , Element('postDelay', Unsigned, 6, True) , Element('quiteOnFailure', Boolean, 7) , Element('writeSuccessFul', Boolean, 8) ] class ActionList(Sequence): sequenceElements = \ [ Element('action', SequenceOf(ActionCommand), 0) ] class AddressBinding(Sequence): sequenceElements = \ [ Element('deviceObjectIdentifier', ObjectIdentifier) , Element('deviceAddress', DeviceAddress) ] class AssignedAccessRights(Sequence): serviceChoice = 15 sequenceElements = \ [ Element('assignedAccessRights', DeviceObjectReference, 0) , Element('enable', Boolean, 1) ] class AuthenticationFactor(Sequence): sequenceElements = \ [ Element('formatType', AuthenticationFactorType, 0) , Element('formatClass', Unsigned, 1) , Element('value', OctetString, 2) ] class AuthenticationFactorFormat(Sequence): sequenceElements = \ [ Element('formatType', AuthenticationFactorType, 0) , Element('vendorId', Unsigned, 1, True) , Element('vendorFormat', Unsigned, 2, True) ] class AuthenticationPolicyPolicy(Sequence): sequenceElements = \ [ Element('credentialDataInput', DeviceObjectReference, 0) , Element('index', Unsigned, 1) ] class AuthenticationPolicy(Sequence): sequenceElements = \ [ Element('policy', SequenceOf(AuthenticationPolicyPolicy), 0) , Element('orderEnforced', Boolean, 1) , Element('timeout', Unsigned, 2) ] class CalendarEntry(Choice): choiceElements = \ [ Element('date', Date, 0) , Element('dateRange', DateRange, 1) , Element('weekNDay', WeekNDay, 2) ] class ChannelValue(Choice): choiceElements = [ ### needs help ] class ClientCOV(Choice): choiceElements = \ [ Element('realIncrement', Real) , Element('defaultIncrement', Null) ] class COVSubscription(Sequence): sequenceElements = \ [ Element('recipient', RecipientProcess, 0) , Element('monitoredPropertyReference', ObjectPropertyReference, 1) , Element('issueConfirmedNotifications', Boolean, 2) , Element('timeRemaining', Unsigned, 3) , Element('covIncrement', Real, 4, True) ] class CredentialAuthenticationFactor(Sequence): sequenceElements = \ [ Element('disable', AccessAuthenticationFactorDisable, 0) , Element('authenticationFactor', AuthenticationFactor, 1) ] class DailySchedule(Sequence): sequenceElements = \ [ Element('daySchedule', SequenceOf(TimeValue), 0) ] class Destination(Sequence): sequenceElements = \ [ Element('validDays', DaysOfWeek) , Element('fromTime', Time) , Element('toTime', Time) , Element('recipient', Recipient) , Element('processIdentifier', Unsigned) , Element('issueConfirmedNotifications', Boolean) , Element('transitions', EventTransitionBits) ] class DeviceObjectPropertyValue(Sequence): sequenceElements = \ [ Element('deviceIdentifier', ObjectIdentifier, 0) , Element('objectIdentifier', ObjectIdentifier, 1) , Element('propertyIdentifier', PropertyIdentifier, 2) , Element('arrayIndex', Unsigned, 3, True) , Element('value', Any, 4) ] class EventNotificationSubscription(Sequence): sequenceElements = \ [ Element('recipient', Recipient, 0) , Element('processIdentifier', Unsigned, 1) , Element('issueConfirmedNotifications', Boolean, 2) , Element('timeRemaining', Unsigned, 3) ] class EventParameterChangeOfBitstring(Sequence): sequenceElements = \ [ Element('timeDelay', Unsigned, 0) , Element('bitmask', BitString, 1) , Element('listOfBitstringValues', SequenceOf(BitString), 2) ] class EventParameterChangeOfState(Sequence): sequenceElements = \ [ Element('timeDelay', Unsigned, 0) , Element('listOfValues', SequenceOf(PropertyStates), 1) ] class EventParameterChangeOfValueCOVCriteria(Choice): choiceElements = \ [ Element('bitmask', BitString, 0) , Element('referencedPropertyIncrement', Real, 1) ] class EventParameterChangeOfValue(Sequence): sequenceElements = \ [ Element('timeDelay', Unsigned, 0) , Element('covCriteria', EventParameterChangeOfValueCOVCriteria, 1) ] class EventParameterCommandFailure(Sequence): sequenceElements = \ [ Element('timeDelay', Unsigned, 0) , Element('feedbackPropertyReference', DeviceObjectPropertyReference, 1) ] class EventParameterFloatingLimit(Sequence): sequenceElements = \ [ Element('timeDelay', Unsigned, 0) , Element('setpointReference', DeviceObjectPropertyReference, 1) , Element('lowDiffLimit', Real, 2) , Element('highDiffLimit', Real, 3) , Element('deadband', Real, 4) ] class EventParameterOutOfRange(Sequence): sequenceElements = \ [ Element('timeDelay', Unsigned, 0) , Element('lowLimit', Real, 1) , Element('highLimit', Real, 2) , Element('deadband', Real, 3) ] class EventParameterChangeOfLifeSafety(Sequence): sequenceElements = \ [ Element('timeDelay', Unsigned, 0) , Element('listOfLifeSafetyAlarmValues', SequenceOf(LifeSafetyState), 1) , Element('listOfAlarmValues', SequenceOf(LifeSafetyState), 2) , Element('modePropertyReference', DeviceObjectPropertyReference, 3) ] class EventParameterExtendedParameters(Choice): choiceElements = \ [ Element('null', Null, 0) , Element('real', Real, 1) , Element('integer', Unsigned, 2) , Element('boolean', Boolean, 3) , Element('double', Double, 4) , Element('octet', OctetString, 5) , Element('bitstring', BitString, 6) , Element('enum', Enumerated, 7) , Element('reference', DeviceObjectPropertyReference, 8) ] class EventParameterExtended(Sequence): sequenceElements = \ [ Element('vendorId', Unsigned, 0) , Element('extendedEventType', Unsigned, 1) , Element('parameters', SequenceOf(EventParameterExtendedParameters), 2) ] class EventParameterBufferReady(Sequence): sequenceElements = \ [ Element('notificationThreshold', Unsigned, 0) , Element('previousNotificationCount', Unsigned, 1) ] class EventParameterUnsignedRange(Sequence): sequenceElements = \ [ Element('timeDelay', Unsigned, 0) , Element('lowLimit', Unsigned, 1) , Element('highLimit', Unsigned, 2) ] class EventParameterAccessEventAccessEvent(Sequence): sequenceevents = \ [ Element('listOfAccessEvents', SequenceOf(AccessEvent), 0) , Element('accessEventTimeReference', DeviceObjectPropertyReference, 0) ] class EventParameterAccessEvent(Sequence): sequenceElements = \ [ Element('accessEvent', SequenceOf(EventParameterAccessEventAccessEvent), 0) ] class EventParameterDoubleOutOfRange(Sequence): sequenceElements = \ [ Element('timeDelay', Unsigned, 0) , Element('lowLimit', Double, 1) , Element('highLimit', Double, 2) , Element('deadband', Double, 3) ] class EventParameterSignedOutOfRange(Sequence): sequenceElements = \ [ Element('timeDelay', Unsigned, 0) , Element('lowLimit', Integer, 1) , Element('highLimit', Integer, 2) , Element('deadband', Unsigned, 3) ] class EventParameterUnsignedOutOfRange(Sequence): sequenceElements = \ [ Element('timeDelay', Unsigned, 0) , Element('lowLimit', Unsigned, 1) , Element('highLimit', Unsigned, 2) , Element('deadband', Unsigned, 3) ] class EventParameterChangeOfCharacterString(Sequence): sequenceElements = \ [ Element('timeDelay', Unsigned, 0) , Element('listOfAlarmValues', SequenceOf(CharacterString), 1) ] class EventParameterChangeOfStatusFlags(Sequence): sequenceElements = \ [ Element('timeDelay', Unsigned, 0) , Element('selectedFlags', StatusFlags, 1) ] class EventParameter(Choice): choiceElements = \ [ Element('changeOfBitstring', EventParameterChangeOfBitstring, 0) , Element('changeOfState', EventParameterChangeOfState, 1) , Element('changeOfValue', EventParameterChangeOfValue, 2) , Element('commandFailure', EventParameterCommandFailure, 3) , Element('floatingLimit', EventParameterFloatingLimit, 4) , Element('outOfRange', EventParameterOutOfRange, 5) , Element('changeOfLifesafety', EventParameterChangeOfLifeSafety, 8) , Element('extended', EventParameterExtended, 9) , Element('bufferReady', EventParameterBufferReady, 10) , Element('unsignedRange', EventParameterUnsignedRange, 11) , Element('accessEvent', EventParameterAccessEvent, 13) , Element('doubleOutOfRange', EventParameterDoubleOutOfRange, 14) , Element('signedOutOfRange', EventParameterSignedOutOfRange, 15) , Element('unsignedOutOfRange', EventParameterUnsignedOutOfRange, 16) , Element('changeOfCharacterstring', EventParameterChangeOfCharacterString, 17) , Element('changeOfStatusflags', EventParameterChangeOfStatusFlags, 18) ] class FaultParameterCharacterString(Sequence): sequenceElements = \ [ Element('listOfFaultValues', SequenceOf(CharacterString), 0) ] class FaultParameterExtendedParameters(Choice): choiceElements = \ [ Element('null', Null) , Element('real', Real) , Element('unsigned', Unsigned) , Element('boolean', Boolean) , Element('integer', Integer) , Element('double', Double) , Element('octet', OctetString) , Element('characterString', CharacterString) , Element('bitstring', BitString) , Element('enum', Enumerated) , Element('date', Date) , Element('time', Time) , Element('objectIdentifier', ObjectIdentifier) , Element('reference', DeviceObjectPropertyReference, 0) ] class FaultParameterExtended(Sequence): sequenceElements = \ [ Element('vendorId', Unsigned, 0) , Element('extendedFaultType', Unsigned, 1) , Element('parameters', SequenceOf(FaultParameterExtendedParameters), 2) ] class FaultParameterLifeSafety(Sequence): sequenceElements = \ [ Element('listOfFaultValues', SequenceOf(LifeSafetyState), 0) , Element('modePropertyReference', DeviceObjectPropertyReference, 1) ] class FaultParameterState(Sequence): sequenceElements = \ [ Element('listOfFaultValues', SequenceOf(PropertyStates), 0) ] class FaultParameterStatusFlags(Sequence): sequenceElements = \ [ Element('statusFlagsReference', DeviceObjectPropertyReference, 0) ] class FaultParameter(Choice): choiceElements = \ [ Element('none', Null, 0) , Element('faultCharacterString', FaultParameterCharacterString, 1) , Element('faultExtended', FaultParameterExtended, 2) , Element('faultLifeSafety', FaultParameterLifeSafety, 3) , Element('faultState', FaultParameterState, 4) , Element('faultStatusFlags', FaultParameterStatusFlags, 5) ] class KeyIdentifier(Sequence): sequenceElements = \ [ Element('algorithm', Unsigned, 0) , Element('keyId', Unsigned, 1) ] class LightingCommand(Sequence): sequenceElements = \ [ Element('operation', LightingOperation, 0) , Element('targetLevel', Real, 1) ### optional , Element('rampRate', Real, 2) ### optional , Element('stepIncrement', Real, 3) ### optional , Element('fadeTime', Unsigned, 4) ### optional , Element('priority', Unsigned, 5) ### optional ] class LogDataLogData(Choice): choiceElements = \ [ Element('booleanValue', Boolean, 0) , Element('realValue', Real, 1) , Element('enumValue', Enumerated, 2) , Element('unsignedValue', Unsigned, 3) , Element('signedValue', Integer, 4) , Element('bitstringValue', BitString, 5) , Element('nullValue', Null, 6) , Element('failure', ErrorType, 7) , Element('anyValue', Any, 8) ] class LogData(Choice): choiceElements = \ [ Element('logStatus', LogStatus, 0) , Element('logData', SequenceOf(LogDataLogData), 1) , Element('timeChange', Real, 2) ] class LogMultipleRecord(Sequence): sequenceElements = \ [ Element('timestamp', DateTime, 0) , Element('logData', LogData, 1) ] class LogRecordLogDatum(Choice): choiceElements = \ [ Element('logStatus', LogStatus, 0) , Element('booleanValue', Boolean, 1) , Element('realValue', Real, 2) , Element('enumValue', Enumerated, 3) , Element('unsignedValue', Unsigned, 4) , Element('signedValue', Integer, 5) , Element('bitstringValue', BitString, 6) , Element('nullValue', Null, 7) , Element('failure', ErrorType, 8) , Element('timeChange', Real, 9) , Element('anyValue', Any, 10) ] class LogRecord(Sequence): sequenceElements = \ [ Element('timestamp', DateTime, 0) , Element('logDatum', LogRecordLogDatum, 1) , Element('statusFlags', StatusFlags, 2, True) ] class NetworkSecurityPolicy(Sequence): sequenceElements = \ [ Element('portId', Unsigned, 0) , Element('securityLevel', SecurityPolicy, 1) ] class NotificationParametersChangeOfBitstring(Sequence): sequenceElements = \ [ Element('referencedBitstring', BitString, 0) , Element('statusFlags', StatusFlags, 1) ] class NotificationParametersChangeOfState(Sequence): sequenceElements = \ [ Element('newState', PropertyStates, 0) , Element('statusFlags', StatusFlags, 1) ] class NotificationParametersChangeOfValueNewValue(Choice): choiceElements = \ [ Element('changedBits', BitString, 0) , Element('changedValue', Real, 1) ] class NotificationParametersChangeOfValue(Sequence): sequenceElements = \ [ Element('newValue', NotificationParametersChangeOfValueNewValue, 0) , Element('statusFlags', StatusFlags, 1) ] class NotificationParametersCommandFailure(Sequence): sequenceElements = \ [ Element('commandValue', Any, 0) , Element('statusFlags', StatusFlags, 1) , Element('feedbackValue', Any, 2) ] class NotificationParametersFloatingLimit(Sequence): sequenceElements = \ [ Element('referenceValue', Real, 0) , Element('statusFlags', StatusFlags, 1) , Element('setpointValue', Real, 2) , Element('errorLimit', Real, 3) ] class NotificationParametersOutOfRange(Sequence): sequenceElements = \ [ Element('exceedingValue', Real, 0) , Element('statusFlags', StatusFlags, 1) , Element('deadband', Real, 2) , Element('exceededLimit', Real, 3) ] class NotificationParametersExtendedParametersType(Choice): choiceElements = \ [ Element('null', Null) , Element('real', Real) , Element('integer', Unsigned) , Element('boolean', Boolean) , Element('double', Double) , Element('octet', OctetString) , Element('bitstring', BitString) , Element('enum', Enumerated) , Element('propertyValue', DeviceObjectPropertyValue) ] class NotificationParametersExtended(Sequence): sequenceElements = \ [ Element('vendorId', Unsigned, 0) , Element('extendedEventType', Unsigned, 1) , Element('parameters', NotificationParametersExtendedParametersType, 2) ] class NotificationParametersBufferReady(Sequence): sequenceElements = \ [ Element('bufferProperty', DeviceObjectPropertyReference, 0) , Element('previousNotification', Unsigned, 1) , Element('currentNotification', Unsigned, 2) ] class NotificationParametersUnsignedRange(Sequence): sequenceElements = \ [ Element('exceedingValue', Unsigned, 0) , Element('statusFlags', StatusFlags, 1) , Element('exceedingLimit', Unsigned, 2) ] class NotificationParametersComplexEventType(Sequence): sequenceElements = \ [ Element('complexEventType', PropertyValue, 0) ] class NotificationParametersChangeOfLifeSafety(Sequence): sequenceElements = \ [ Element('newState', LifeSafetyState, 0) , Element('newMode', LifeSafetyMode, 1) , Element('statusFlags',StatusFlags, 2) , Element('operationExpected', LifeSafetyOperation, 3) ] class NotificationParametersAccessEventType(Sequence): sequenceElements = \ [ Element('accessEvent', AccessEvent, 0) , Element('statusFlags', StatusFlags, 1) , Element('accessEventTag', Unsigned, 2) , Element('accessEventTime', TimeStamp, 3) , Element('accessCredential', DeviceObjectReference, 4) , Element('authenicationFactor', AuthenticationFactorType, 5, True) ] class NotificationParametersDoubleOutOfRangeType(Sequence): sequenceElements = \ [ Element('exceedingValue', Double, 0) , Element('statusFlags', StatusFlags, 1) , Element('deadband', Double, 2) , Element('exceededLimit', Double, 3) ] class NotificationParametersSignedOutOfRangeType(Sequence): sequenceElements = \ [ Element('exceedingValue', Integer, 0) , Element('statusFlags', StatusFlags, 1) , Element('deadband', Unsigned, 2) , Element('exceededLimit', Integer, 3) ] class NotificationParametersUnsignedOutOfRangeType(Sequence): sequenceElements = \ [ Element('exceedingValue', Unsigned, 0) , Element('statusFlags', StatusFlags, 1) , Element('deadband', Unsigned, 2) , Element('exceededLimit', Unsigned, 3) ] class NotificationParametersChangeOfCharacterStringType(Sequence): sequenceElements = \ [ Element('changedValue', CharacterString, 0) , Element('statusFlags', StatusFlags, 1) , Element('alarmValue', CharacterString, 2) ] class NotificationParametersChangeOfStatusFlagsType(Sequence): sequenceElements = \ [ Element('presentValue', CharacterString, 0) , Element('referencedFlags', StatusFlags, 1) ] class NotificationParameters(Choice): choiceElements = \ [ Element('changeOfBitstring', NotificationParametersChangeOfBitstring, 0) , Element('changeOfState', NotificationParametersChangeOfState, 1) , Element('changeOfValue', NotificationParametersChangeOfValue, 2) , Element('commandFailure', NotificationParametersCommandFailure, 3) , Element('floatingLimit', NotificationParametersFloatingLimit, 4) , Element('outOfRange', NotificationParametersOutOfRange, 5) , Element('complexEventType', NotificationParametersComplexEventType, 6) , Element('changeOfLifeSafety', NotificationParametersChangeOfLifeSafety, 8) , Element('extended', NotificationParametersExtended, 9) , Element('bufferReady', NotificationParametersBufferReady, 10) , Element('unsignedRange', NotificationParametersUnsignedRange, 11) , Element('accessEvent', NotificationParametersAccessEventType, 13) , Element('doubleOutOfRange', NotificationParametersDoubleOutOfRangeType, 14) , Element('signedOutOfRange', NotificationParametersSignedOutOfRangeType, 15) , Element('unsignedOutOfRange', NotificationParametersUnsignedOutOfRangeType, 16) , Element('changeOfCharacterString', NotificationParametersChangeOfCharacterStringType, 17) , Element('changeOfStatusFlags', NotificationParametersChangeOfStatusFlagsType, 18) ] class ObjectPropertyValue(Sequence): sequenceElements = \ [ Element('objectIdentifier', ObjectIdentifier, 0) , Element('propertyIdentifier', PropertyIdentifier, 1) , Element('propertyArrayIndex', Unsigned, 2, True) , Element('value', Any, 3) , Element('priority', Unsigned, 4, True) ] class OptionalCharacterString(Choice): choiceElements = \ [ Element('null', Null) , Element('characterString', CharacterString) ] class PortPermission(Sequence): sequenceElements = \ [ Element('portId', Unsigned, 0) , Element('enabled', Boolean, 1) ] class Prescale(Sequence): sequenceElements = \ [ Element('multiplier', Unsigned, 0) , Element('moduloDivide', Unsigned, 1) ] class PriorityValue(Choice): choiceElements = \ [ Element('null', Null) , Element('real', Real) , Element('enumerated', Enumerated) , Element('unsigned', Unsigned) , Element('boolean', Boolean) , Element('signed', Integer) , Element('double', Double) , Element('time', Time) , Element('characterString', CharacterString) , Element('octetString', OctetString) , Element('bitString', BitString) , Element('date', Date) , Element('objectid', ObjectIdentifier) , Element('constructedValue', Any, 0) , Element('datetime', DateTime, 1) ] class PriorityArray(ArrayOf(PriorityValue)): pass class PropertyAccessResultAccessResult(Choice): choiceElements = \ [ Element('propertyValue', Any, 4) , Element('propertyAccessError', ErrorType, 5) ] class PropertyAccessResult(Sequence): sequenceElements = \ [ Element('objectIdentifier', ObjectIdentifier, 0) , Element('propertyIdentifier', PropertyIdentifier, 1) , Element('propertyArrayIndex', Unsigned, 2, True) , Element('deviceIdentifier', ObjectIdentifier, 3, True) , Element('accessResult', PropertyAccessResultAccessResult) ] class PropertyReference(Sequence): sequenceElements = \ [ Element('propertyIdentifier', PropertyIdentifier, 0) , Element('propertyArrayIndex', Unsigned, 1, True) ] class Scale(Choice): choiceElements = \ [ Element('floatScale', Real) , Element('integerScale', Integer) ] class SecurityKeySet(Sequence): sequenceElements = \ [ Element('keyRevision', Unsigned, 0) , Element('activationTime', DateTime, 1) , Element('expirationTime', DateTime, 2) , Element('keyIds', SequenceOf(KeyIdentifier), 3) ] class ShedLevel(Choice): choiceElements = \ [ Element('percent', Unsigned, 0) , Element('level', Unsigned, 1) , Element('amount', Real, 2) ] class SetpointReference(Sequence): sequenceElements = \ [ Element('setpointReference', ObjectPropertyReference, 0, True) ] class SpecialEventPeriod(Choice): choiceElements = \ [ Element('calendarEntry', CalendarEntry, 0) , Element('calendarReference', ObjectIdentifier, 1) ] class SpecialEvent(Sequence): sequenceElements = \ [ Element('period', SpecialEventPeriod) , Element('listOfTimeValues', SequenceOf(TimeValue), 2) , Element('eventPriority', Unsigned, 3) ] class VTSession(Sequence): sequenceElements = \ [ Element('localVtSessionID', Unsigned) , Element('remoteVtSessionID', Unsigned) , Element('remoteVtAddress', DeviceAddress) ] PKH$yybacpypes/bvllservice.py#!/usr/bin/python """ BACnet Virtual Link Layer Service """ import sys import struct from time import time as _time from .debugging import ModuleLogger, DebugContents, bacpypes_debugging from .udp import UDPDirector from .task import OneShotTask, RecurringTask from .comm import Client, Server, bind, \ ServiceAccessPoint, ApplicationServiceElement from .pdu import Address, LocalBroadcast, LocalStation, PDU, \ unpack_ip_addr from .bvll import BVLPDU, DeleteForeignDeviceTableEntry, \ DistributeBroadcastToNetwork, FDTEntry, ForwardedNPDU, \ OriginalBroadcastNPDU, OriginalUnicastNPDU, \ ReadBroadcastDistributionTable, ReadBroadcastDistributionTableAck, \ ReadForeignDeviceTable, ReadForeignDeviceTableAck, RegisterForeignDevice, \ Result, WriteBroadcastDistributionTable, bvl_pdu_types # some debugging _debug = 0 _log = ModuleLogger(globals()) # # _Multiplex Client and Server # class _MultiplexClient(Client): def __init__(self, mux): Client.__init__(self) self.multiplexer = mux def confirmation(self, pdu): self.multiplexer.confirmation(self, pdu) class _MultiplexServer(Server): def __init__(self, mux): Server.__init__(self) self.multiplexer = mux def indication(self, pdu): self.multiplexer.indication(self, pdu) # # UDPMultiplexer # @bacpypes_debugging class UDPMultiplexer: def __init__(self, addr=None, noBroadcast=False): if _debug: UDPMultiplexer._debug("__init__ %r noBroadcast=%r", addr, noBroadcast) # check for some options specialBroadcast = False if addr is None: self.address = Address() self.addrTuple = ('', 47808) self.addrBroadcastTuple = ('255.255.255.255', 47808) else: # allow the address to be cast if isinstance(addr, Address): self.address = addr else: self.address = Address(addr) # check for a special broadcast address self.addrTuple = self.address.addrTuple self.addrBroadcastTuple = self.address.addrBroadcastTuple if (self.addrTuple == self.addrBroadcastTuple): self.addrBroadcastTuple = ('255.255.255.255', self.addrTuple[1]) else: specialBroadcast = True if _debug: UDPMultiplexer._debug(" - address: %r", self.address) UDPMultiplexer._debug(" - addrTuple: %r", self.addrTuple) UDPMultiplexer._debug(" - addrBroadcastTuple: %r", self.addrBroadcastTuple) # create and bind the direct address self.direct = _MultiplexClient(self) self.directPort = UDPDirector(self.addrTuple) bind(self.direct, self.directPort) # create and bind the broadcast address for non-Windows if specialBroadcast and (not noBroadcast) and sys.platform in ('linux', 'darwin'): self.broadcast = _MultiplexClient(self) self.broadcastPort = UDPDirector(self.addrBroadcastTuple, reuse=True) bind(self.direct, self.broadcastPort) else: self.broadcast = None # create and bind the Annex H and J servers self.annexH = _MultiplexServer(self) self.annexJ = _MultiplexServer(self) def indication(self, server, pdu): if _debug: UDPMultiplexer._debug("indication %r %r", server, pdu) # check for a broadcast message if pdu.pduDestination.addrType == Address.localBroadcastAddr: dest = self.addrBroadcastTuple if _debug: UDPMultiplexer._debug(" - requesting local broadcast: %r", dest) elif pdu.pduDestination.addrType == Address.localStationAddr: dest = pdu.pduDestination.addrTuple if _debug: UDPMultiplexer._debug(" - requesting local station: %r", dest) else: raise RuntimeError("invalid destination address type") self.directPort.indication(PDU(pdu, destination=dest)) def confirmation(self, client, pdu): if _debug: UDPMultiplexer._debug("confirmation %r %r", client, pdu) # if this came from ourselves, dump it if pdu.pduSource == self.addrTuple: if _debug: UDPMultiplexer._debug(" - from us!") return # the PDU source is a tuple, convert it to an Address instance src = Address(pdu.pduSource) # match the destination in case the stack needs it if client is self.direct: dest = self.address elif client is self.broadcast: dest = LocalBroadcast() else: raise RuntimeError("confirmation mismatch") # must have at least one octet if not pdu.pduData: if _debug: UDPMultiplexer._debug(" - no data") return # extract the first octet msg_type = pdu.pduData[0] # check for the message type if msg_type == 0x01: if self.annexH.serverPeer: self.annexH.response(PDU(pdu, source=src, destination=dest)) elif msg_type == 0x81: if self.annexJ.serverPeer: self.annexJ.response(PDU(pdu, source=src, destination=dest)) else: UDPMultiplexer._warning("unsupported message") # # BTR # @bacpypes_debugging class BTR(Client, Server, DebugContents): _debug_contents = ('peers+',) def __init__(self, cid=None, sid=None): """An Annex-H BACnet Tunneling Router node.""" if _debug: BTR._debug("__init__ cid=%r sid=%r", cid, sid) Client.__init__(self, cid) Server.__init__(self, sid) # initialize a dicitonary of peers self.peers = {} def indication(self, pdu): if _debug: BTR._debug("indication %r", pdu) # check for local stations if pdu.pduDestination.addrType == Address.localStationAddr: # make sure it is going to a peer if pdu.pduDestination not in self.peers: ### log this return # send it downstream self.request(pdu) # check for broadcasts elif pdu.pduDestination.addrType == Address.localBroadcastAddr: # loop through the peers for peerAddr in self.peers.keys(): xpdu = PDU(pdu.pduData, destination=peerAddr) # send it downstream self.request(xpdu) else: raise RuntimeError("invalid destination address type (2)") def confirmation(self, pdu): if _debug: BTR._debug("confirmation %r", pdu) # make sure it came from a peer if pdu.pduSource not in self.peers: BTR._warning("not a peer: %r", pdu.pduSource) return # send it upstream self.response(pdu) def add_peer(self, peerAddr, networks=None): """Add a peer and optionally provide a list of the reachable networks.""" if _debug: BTR._debug("add_peer %r networks=%r", peerAddr, networks) # see if this is already a peer if peerAddr in self.peers: # add the (new?) reachable networks if not networks: networks = [] else: self.peers[peerAddr].extend(networks) else: if not networks: networks = [] # save the networks self.peers[peerAddr] = networks ### send a control message upstream that these are reachable def delete_peer(self, peerAddr): """Delete a peer.""" if _debug: BTR._debug("delete_peer %r", peerAddr) # get the peer networks # networks = self.peers[peerAddr] ### send a control message upstream that these are no longer reachable # now delete the peer del self.peers[peerAddr] # # AnnexJCodec # @bacpypes_debugging class AnnexJCodec(Client, Server): def __init__(self, cid=None, sid=None): if _debug: AnnexJCodec._debug("__init__ cid=%r sid=%r", cid, sid) Client.__init__(self, cid) Server.__init__(self, sid) def indication(self, rpdu): if _debug: AnnexJCodec._debug("indication %r", rpdu) # encode it as a generic BVLL PDU bvlpdu = BVLPDU() rpdu.encode(bvlpdu) # encode it as a PDU pdu = PDU() bvlpdu.encode(pdu) # send it downstream self.request(pdu) def confirmation(self, pdu): if _debug: AnnexJCodec._debug("confirmation %r", pdu) # interpret as a BVLL PDU bvlpdu = BVLPDU() bvlpdu.decode(pdu) # get the class related to the function rpdu = bvl_pdu_types[bvlpdu.bvlciFunction]() rpdu.decode(bvlpdu) # send it upstream self.response(rpdu) # # BIPSAP # @bacpypes_debugging class BIPSAP(ServiceAccessPoint): def __init__(self, sap=None): """A BIP service access point.""" if _debug: BIPSAP._debug("__init__ sap=%r", sap) ServiceAccessPoint.__init__(self, sap) def sap_indication(self, pdu): if _debug: BIPSAP._debug("sap_indication %r", pdu) # this is a request initiated by the ASE, send this downstream self.request(pdu) def sap_confirmation(self, pdu): if _debug: BIPSAP._debug("sap_confirmation %r", pdu) # this is a response from the ASE, send this downstream self.request(pdu) # # BIPSimple # @bacpypes_debugging class BIPSimple(BIPSAP, Client, Server): def __init__(self, sapID=None, cid=None, sid=None): """A BIP node.""" if _debug: BIPSimple._debug("__init__ sapID=%r cid=%r sid=%r", sapID, cid, sid) BIPSAP.__init__(self, sapID) Client.__init__(self, cid) Server.__init__(self, sid) def indication(self, pdu): if _debug: BIPSimple._debug("indication %r", pdu) # check for local stations if pdu.pduDestination.addrType == Address.localStationAddr: # make an original unicast PDU xpdu = OriginalUnicastNPDU(pdu, destination=pdu.pduDestination, user_data=pdu.pduUserData) if _debug: BIPSimple._debug(" - xpdu: %r", xpdu) # send it downstream self.request(xpdu) # check for broadcasts elif pdu.pduDestination.addrType == Address.localBroadcastAddr: # make an original broadcast PDU xpdu = OriginalBroadcastNPDU(pdu, destination=pdu.pduDestination, user_data=pdu.pduUserData) if _debug: BIPSimple._debug(" - xpdu: %r", xpdu) # send it downstream self.request(xpdu) else: BIPSimple._warning("invalid destination address: %r", pdu.pduDestination) def confirmation(self, pdu): if _debug: BIPSimple._debug("confirmation %r", pdu) # some kind of response to a request if isinstance(pdu, Result): # send this to the service access point self.sap_response(pdu) elif isinstance(pdu, ReadBroadcastDistributionTableAck): # send this to the service access point self.sap_response(pdu) elif isinstance(pdu, ReadForeignDeviceTableAck): # send this to the service access point self.sap_response(pdu) elif isinstance(pdu, OriginalUnicastNPDU): # build a vanilla PDU xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=pdu.pduDestination, user_data=pdu.pduUserData) if _debug: BIPSimple._debug(" - xpdu: %r", xpdu) # send it upstream self.response(xpdu) elif isinstance(pdu, OriginalBroadcastNPDU): # build a PDU with a local broadcast address xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=LocalBroadcast(), user_data=pdu.pduUserData) if _debug: BIPSimple._debug(" - xpdu: %r", xpdu) # send it upstream self.response(xpdu) elif isinstance(pdu, ForwardedNPDU): # build a PDU with the source from the real source xpdu = PDU(pdu.pduData, source=pdu.bvlciAddress, destination=LocalBroadcast(), user_data=pdu.pduUserData) if _debug: BIPSimple._debug(" - xpdu: %r", xpdu) # send it upstream self.response(xpdu) else: BIPSimple._warning("invalid pdu type: %s", type(pdu)) # # BIPForeign # @bacpypes_debugging class BIPForeign(BIPSAP, Client, Server, OneShotTask, DebugContents): _debug_contents = ('registrationStatus', 'bbmdAddress', 'bbmdTimeToLive') def __init__(self, addr=None, ttl=None, sapID=None, cid=None, sid=None): """A BIP node.""" if _debug: BIPForeign._debug("__init__ addr=%r ttl=%r sapID=%r cid=%r sid=%r", addr, ttl, sapID, cid, sid) BIPSAP.__init__(self, sapID) Client.__init__(self, cid) Server.__init__(self, sid) OneShotTask.__init__(self) # -2=unregistered, -1=not attempted or no ack, 0=OK, >0 error self.registrationStatus = -1 # clear the BBMD address and time-to-live self.bbmdAddress = None self.bbmdTimeToLive = None # registration provided if addr: # a little error checking if ttl is None: raise RuntimeError("BBMD address and time-to-live must both be specified") self.register(addr, ttl) def indication(self, pdu): if _debug: BIPForeign._debug("indication %r", pdu) # check the BBMD registration status, we may not be registered if self.registrationStatus != 0: if _debug: BIPForeign._debug(" - packet dropped, unregistered") return # check for local stations if pdu.pduDestination.addrType == Address.localStationAddr: # make an original unicast PDU xpdu = OriginalUnicastNPDU(pdu, user_data=pdu.pduUserData) xpdu.pduDestination = pdu.pduDestination # send it downstream self.request(xpdu) # check for broadcasts elif pdu.pduDestination.addrType == Address.localBroadcastAddr: # make an original broadcast PDU xpdu = DistributeBroadcastToNetwork(pdu, user_data=pdu.pduUserData) xpdu.pduDestination = self.bbmdAddress # send it downstream self.request(xpdu) else: BIPForeign._warning("invalid destination address: %r", pdu.pduDestination) def confirmation(self, pdu): if _debug: BIPForeign._debug("confirmation %r", pdu) # check for a registration request result if isinstance(pdu, Result): # if we are unbinding, do nothing if self.registrationStatus == -2: return ### make sure we have a bind request in process # make sure the result is from the bbmd if pdu.pduSource != self.bbmdAddress: if _debug: BIPForeign._debug(" - packet dropped, not from the BBMD") return # save the result code as the status self.registrationStatus = pdu.bvlciResultCode # check for success if pdu.bvlciResultCode == 0: # schedule for a refresh self.install_task(_time() + self.bbmdTimeToLive) return # check the BBMD registration status, we may not be registered if self.registrationStatus != 0: if _debug: BIPForeign._debug(" - packet dropped, unregistered") return if isinstance(pdu, ReadBroadcastDistributionTableAck): # send this to the service access point self.sap_response(pdu) elif isinstance(pdu, ReadForeignDeviceTableAck): # send this to the service access point self.sap_response(pdu) elif isinstance(pdu, OriginalUnicastNPDU): # build a vanilla PDU xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=pdu.pduDestination, user_data=pdu.pduUserData) # send it upstream self.response(xpdu) elif isinstance(pdu, ForwardedNPDU): # build a PDU with the source from the real source xpdu = PDU(pdu.pduData, source=pdu.bvlciAddress, destination=LocalBroadcast(), user_data=pdu.pduUserData) # send it upstream self.response(xpdu) else: BIPForeign._warning("invalid pdu type: %s", type(pdu)) def register(self, addr, ttl): """Initiate the process of registering with a BBMD.""" # a little error checking if ttl <= 0: raise ValueError("time-to-live must be greater than zero") # save the BBMD address and time-to-live if isinstance(addr, Address): self.bbmdAddress = addr else: self.bbmdAddress = Address(addr) self.bbmdTimeToLive = ttl # install this task to run when it gets a chance self.install_task(0) def unregister(self): """Drop the registration with a BBMD.""" pdu = RegisterForeignDevice(0) pdu.pduDestination = self.bbmdAddress # send it downstream self.request(pdu) # change the status to unregistered self.registrationStatus = -2 # clear the BBMD address and time-to-live self.bbmdAddress = None self.bbmdTimeToLive = None def process_task(self): """Called when the registration request should be sent to the BBMD.""" pdu = RegisterForeignDevice(self.bbmdTimeToLive) pdu.pduDestination = self.bbmdAddress # send it downstream self.request(pdu) # # BIPBBMD # @bacpypes_debugging class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): _debug_contents = ('bbmdAddress', 'bbmdBDT+', 'bbmdFDT+') def __init__(self, addr, sapID=None, cid=None, sid=None): """A BBMD node.""" if _debug: BIPBBMD._debug("__init__ %r sapID=%r cid=%r sid=%r", addr, sapID, cid, sid) BIPSAP.__init__(self, sapID) Client.__init__(self, cid) Server.__init__(self, sid) RecurringTask.__init__(self, 1000.0) self.bbmdAddress = addr self.bbmdBDT = [] self.bbmdFDT = [] # install so process_task runs self.install_task() def indication(self, pdu): if _debug: BIPBBMD._debug("indication %r", pdu) # check for local stations if pdu.pduDestination.addrType == Address.localStationAddr: # make an original unicast PDU xpdu = OriginalUnicastNPDU(pdu, user_data=pdu.pduUserData) xpdu.pduDestination = pdu.pduDestination if _debug: BIPBBMD._debug(" - xpdu: %r", xpdu) # send it downstream self.request(xpdu) # check for broadcasts elif pdu.pduDestination.addrType == Address.localBroadcastAddr: # make an original broadcast PDU xpdu = OriginalBroadcastNPDU(pdu, user_data=pdu.pduUserData) xpdu.pduDestination = pdu.pduDestination if _debug: BIPBBMD._debug(" - original broadcast xpdu: %r", xpdu) # send it downstream self.request(xpdu) # make a forwarded PDU xpdu = ForwardedNPDU(self.bbmdAddress, pdu, user_data=pdu.pduUserData) if _debug: BIPBBMD._debug(" - forwarded xpdu: %r", xpdu) # send it to the peers for bdte in self.bbmdBDT: if bdte != self.bbmdAddress: xpdu.pduDestination = Address( ((bdte.addrIP|~bdte.addrMask), bdte.addrPort) ) BIPBBMD._debug(" - sending to peer: %r", xpdu.pduDestination) self.request(xpdu) # send it to the registered foreign devices for fdte in self.bbmdFDT: xpdu.pduDestination = fdte.fdAddress if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) self.request(xpdu) else: BIPBBMD._warning("invalid destination address: %r", pdu.pduDestination) def confirmation(self, pdu): if _debug: BIPBBMD._debug("confirmation %r", pdu) # some kind of response to a request if isinstance(pdu, Result): # send this to the service access point self.sap_response(pdu) elif isinstance(pdu, WriteBroadcastDistributionTable): # build a response xpdu = Result(code=99, user_data=pdu.pduUserData) xpdu.pduDestination = pdu.pduSource # send it downstream self.request(xpdu) elif isinstance(pdu, ReadBroadcastDistributionTable): # build a response xpdu = ReadBroadcastDistributionTableAck(self.bbmdBDT, user_data=pdu.pduUserData) xpdu.pduDestination = pdu.pduSource if _debug: BIPBBMD._debug(" - xpdu: %r", xpdu) # send it downstream self.request(xpdu) elif isinstance(pdu, ReadBroadcastDistributionTableAck): # send this to the service access point self.sap_response(pdu) elif isinstance(pdu, ForwardedNPDU): # build a PDU with the source from the real source xpdu = PDU(pdu.pduData, source=pdu.bvlciAddress, destination=LocalBroadcast(), user_data=pdu.pduUserData) if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) # send it upstream self.response(xpdu) # build a forwarded NPDU to send out xpdu = ForwardedNPDU(pdu.bvlciAddress, pdu, destination=None, user_data=pdu.pduUserData) if _debug: BIPBBMD._debug(" - forwarded xpdu: %r", xpdu) # look for self as first entry in the BDT if self.bbmdBDT and (self.bbmdBDT[0] == self.bbmdAddress): xpdu.pduDestination = LocalBroadcast() if _debug: BIPBBMD._debug(" - local broadcast") self.request(xpdu) # send it to the registered foreign devices for fdte in self.bbmdFDT: xpdu.pduDestination = fdte.fdAddress if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) self.request(xpdu) elif isinstance(pdu, RegisterForeignDevice): # process the request stat = self.register_foreign_device(pdu.pduSource, pdu.bvlciTimeToLive) # build a response xpdu = Result(code=stat, destination=pdu.pduSource, user_data=pdu.pduUserData) if _debug: BIPBBMD._debug(" - xpdu: %r", xpdu) # send it downstream self.request(xpdu) elif isinstance(pdu, ReadForeignDeviceTable): # build a response xpdu = ReadForeignDeviceTableAck(self.bbmdFDT, destination=pdu.pduSource, user_data=pdu.pduUserData) if _debug: BIPBBMD._debug(" - xpdu: %r", xpdu) # send it downstream self.request(xpdu) elif isinstance(pdu, ReadForeignDeviceTableAck): # send this to the service access point self.sap_response(pdu) elif isinstance(pdu, DeleteForeignDeviceTableEntry): # process the request stat = self.delete_foreign_device_table_entry(pdu.bvlciAddress) # build a response xpdu = Result(code=stat, user_data=pdu.pduUserData) xpdu.pduDestination = pdu.pduSource if _debug: BIPBBMD._debug(" - xpdu: %r", xpdu) # send it downstream self.request(xpdu) elif isinstance(pdu, DistributeBroadcastToNetwork): # build a PDU with a local broadcast address xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=LocalBroadcast(), user_data=pdu.pduUserData) if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) # send it upstream self.response(xpdu) # build a forwarded NPDU to send out xpdu = ForwardedNPDU(pdu.pduSource, pdu, user_data=pdu.pduUserData) if _debug: BIPBBMD._debug(" - forwarded xpdu: %r", xpdu) # send it to the peers for bdte in self.bbmdBDT: if bdte == self.bbmdAddress: xpdu.pduDestination = LocalBroadcast() if _debug: BIPBBMD._debug(" - local broadcast") self.request(xpdu) else: xpdu.pduDestination = Address( ((bdte.addrIP|~bdte.addrMask), bdte.addrPort) ) if _debug: BIPBBMD._debug(" - sending to peer: %r", xpdu.pduDestination) self.request(xpdu) # send it to the other registered foreign devices for fdte in self.bbmdFDT: if fdte.fdAddress != pdu.pduSource: xpdu.pduDestination = fdte.fdAddress if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) self.request(xpdu) elif isinstance(pdu, OriginalUnicastNPDU): # build a vanilla PDU xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=pdu.pduDestination, user_data=pdu.pduUserData) if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) # send it upstream self.response(xpdu) elif isinstance(pdu, OriginalBroadcastNPDU): # build a PDU with a local broadcast address xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=LocalBroadcast(), user_data=pdu.pduUserData) if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) # send it upstream self.response(xpdu) # make a forwarded PDU xpdu = ForwardedNPDU(pdu.pduSource, pdu, user_data=pdu.pduUserData) if _debug: BIPBBMD._debug(" - forwarded xpdu: %r", xpdu) # send it to the peers for bdte in self.bbmdBDT: if bdte != self.bbmdAddress: xpdu.pduDestination = Address( ((bdte.addrIP|~bdte.addrMask), bdte.addrPort) ) if _debug: BIPBBMD._debug(" - sending to peer: %r", xpdu.pduDestination) self.request(xpdu) # send it to the registered foreign devices for fdte in self.bbmdFDT: xpdu.pduDestination = fdte.fdAddress if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) self.request(xpdu) else: BIPBBMD._warning("invalid pdu type: %s", type(pdu)) def register_foreign_device(self, addr, ttl): """Add a foreign device to the FDT.""" if _debug: BIPBBMD._debug("register_foreign_device %r %r", addr, ttl) # see if it is an address or make it one if isinstance(addr, Address): pass elif isinstance(addr, str): addr = LocalStation( addr ) else: raise TypeError("addr must be a string or an Address") for fdte in self.bbmdFDT: if addr == fdte.fdAddress: break else: fdte = FDTEntry() fdte.fdAddress = addr self.bbmdFDT.append( fdte ) fdte.fdTTL = ttl fdte.fdRemain = ttl + 5 # return success return 0 def delete_foreign_device_table_entry(self, addr): if _debug: BIPBBMD._debug("delete_foreign_device_table_entry %r", addr) # see if it is an address or make it one if isinstance(addr, Address): pass elif isinstance(addr, str): addr = LocalStation( addr ) else: raise TypeError("addr must be a string or an Address") # find it and delete it stat = 0 for i in range(len(self.bbmdFDT)-1, -1, -1): if addr == self.bbmdFDT[i].fdAddress: del self.bbmdFDT[i] break else: stat = 99 ### entry not found # return status return stat def process_task(self): # look for foreign device registrations that have expired for i in range(len(self.bbmdFDT)-1, -1, -1): fdte = self.bbmdFDT[i] fdte.fdRemain -= 1 # delete it if it expired if fdte.fdRemain <= 0: if _debug: BIPBBMD._debug("foreign device expired: %r", fdte.fdAddress) del self.bbmdFDT[i] def add_peer(self, addr): if _debug: BIPBBMD._debug("add_peer %r", addr) # see if it is an address or make it one if isinstance(addr, Address): pass elif isinstance(addr, str): addr = LocalStation(addr) else: raise TypeError("addr must be a string or an Address") # if it's this BBMD, make it the first one if self.bbmdBDT and (addr == self.bbmdAddress): raise RuntimeError("add self to BDT as first address") # see if it's already there for bdte in self.bbmdBDT: if addr == bdte: break else: self.bbmdBDT.append(addr) def delete_peer(self, addr): if _debug: BIPBBMD._debug("delete_peer %r", addr) # see if it is an address or make it one if isinstance(addr, Address): pass elif isinstance(addr, str): addr = LocalStation(addr) else: raise TypeError("addr must be a string or an Address") # look for the peer address for i in range(len(self.bbmdBDT)-1, -1, -1): if addr == self.bbmdBDT[i]: del self.bbmdBDT[i] break else: pass # # BVLLServiceElement # @bacpypes_debugging class BVLLServiceElement(ApplicationServiceElement): def __init__(self, aseID=None): if _debug: BVLLServiceElement._debug("__init__ aseID=%r", aseID) ApplicationServiceElement.__init__(self, aseID) def indication(self, npdu): if _debug: BVLLServiceElement._debug("indication %r %r", npdu) # redirect fn = npdu.__class__.__name__ if hasattr(self, fn): getattr(self, fn)(npdu) else: BVLLServiceElement._warning("no handler for %s", fn) def confirmation(self, npdu): if _debug: BVLLServiceElement._debug("confirmation %r %r", npdu) # redirect fn = npdu.__class__.__name__ if hasattr(self, fn): getattr(self, fn)(npdu) else: BVLLServiceElement._warning("no handler for %s", fn) PKH;!8mmbacpypes/app.py#!/usr/bin/python """ Application Module """ from .debugging import bacpypes_debugging, ModuleLogger from .comm import ApplicationServiceElement, bind from .pdu import Address from .primitivedata import Atomic, Date, Null, ObjectIdentifier, Time, Unsigned from .constructeddata import Any, Array, ArrayOf from .appservice import StateMachineAccessPoint, ApplicationServiceAccessPoint from .netservice import NetworkServiceAccessPoint, NetworkServiceElement from .bvllservice import BIPSimple, BIPForeign, AnnexJCodec, UDPMultiplexer from .object import Property, PropertyError, DeviceObject, \ registered_object_types, register_object_type from .apdu import ConfirmedRequestPDU, SimpleAckPDU, RejectPDU, RejectReason from .apdu import IAmRequest, ReadPropertyACK, Error from .errors import ExecutionError # 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 # some debugging _debug = 0 _log = ModuleLogger(globals()) # # CurrentDateProperty # class CurrentDateProperty(Property): def __init__(self, identifier): Property.__init__(self, identifier, Date, default=None, optional=True, mutable=False) def ReadProperty(self, obj, arrayIndex=None): # access an array if arrayIndex is not None: raise TypeError("{0} is unsubscriptable".format(self.identifier)) # get the value now = Date() now.now() return now.value def WriteProperty(self, obj, value, arrayIndex=None, priority=None): raise ExecutionError(errorClass='property', errorCode='writeAccessDenied') # # CurrentTimeProperty # class CurrentTimeProperty(Property): def __init__(self, identifier): Property.__init__(self, identifier, Time, default=None, optional=True, mutable=False) def ReadProperty(self, obj, arrayIndex=None): # access an array if arrayIndex is not None: raise TypeError("{0} is unsubscriptable".format(self.identifier)) # get the value now = Time() now.now() return now.value def WriteProperty(self, obj, value, arrayIndex=None, priority=None): raise ExecutionError(errorClass='property', errorCode='writeAccessDenied') # # LocalDeviceObject # @bacpypes_debugging class LocalDeviceObject(DeviceObject): properties = \ [ CurrentTimeProperty('localTime') , CurrentDateProperty('localDate') ] defaultProperties = \ { 'maxApduLengthAccepted': 1024 , 'segmentationSupported': 'segmentedBoth' , 'maxSegmentsAccepted': 16 , 'apduSegmentTimeout': 20000 , 'apduTimeout': 3000 , 'numberOfApduRetries': 3 } def __init__(self, **kwargs): if _debug: LocalDeviceObject._debug("__init__ %r", kwargs) # fill in default property values not in kwargs for attr, value in LocalDeviceObject.defaultProperties.items(): if attr not in kwargs: kwargs[attr] = value # check for registration if self.__class__ not in registered_object_types.values(): if 'vendorIdentifier' not in kwargs: raise RuntimeError("vendorIdentifier required to auto-register the LocalDeviceObject class") register_object_type(self.__class__, vendor_id=kwargs['vendorIdentifier']) # check for local time 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") # 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') # # Application # @bacpypes_debugging class Application(ApplicationServiceElement): def __init__(self, localDevice, localAddress, aseID=None): if _debug: Application._debug("__init__ %r %r aseID=%r", localDevice, localAddress, aseID) ApplicationServiceElement.__init__(self, aseID) # keep track of the local device self.localDevice = localDevice # 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) # extract the object name and identifier object_name = obj.objectName if not object_name: raise RuntimeError("object name required") object_identifier = obj.objectIdentifier if not object_identifier: raise RuntimeError("object identifier required") # assuming the object identifier is well formed, check the instance number if (object_identifier[1] >= ObjectIdentifier.maximum_instance_number): raise RuntimeError("invalid object identifier") # make sure it hasn't already been defined if object_name in self.objectName: raise RuntimeError("already an object with name {0!r}".format(object_name)) if object_identifier in self.objectIdentifier: raise RuntimeError("already an object with identifier {0!r}".format(object_identifier)) # now put it in local dictionaries 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) # let the object know which application stack it belongs to obj._app = self def delete_object(self, obj): """Add an object to the local collection.""" if _debug: Application._debug("delete_object %r", obj) # extract the object name and identifier object_name = obj.objectName object_identifier = obj.objectIdentifier # delete it from the application del self.objectName[object_name] 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] # make sure the object knows it's detached from an application obj._app = None def get_object_id(self, objid): """Return a local object or None.""" return self.objectIdentifier.get(objid, None) def get_object_name(self, objname): """Return a local object or None.""" return self.objectName.get(objname, None) def iter_objects(self): """Iterate over the objects.""" return iter(self.objectIdentifier.values()) def get_services_supported(self): """Return a ServicesSupported bit string based in introspection, look for helper methods that match confirmed and unconfirmed services.""" if _debug: Application._debug("get_services_supported") services_supported = ServicesSupported() # look through the confirmed services for service_choice, service_request_class in confirmed_request_types.items(): service_helper = "do_" + service_request_class.__name__ if hasattr(self, service_helper): service_supported = ConfirmedServiceChoice._xlate_table[service_choice] services_supported[service_supported] = 1 # look through the unconfirmed services for service_choice, service_request_class in unconfirmed_request_types.items(): service_helper = "do_" + service_request_class.__name__ if hasattr(self, service_helper): service_supported = UnconfirmedServiceChoice._xlate_table[service_choice] services_supported[service_supported] = 1 # return the bit list return services_supported #----- def indication(self, apdu): if _debug: Application._debug("indication %r", apdu) # get a helper function helperName = "do_" + apdu.__class__.__name__ helperFn = getattr(self, helperName, None) if _debug: Application._debug(" - helperFn: %r", helperFn) # send back a reject for unrecognized services if not helperFn: if isinstance(apdu, ConfirmedRequestPDU): response = RejectPDU( apdu.apduInvokeID, RejectReason.UNRECOGNIZEDSERVICE, context=apdu) self.response(response) return # pass the apdu on to the helper function try: helperFn(apdu) except ExecutionError as err: if _debug: Application._debug(" - execution error: %r", err) # send back an error if isinstance(apdu, ConfirmedRequestPDU): resp = Error(errorClass=err.errorClass, errorCode=err.errorCode, context=apdu) self.response(resp) except Exception as err: Application._exception("exception: %r", err) # send back an error if isinstance(apdu, ConfirmedRequestPDU): resp = Error(errorClass='device', errorCode='operationalProblem', context=apdu) self.response(resp) def do_WhoIsRequest(self, apdu): """Respond to a Who-Is request.""" if _debug: Application._debug("do_WhoIsRequest %r", apdu) # may be a restriction if (apdu.deviceInstanceRangeLowLimit is not None) and \ (apdu.deviceInstanceRangeHighLimit is not None): if (self.localDevice.objectIdentifier[1] < apdu.deviceInstanceRangeLowLimit): return if (self.localDevice.objectIdentifier[1] > apdu.deviceInstanceRangeHighLimit): return # create a I-Am "response" back to the source iAm = IAmRequest() iAm.pduDestination = apdu.pduSource iAm.iAmDeviceIdentifier = self.localDevice.objectIdentifier iAm.maxAPDULengthAccepted = self.localDevice.maxApduLengthAccepted iAm.segmentationSupported = self.localDevice.segmentationSupported iAm.vendorID = self.localDevice.vendorIdentifier if _debug: Application._debug(" - iAm: %r", iAm) # away it goes self.request(iAm) def do_IAmRequest(self, apdu): """Respond to an I-Am request.""" if _debug: Application._debug("do_IAmRequest %r", apdu) def do_ReadPropertyRequest(self, apdu): """Return the value of some property of one of our objects.""" if _debug: Application._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") objId = self.localDevice.objectIdentifier # get the object obj = self.get_object_id(objId) if _debug: Application._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) # 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) # 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): raise TypeError("invalid result datatype, expecting {0} and got {1}" \ .format(datatype.__name__, type(value).__name__)) if _debug: Application._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 # save the result in the property value resp.propertyValue = Any() resp.propertyValue.cast_in(value) except PropertyError: resp = Error(errorClass='object', errorCode='unknownProperty', context=apdu) if _debug: Application._debug(" - resp: %r", resp) # 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) # get the object obj = self.get_object_id(apdu.objectIdentifier) if _debug: Application._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) # 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: Application._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: 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) # success resp = SimpleAckPDU(context=apdu) except PropertyError: resp = Error(errorClass='object', errorCode='unknownProperty', context=apdu) if _debug: Application._debug(" - resp: %r", resp) # return the result self.response(resp) def do_AtomicReadFileRequest(self, apdu): """Return one of our records.""" if _debug: Application._debug("do_AtomicReadFileRequest %r", apdu) if (apdu.fileIdentifier[0] != 'file'): resp = Error(errorClass='services', errorCode='inconsistentObjectType', context=apdu) if _debug: Application._debug(" - error resp: %r", resp) self.response(resp) return # get the object obj = self.get_object_id(apdu.fileIdentifier) if _debug: Application._debug(" - object: %r", obj) if not obj: resp = Error(errorClass='object', errorCode='unknownObject', context=apdu) elif apdu.accessMethod.recordAccess: # check against the object if obj.fileAccessMethod != 'recordAccess': resp = Error(errorClass='services', errorCode='invalidFileAccessMethod', context=apdu ) ### verify start is valid - double check this (empty files?) elif (apdu.accessMethod.recordAccess.fileStartRecord < 0) or \ (apdu.accessMethod.recordAccess.fileStartRecord >= len(obj)): resp = Error(errorClass='services', errorCode='invalidFileStartPosition', context=apdu ) else: # pass along to the object end_of_file, record_data = obj.ReadFile( apdu.accessMethod.recordAccess.fileStartRecord, apdu.accessMethod.recordAccess.requestedRecordCount, ) if _debug: Application._debug(" - record_data: %r", record_data) # this is an ack resp = AtomicReadFileACK(context=apdu, endOfFile=end_of_file, accessMethod=AtomicReadFileACKAccessMethodChoice( recordAccess=AtomicReadFileACKAccessMethodRecordAccess( fileStartRecord=apdu.accessMethod.recordAccess.fileStartRecord, returnedRecordCount=len(record_data), fileRecordData=record_data, ), ), ) elif apdu.accessMethod.streamAccess: # check against the object if obj.fileAccessMethod != 'streamAccess': resp = Error(errorClass='services', errorCode='invalidFileAccessMethod', context=apdu ) ### verify start is valid - double check this (empty files?) elif (apdu.accessMethod.streamAccess.fileStartPosition < 0) or \ (apdu.accessMethod.streamAccess.fileStartPosition >= len(obj)): resp = Error(errorClass='services', errorCode='invalidFileStartPosition', context=apdu ) else: # pass along to the object end_of_file, record_data = obj.ReadFile( apdu.accessMethod.streamAccess.fileStartPosition, apdu.accessMethod.streamAccess.requestedOctetCount, ) if _debug: Application._debug(" - record_data: %r", record_data) # this is an ack resp = AtomicReadFileACK(context=apdu, endOfFile=end_of_file, accessMethod=AtomicReadFileACKAccessMethodChoice( streamAccess=AtomicReadFileACKAccessMethodStreamAccess( fileStartPosition=apdu.accessMethod.streamAccess.fileStartPosition, fileData=record_data, ), ), ) if _debug: Application._debug(" - resp: %r", resp) # return the result self.response(resp) def do_AtomicWriteFileRequest(self, apdu): """Return one of our records.""" if _debug: Application._debug("do_AtomicWriteFileRequest %r", apdu) if (apdu.fileIdentifier[0] != 'file'): resp = Error(errorClass='services', errorCode='inconsistentObjectType', context=apdu) if _debug: Application._debug(" - error resp: %r", resp) self.response(resp) return # get the object obj = self.get_object_id(apdu.fileIdentifier) if _debug: Application._debug(" - object: %r", obj) if not obj: resp = Error(errorClass='object', errorCode='unknownObject', context=apdu) elif apdu.accessMethod.recordAccess: # check against the object if obj.fileAccessMethod != 'recordAccess': resp = Error(errorClass='services', errorCode='invalidFileAccessMethod', context=apdu ) if _debug: Application._debug(" - error resp: %r", resp) self.response(resp) return # check for read-only if obj.readOnly: resp = Error(errorClass='services', errorCode='fileAccessDenied', context=apdu ) if _debug: Application._debug(" - error resp: %r", resp) self.response(resp) return # pass along to the object start_record = obj.WriteFile( apdu.accessMethod.recordAccess.fileStartRecord, apdu.accessMethod.recordAccess.recordCount, apdu.accessMethod.recordAccess.fileRecordData, ) if _debug: Application._debug(" - start_record: %r", start_record) # this is an ack resp = AtomicWriteFileACK(context=apdu, fileStartRecord=start_record, ) elif apdu.accessMethod.streamAccess: # check against the object if obj.fileAccessMethod != 'streamAccess': resp = Error(errorClass='services', errorCode='invalidFileAccessMethod', context=apdu ) if _debug: Application._debug(" - error resp: %r", resp) self.response(resp) return # check for read-only if obj.readOnly: resp = Error(errorClass='services', errorCode='fileAccessDenied', context=apdu ) if _debug: Application._debug(" - error resp: %r", resp) self.response(resp) return # pass along to the object start_position = obj.WriteFile( apdu.accessMethod.streamAccess.fileStartPosition, apdu.accessMethod.streamAccess.fileData, ) if _debug: Application._debug(" - start_position: %r", start_position) # this is an ack resp = AtomicWriteFileACK(context=apdu, fileStartPosition=start_position, ) if _debug: Application._debug(" - resp: %r", resp) # return the result self.response(resp) # # BIPSimpleApplication # @bacpypes_debugging class BIPSimpleApplication(Application): def __init__(self, localDevice, localAddress, aseID=None): if _debug: BIPSimpleApplication._debug("__init__ %r %r aseID=%r", localDevice, localAddress, aseID) Application.__init__(self, localDevice, localAddress, aseID) # include a application decoder self.asap = ApplicationServiceAccessPoint() # pass the device object to the state machine access point so it # can know if it should support segmentation self.smap = StateMachineAccessPoint(localDevice) # a network service access point will be needed self.nsap = NetworkServiceAccessPoint() # give the NSAP a generic network layer service element self.nse = NetworkServiceElement() bind(self.nse, self.nsap) # bind the top layers bind(self, self.asap, self.smap, self.nsap) # create a generic BIP stack, bound to the Annex J server # on the UDP multiplexer self.bip = BIPSimple() self.annexj = AnnexJCodec() self.mux = UDPMultiplexer(self.localAddress) # bind the bottom layers bind(self.bip, self.annexj, self.mux.annexJ) # bind the BIP stack to the network, no network number self.nsap.bind(self.bip) # # BIPForeignApplication # @bacpypes_debugging class BIPForeignApplication(Application): 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) # include a application decoder self.asap = ApplicationServiceAccessPoint() # pass the device object to the state machine access point so it # can know if it should support segmentation self.smap = StateMachineAccessPoint(localDevice) # a network service access point will be needed self.nsap = NetworkServiceAccessPoint() # give the NSAP a generic network layer service element self.nse = NetworkServiceElement() bind(self.nse, self.nsap) # bind the top layers bind(self, self.asap, self.smap, self.nsap) # create a generic BIP stack, bound to the Annex J server # on the UDP multiplexer self.bip = BIPForeign(bbmdAddress, bbmdTTL) self.annexj = AnnexJCodec() self.mux = UDPMultiplexer(self.localAddress, noBroadcast=True) # bind the bottom layers bind(self.bip, self.annexj, self.mux.annexJ) # bind the NSAP to the stack, no network number self.nsap.bind(self.bip) # # BIPNetworkApplication # @bacpypes_debugging class BIPNetworkApplication(NetworkServiceElement): def __init__(self, localAddress, eID=None): if _debug: BIPNetworkApplication._debug("__init__ %r eID=%r", localAddress, eID) NetworkServiceElement.__init__(self, eID) # allow the address to be cast to the correct type if isinstance(localAddress, Address): self.localAddress = localAddress else: self.localAddress = Address(localAddress) # a network service access point will be needed self.nsap = NetworkServiceAccessPoint() # give the NSAP a generic network layer service element bind(self, self.nsap) # create a generic BIP stack, bound to the Annex J server # on the UDP multiplexer self.bip = BIPSimple() self.annexj = AnnexJCodec() self.mux = UDPMultiplexer(self.localAddress) # bind the bottom layers bind(self.bip, self.annexj, self.mux.annexJ) # bind the NSAP to the stack, no network number self.nsap.bind(self.bip) PK*UH  bacpypes/console.py#!/usr/bin/env python """ Console Communications """ import sys import asyncore from .debugging import bacpypes_debugging, ModuleLogger from .core import deferred from .comm import PDU, Client, Server # some debugging _debug = 0 _log = ModuleLogger(globals()) # # asyncore.file_dispatcher is only available in Unix. This is a hack that # allows the ConsoleClient and ConsoleServer to initialize on Windows. # try: asyncore.file_dispatcher except: class _barf: def __init__(self, *args): pass asyncore.file_dispatcher = _barf # # ConsoleClient # @bacpypes_debugging class ConsoleClient(asyncore.file_dispatcher, Client): def __init__(self, cid=None): if _debug: ConsoleClient._debug("__init__ cid=%r", cid) asyncore.file_dispatcher.__init__(self, sys.stdin) Client.__init__(self, cid) def readable(self): return True # We are always happy to read def writable(self): return False # we don't have anything to write def handle_read(self): if _debug: deferred(ConsoleClient._debug, "handle_read") data = sys.stdin.read() if _debug: deferred(ConsoleClient._debug, " - data: %r", data) deferred(self.request, PDU(data.encode('utf_8'))) def confirmation(self, pdu): if _debug: deferred(ConsoleClient._debug, "confirmation %r", pdu) try: sys.stdout.write(pdu.pduData.decode('utf_8')) except Exception as err: ConsoleClient._exception("Confirmation sys.stdout.write exception: %r", err) # # ConsoleServer # @bacpypes_debugging class ConsoleServer(asyncore.file_dispatcher, Server): def __init__(self, sid=None): if _debug: ConsoleServer._debug("__init__ sid=%r", sid) asyncore.file_dispatcher.__init__(self, sys.stdin) Server.__init__(self, sid) def readable(self): return True # We are always happy to read def writable(self): return False # we don't have anything to write def handle_read(self): if _debug: deferred(ConsoleServer._debug, "handle_read") data = sys.stdin.read() if _debug: deferred(ConsoleServer._debug, " - data: %r", data) deferred(self.response, PDU(data.encode('utf_8'))) def indication(self, pdu): if _debug: deferred(ConsoleServer._debug, "Indication %r", pdu) try: sys.stdout.write(pdu.pduData.decode('utf_8')) except Exception as err: ConsoleServer._exception("Indication sys.stdout.write exception: %r", err) PKFB*$$bacpypes/debugging.py#!/usr/bin/python """ Debugging """ import sys import logging import binascii from io import StringIO # set the level of the root logger _root = logging.getLogger() _root.setLevel(1) # add a stream handler for warnings and up hdlr = logging.StreamHandler() if ('--debugDebugging' in sys.argv): hdlr.setLevel(logging.DEBUG) else: hdlr.setLevel(logging.WARNING) hdlr.setFormatter(logging.Formatter(logging.BASIC_FORMAT, None)) _root.addHandler(hdlr) del hdlr def btox(data, sep=''): """Return the hex encoding of a blob (byte string).""" # translate the blob into hex hex_str = str(binascii.hexlify(data), 'ascii') # inject the separator if it was given if sep: hex_str = sep.join(hex_str[i:i+2] for i in range(0, len(hex_str), 2)) # return the result return hex_str def xtob(data, sep=''): """Interpret the hex encoding of a blob (byte string).""" # if there is a separator, remove it if sep: data = ''.join(data.split(sep)) # interpret the hex return binascii.unhexlify(data) # # ModuleLogger # def ModuleLogger(globs): """Create a module level logger. To debug a module, create a _debug variable in the module, then use the ModuleLogger function to create a "module level" logger. When a handler is added to this logger or a child of this logger, the _debug variable will be incremented. All of the calls within functions or class methods within the module should first check to see if _debug is set to prevent calls to formatter objects that aren't necessary. """ # make sure that _debug is defined if '_debug' not in globs: raise RuntimeError("define _debug before creating a module logger") # create a logger to be assigned to _log logger = logging.getLogger(globs['__name__']) # put in a reference to the module globals logger.globs = globs return logger # # Typical Use # # some debugging _debug = 0 _log = ModuleLogger(globals()) # # DebugContents # class DebugContents(object): def debug_contents(self, indent=1, file=sys.stdout, _ids=None): """Debug the contents of an object.""" if _debug: _log.debug("debug_contents indent=%r file=%r _ids=%r", indent, file, _ids) klasses = list(self.__class__.__mro__) klasses.reverse() if _debug: _log.debug(" - klasses: %r", klasses) # loop through the classes and look for _debug_contents attrs = [] cids = [] ownFn = [] for klass in klasses: if klass is DebugContents: continue if not issubclass(klass, DebugContents) and hasattr(klass, 'debug_contents'): for i, seenAlready in enumerate(ownFn): if issubclass(klass, seenAlready): del ownFn[i] break ownFn.append(klass) continue # look for a tuple of attribute names if not hasattr(klass, '_debug_contents'): continue debugContents = klass._debug_contents if not isinstance(debugContents, tuple): raise RuntimeError("%s._debug_contents must be a tuple" % (klass.__name__,)) # already seen it? if id(debugContents) in cids: continue cids.append(id(debugContents)) for attr in debugContents: if attr not in attrs: attrs.append(attr) # a bit of debugging if _debug: _log.debug(" - attrs: %r", attrs) _log.debug(" - ownFn: %r", ownFn) # make/extend the list of objects already seen if _ids is None: _ids = [] # loop through the attributes for attr in attrs: # assume you're going deep, but not into lists and dicts goDeep = True goListDict = False goHexed = False # attribute list might want to go deep if attr.endswith("-"): goDeep = False attr = attr[:-1] elif attr.endswith("*"): goHexed = True attr = attr[:-1] elif attr.endswith("+"): goDeep = False goListDict = True attr = attr[:-1] if attr.endswith("+"): goDeep = True attr = attr[:-1] value = getattr(self, attr, None) # skip None if value is None: continue # standard output if goListDict and isinstance(value, list) and value: file.write("%s%s = [\n" % (' ' * indent, attr)) indent += 1 for i, elem in enumerate(value): file.write("%s[%d] %r\n" % (' ' * indent, i, elem)) if goDeep and hasattr(elem, 'debug_contents'): if id(elem) not in _ids: _ids.append(id(elem)) elem.debug_contents(indent + 1, file, _ids) indent -= 1 file.write("%s ]\n" % (' ' * indent,)) elif goListDict and isinstance(value, dict) and value: file.write("%s%s = {\n" % (' ' * indent, attr)) indent += 1 for key, elem in value.items(): file.write("%s%r : %r\n" % (' ' * indent, key, elem)) if goDeep and hasattr(elem, 'debug_contents'): if id(elem) not in _ids: _ids.append(id(elem)) elem.debug_contents(indent + 1, file, _ids) indent -= 1 file.write("%s }\n" % (' ' * indent,)) elif goHexed and isinstance(value, str): if len(value) > 20: hexed = btox(value[:20], '.') + "..." else: hexed = btox(value, '.') file.write("%s%s = x'%s'\n" % (' ' * indent, attr, hexed)) # elif goHexed and isinstance(value, int): # file.write("%s%s = 0x%X\n" % (' ' * indent, attr, value)) else: file.write("%s%s = %r\n" % (' ' * indent, attr, value)) # go nested if it is debugable if goDeep and hasattr(value, 'debug_contents'): if id(value) not in _ids: _ids.append(id(value)) value.debug_contents(indent + 1, file, _ids) # go through the functions ownFn.reverse() for klass in ownFn: klass.debug_contents(self, indent, file, _ids) # # LoggingFormatter # class LoggingFormatter(logging.Formatter): def __init__(self, color=None): logging.Formatter.__init__(self, logging.BASIC_FORMAT, None) # check the color if color is not None: if color not in range(8): raise ValueError("colors are 0 (black) through 7 (white)") # save the color self.color = color def format(self, record): try: # use the basic formatting msg = logging.Formatter.format(self, record) + '\n' # look for detailed arguments for arg in record.args: if isinstance(arg, DebugContents): if msg: sio = StringIO() sio.write(msg) msg = None sio.write(" %r\n" % (arg,)) arg.debug_contents(indent=2, file=sio) # get the message from the StringIO buffer if not msg: msg = sio.getvalue() # trim off the last '\n' msg = msg[:-1] except Exception as err: record_attrs = [ attr + ": " + str(getattr(record, attr, "N/A")) for attr in ('name', 'level', 'pathname', 'lineno', 'msg', 'args', 'exc_info', 'func') ] record_attrs[:0] = ["LoggingFormatter exception: " + str(err)] msg = "\n ".join(record_attrs) if self.color is not None: msg = "\x1b[%dm" % (30+self.color,) + msg + "\x1b[0m" return msg # # bacpypes_debugging # def bacpypes_debugging(obj): """Function for attaching a debugging logger to a class or function.""" # create a logger for this object logger = logging.getLogger(obj.__module__ + '.' + obj.__name__) # make it available to instances obj._logger = logger obj._debug = logger.debug obj._info = logger.info obj._warning = logger.warning obj._error = logger.error obj._exception = logger.exception obj._fatal = logger.fatal return obj # # _LoggingMetaclass # class _LoggingMetaclass(type): def __init__(cls, *args): # wrap the class bacpypes_debugging(cls) # # Logging # class Logging(object): __metaclass__ = _LoggingMetaclass # # class_debugging # def class_debugging(cls): """Add the debugging logger to the class.""" bacpypes_debugging(cls) return cls # # function_debugging # def function_debugging(f): """Add the debugging logger to the function.""" bacpypes_debugging(f) return f PKG5Hmu  bacpypes/errors.py#!/usr/bin/python # # ConfigurationError # class ConfigurationError(ValueError): """This error is raised when there is a configuration problem such as bindings between layers or required parameters that are missing. """ def __init__(self, *args): self.args = args # # EncodingError # class EncodingError(ValueError): """This error is raised if there is a problem during encoding. """ def __init__(self, *args): self.args = args # # DecodingError # class DecodingError(ValueError): """This error is raised if there is a problem during decoding. """ def __init__(self, *args): self.args = args # # ExecutionError # class ExecutionError(RuntimeError): """This error is raised for if there is an error during the execution of a service or function at the application layer of stack and the error translated into an ErrorPDU. """ def __init__(self, errorClass, errorCode): self.errorClass = errorClass self.errorCode = errorCode self.args = (errorClass, errorCode) # # Reject Exception Family # class RejectException(Exception): """Exceptions in this family correspond to reject reasons. If the application raises one of these errors while processing a confirmed service request, the stack will form an appropriate RejectPDU and send it to the client. """ rejectReason = None def __init__(self, *args): if not self.rejectReason: raise NotImplementedError("use a derived class") # save the args self.args = args class RejectOther(RejectException): """Generated in response to a confirmed request APDU that contains a syntax error for which an error code has not been explicitly defined. """ rejectReason = 'other' class RejectBufferOverflow(RejectException): """A buffer capacity has been exceeded. """ rejectReason = 'bufferOverflow' class InconsistentParameters(RejectException): """Generated in response to a confirmed request APDU that omits a conditional service argument that should be present or contains a conditional service argument that should not be present. This condition could also elicit a Reject PDU with a Reject Reason of INVALID_TAG. """ rejectReason = 'inconsistentParameters' class InvalidParameterDatatype(RejectException): """Generated in response to a confirmed request APDU in which the encoding of one or more of the service parameters does not follow the correct type specification. This condition could also elicit a Reject PDU with a Reject Reason of INVALID_TAG. """ rejectReason = 'invalidParameterDatatype' class InvalidTag(RejectException): """While parsing a message, an invalid tag was encountered. Since an invalid tag could confuse the parsing logic, any of the following Reject Reasons may also be generated in response to a confirmed request containing an invalid tag: INCONSISTENT_PARAMETERS, INVALID_PARAMETER_DATA_TYPE, MISSING_REQUIRED_PARAMETER, and TOO_MANY_ARGUMENTS. """ rejectReason = 'invalidTag' class MissingRequiredParameter(RejectException): """Generated in response to a confirmed request APDU that is missing at least one mandatory service argument. This condition could also elicit a Reject PDU with a Reject Reason of INVALID_TAG. """ rejectReason = 'missingRequiredParameter' class ParameterOutOfRange(RejectException): """Generated in response to a confirmed request APDU that conveys a parameter whose value is outside the range defined for this service. """ rejectReason = 'parameterOutOfRange' class TooManyArguments(RejectException): """Generated in response to a confirmed request APDU in which the total number of service arguments is greater than specified for the service. This condition could also elicit a Reject PDU with a Reject Reason of INVALID_TAG. """ rejectReason = 'tooManyArguments' class UndefinedEnumeration(RejectException): """Generated in response to a confirmed request APDU in which one or more of the service parameters is decoded as an enumeration that is not defined by the type specification of this parameter. """ rejectReason = 'undefinedEnumeration' class UnrecognizedService(RejectException): """Generated in response to a confirmed request APDU in which the Service Choice field specifies an unknown or unsupported service. """ rejectReason = 'unrecognizedService' # # Abort Exception Family # class AbortException(Exception): """Exceptions in this family correspond to abort reasons. If the application raises one of these errors while processing a confirmed service request, the stack will form an appropriate AbortPDU and send it to the client. """ abortReason = None def __init__(self, *args): if not self.abortReason: raise NotImplementedError("use a derived class") # save the args self.args = args class AbortOther(AbortException): """This abort reason is returned for a reason other than any of those for which an error code has not been explicitly defined. """ abortReason = 'other' class AbortBufferOverflow(AbortException): """A buffer capacity has been exceeded. """ abortReason = 'bufferOverflow' class InvalidAPDUInThisState(AbortException): """Generated in response to an APDU that is not expected in the present state of the Transaction State Machine. """ abortReason = 'invalidApduInThisState' class PreemptedByHigherPriorityTask(AbortException): """The transaction shall be aborted to permit higher priority processing. """ abortReason = 'preemptedByHigherPriorityTask' class SegmentationNotSupported(AbortException): """Generated in response to an APDU that has its segmentation bit set to TRUE when the receiving device does not support segmentation. It is also generated when a BACnet-ComplexACK- PDU is large enough to require segmentation but it cannot be transmitted because either the transmitting device or the receiving device does not support segmentation. """ abortReason = 'segmentationNotSupported' class SecurityError(AbortException): """The Transaction is aborted due to receipt of a security error. """ abortReason = 'securityError' class InsufficientSecurity(AbortException): """The transaction is aborted due to receipt of a PDU secured differently than the original PDU of the transaction. """ abortReason = 'insufficientSecurity' class WindowSizeOutOfRange(AbortException): """A device receives a request that is segmented, or receives any segment of a segmented request, where the Proposed Window Size field of the PDU header is either zero or greater than 127. """ abortReason = 'windowSizeOutOfRange' class ApplicationExceededReplyTime(AbortException): """A device receives a confirmed request but its application layer has not responded within the published APDU Timeout period. """ abortReason = 'applicationExceededReplyTime' class OutOfResources(AbortException): """A device receives a request but cannot start processing because it has run out of some internal resource. """ abortReason = 'outOfResources' class TSMTimeout(AbortException): """A transaction state machine timer exceeded the timeout applicable for the current state, causing the transaction machine to abort the transaction. """ abortReason = 'tsmTimeout' class APDUTooLong(AbortException): """An APDU was received from the local application program whose overall size exceeds the maximum transmittable length or exceeds the maximum number of segments accepted by the server. """ abortReason = 'apduTooLong' class ServerTimeout(AbortException): """BACpypes specific. """ abortReason = 'serverTimeout' class NoResponse(AbortException): """BACpypes specific. """ abortReason = 'noResponse' PK9(G'abacpypes/consolelogging.py#!/usr/bin/python """ Console Logging """ import sys import logging import argparse from .debugging import bacpypes_debugging, LoggingFormatter, ModuleLogger from configparser import ConfigParser as _ConfigParser # some debugging _debug = 0 _log = ModuleLogger(globals()) # # ConsoleLogHandler # def ConsoleLogHandler(loggerRef='', level=logging.DEBUG, color=None): """Add a stream handler to stderr with our custom formatter to a logger.""" if isinstance(loggerRef, logging.Logger): pass elif isinstance(loggerRef, str): # check for root if not loggerRef: loggerRef = _log # check for a valid logger name elif loggerRef not in logging.Logger.manager.loggerDict: raise RuntimeError("not a valid logger name: %r" % (loggerRef,)) # get the logger loggerRef = logging.getLogger(loggerRef) else: raise RuntimeError("not a valid logger reference: %r" % (loggerRef,)) # see if this (or its parent) is a module level logger if hasattr(loggerRef, 'globs'): loggerRef.globs['_debug'] += 1 elif hasattr(loggerRef.parent, 'globs'): loggerRef.parent.globs['_debug'] += 1 # make a debug handler hdlr = logging.StreamHandler() hdlr.setLevel(level) # use our formatter hdlr.setFormatter(LoggingFormatter(color)) # add it to the logger loggerRef.addHandler(hdlr) # make sure the logger has at least this level loggerRef.setLevel(level) # # ArgumentParser # @bacpypes_debugging class ArgumentParser(argparse.ArgumentParser): """ ArgumentParser extends the one with the same name from the argparse module by adding the common command line arguments found in BACpypes applications. --buggers list the debugging logger names --debug [DBEUG [DEBUG ...]] attach a console to loggers --color debug in color """ def __init__(self, **kwargs): """Follow normal initialization and add BACpypes arguments.""" if _debug: ArgumentParser._debug("__init__") argparse.ArgumentParser.__init__(self, **kwargs) # add a way to get a list of the debugging hooks self.add_argument("--buggers", help="list the debugging logger names", action="store_true", ) # add a way to attach debuggers self.add_argument('--debug', nargs='*', help="add console log handler to each debugging logger", ) # add a way to turn on color debugging self.add_argument("--color", help="turn on color debugging", action="store_true", ) def parse_args(self, *args, **kwargs): """Parse the arguments as usual, then add default processing.""" if _debug: ArgumentParser._debug("parse_args") # pass along to the parent class result_args = argparse.ArgumentParser.parse_args(self, *args, **kwargs) # check to dump labels if result_args.buggers: loggers = sorted(logging.Logger.manager.loggerDict.keys()) for loggerName in loggers: sys.stdout.write(loggerName + '\n') sys.exit(0) # check for debug if result_args.debug is None: # --debug not specified bug_list = [] elif not result_args.debug: # --debug, but no arguments bug_list = ["__main__"] else: # --debug with arguments bug_list = result_args.debug # attach any that are specified if result_args.color: for i, debug_name in enumerate(bug_list): ConsoleLogHandler(debug_name, color=(i % 6) + 2) else: for debug_name in bug_list: ConsoleLogHandler(debug_name) # return what was parsed return result_args # # ConfigArgumentParser # @bacpypes_debugging class ConfigArgumentParser(ArgumentParser): """ ConfigArgumentParser extends the ArgumentParser with the functionality to read in a configuration file. --ini INI provide a separate INI file """ def __init__(self, **kwargs): """Follow normal initialization and add BACpypes arguments.""" if _debug: ConfigArgumentParser._debug("__init__") ArgumentParser.__init__(self, **kwargs) # add a way to read a configuration file self.add_argument('--ini', help="device object configuration file", default="BACpypes.ini", ) def parse_args(self, *args, **kwargs): """Parse the arguments as usual, then add default processing.""" if _debug: ConfigArgumentParser._debug("parse_args") # pass along to the parent class result_args = ArgumentParser.parse_args(self, *args, **kwargs) # read in the configuration file config = _ConfigParser() config.read(result_args.ini) if _debug: _log.debug(" - config: %r", config) # check for BACpypes section if not config.has_section('BACpypes'): raise RuntimeError("INI file with BACpypes section required") # convert the contents to an object ini_obj = type('ini', (object,), dict(config.items('BACpypes'))) if _debug: _log.debug(" - ini_obj: %r", ini_obj) # add the object to the parsed arguments setattr(result_args, 'ini', ini_obj) # return what was parsed return result_args PK'iFU|:^:^bacpypes/bsll.py#!/usr/bin/python """ BACnet Streaming Link Layer Module """ import hashlib from .errors import EncodingError, DecodingError from .debugging import ModuleLogger, DebugContents, bacpypes_debugging from .pdu import LocalStation, PCI, PDUData # some debugging _debug = 0 _log = ModuleLogger(globals()) # a dictionary of message type values and classes bsl_pdu_types = {} def register_bslpdu_type(klass): bsl_pdu_types[klass.messageType] = klass # # Service Identifiers # DEVICE_TO_DEVICE_SERVICE_ID = 0x01 ROUTER_TO_ROUTER_SERVICE_ID = 0x02 PROXY_SERVICE_ID = 0x03 LANE_SERVICE_ID = 0x04 CLIENT_SERVER_SERVICE_ID = 0x05 # # Hash Functions # _md5 = lambda x: hashlib.md5(x).digest() _sha1 = lambda x: hashlib.sha1(x).digest() _sha224 = lambda x: hashlib.sha224(x).digest() _sha256 = lambda x: hashlib.sha256(x).digest() _sha384 = lambda x: hashlib.sha384(x).digest() _sha512 = lambda x: hashlib.sha512(x).digest() hash_functions = (_md5, _sha1, _sha224, _sha256, _sha384, _sha512) # # Result Codes # SUCCESS = 0 NO_DEVICE_TO_DEVICE_SERVICE = 1 NO_ROUTER_TO_ROUTER_SERVICE = 2 NO_PROXY_SERVICE = 3 NO_LANE_SERVICE = 4 UNRECOGNIZED_SERVICE = 5 AUTHENTICATION_REQUIRED = 10 # authentication required AUTHENTICATION_FAILURE = 11 # username and/or username/password failure AUTHENTICATION_NO_SERVICE = 12 # AUTHENTICATION_HASH = 13 # specified hash function not supported # # BSLCI # @bacpypes_debugging class BSLCI(PCI, DebugContents): _debug_contents = ('bslciType', 'bslciFunction', 'bslciLength') result = 0x00 serviceRequest = 0x01 accessRequest = 0x02 accessChallenge = 0x03 accessResponse = 0x04 deviceToDeviceAPDU = 0x05 routerToRouterNPDU = 0x06 proxyToServerUnicastNPDU = 0x07 proxyToServerBroadcastNPDU = 0x08 serverToProxyUnicastNPDU = 0x09 serverToProxyBroadcastNPDU = 0x0A clientToLESUnicastNPDU = 0x0B clientToLESBroadcastNPDU = 0x0C lesToClientUnicastNPDU = 0x0D lesToClientBroadcastNPDU = 0x0E clientToServerUnicastAPDU = 0x0F clientToServerBroadcastAPDU = 0x10 serverToClientUnicastAPDU = 0x11 serverToClientBroadcastAPDU = 0x12 def __init__(self, *args, **kwargs): super(BSLCI, self).__init__(*args, **kwargs) self.bslciType = 0x83 self.bslciFunction = None self.bslciLength = None def update(self, bslci): PCI.update(self, bslci) self.bslciType = bslci.bslciType self.bslciFunction = bslci.bslciFunction self.bslciLength = bslci.bslciLength def encode(self, pdu): """encode the contents of the BSLCI into the PDU.""" if _debug: BSLCI._debug("encode %r", pdu) # copy the basics PCI.update(pdu, self) pdu.put( self.bslciType ) # 0x83 pdu.put( self.bslciFunction ) if (self.bslciLength != len(self.pduData) + 4): raise EncodingError("invalid BSLCI length") pdu.put_short( self.bslciLength ) def decode(self, pdu): """decode the contents of the PDU into the BSLCI.""" if _debug: BSLCI._debug("decode %r", pdu) # copy the basics PCI.update(self, pdu) self.bslciType = pdu.get() if self.bslciType != 0x83: raise DecodingError("invalid BSLCI type") self.bslciFunction = pdu.get() self.bslciLength = pdu.get_short() if (self.bslciLength != len(pdu.pduData) + 4): raise DecodingError("invalid BSLCI length") # # BSLPDU # class BSLPDU(BSLCI, PDUData): def __init__(self, *args, **kwargs): super(BSLPDU, self).__init__(*args, **kwargs) def encode(self, pdu): BSLCI.encode(self, pdu) pdu.put_data(self.pduData) def decode(self, pdu): BSLCI.decode(self, pdu) self.pduData = pdu.get_data(len(pdu.pduData)) # # Result # class Result(BSLCI): _debug_contents = ('bslciResultCode',) messageType = BSLCI.result def __init__(self, code=None, *args, **kwargs): super(Result, self).__init__(*args, **kwargs) self.bslciFunction = BSLCI.result self.bslciLength = 6 self.bslciResultCode = code def encode(self, bslpdu): BSLCI.update(bslpdu, self) bslpdu.put_short( self.bslciResultCode ) def decode(self, bslpdu): BSLCI.update(self, bslpdu) self.bslciResultCode = bslpdu.get_short() register_bslpdu_type(Result) # # ServiceRequest # class ServiceRequest(BSLCI): _debug_contents = ('bslciServiceID',) messageType = BSLCI.serviceRequest def __init__(self, code=None, *args, **kwargs): super(ServiceRequest, self).__init__(*args, **kwargs) self.bslciFunction = BSLCI.serviceRequest self.bslciLength = 6 self.bslciServiceID = code def encode(self, bslpdu): BSLCI.update(bslpdu, self) bslpdu.put_short( self.bslciServiceID ) def decode(self, bslpdu): BSLCI.update(self, bslpdu) self.bslciServiceID = bslpdu.get_short() register_bslpdu_type(ServiceRequest) # # AccessRequest # class AccessRequest(BSLCI): _debug_contents = ('bslciHashFn', 'bslciUsername') messageType = BSLCI.accessRequest def __init__(self, hashFn=0, username='', *args, **kwargs): super(AccessRequest, self).__init__(*args, **kwargs) self.bslciFunction = BSLCI.accessRequest self.bslciLength = 5 self.bslciHashFn = hashFn self.bslciUsername = username if username: self.bslciLength += len(username) def encode(self, bslpdu): BSLCI.update(bslpdu, self) bslpdu.put( self.bslciHashFn ) bslpdu.put_data( self.bslciUsername ) def decode(self, bslpdu): BSLCI.update(self, bslpdu) self.bslciHashFn = bslpdu.get() self.bslciUsername = bslpdu.get_data(len(bslpdu.pduData)) register_bslpdu_type(AccessRequest) # # AccessChallenge # class AccessChallenge(BSLCI): _debug_contents = ('bslciHashFn', 'bslciChallenge*') messageType = BSLCI.accessChallenge def __init__(self, hashFn=0, challenge='', *args, **kwargs): super(AccessChallenge, self).__init__(*args, **kwargs) self.bslciFunction = BSLCI.accessChallenge self.bslciLength = 5 self.bslciHashFn = hashFn self.bslciChallenge = challenge if challenge: self.bslciLength += len(challenge) def encode(self, bslpdu): BSLCI.update(bslpdu, self) bslpdu.put( self.bslciHashFn ) bslpdu.put_data( self.bslciChallenge ) def decode(self, bslpdu): BSLCI.update(self, bslpdu) self.bslciHashFn = bslpdu.get() self.bslciChallenge = bslpdu.get_data(len(bslpdu.pduData)) register_bslpdu_type(AccessChallenge) # # AccessResponse # class AccessResponse(BSLCI): _debug_contents = ('bslciHashFn', 'bslciResponse*') messageType = BSLCI.accessResponse def __init__(self, hashFn=0, response='', *args, **kwargs): super(AccessResponse, self).__init__(*args, **kwargs) self.bslciFunction = BSLCI.accessResponse self.bslciLength = 5 self.bslciHashFn = hashFn self.bslciResponse = response if response: self.bslciLength += len(response) def encode(self, bslpdu): BSLCI.update(bslpdu, self) bslpdu.put( self.bslciHashFn ) bslpdu.put_data( self.bslciResponse ) def decode(self, bslpdu): BSLCI.update(self, bslpdu) self.bslciHashFn = bslpdu.get() self.bslciResponse = bslpdu.get_data(len(bslpdu.pduData)) register_bslpdu_type(AccessResponse) #------------------------------ # # DeviceToDeviceAPDU # class DeviceToDeviceAPDU(BSLPDU): messageType = BSLCI.deviceToDeviceAPDU def __init__(self, *args, **kwargs): super(DeviceToDeviceAPDU, self).__init__(*args, **kwargs) self.bslciFunction = BSLCI.deviceToDeviceAPDU self.bslciLength = 4 + len(self.pduData) def encode(self, bslpdu): # make sure the length is correct self.bslciLength = 4 + len(self.pduData) BSLCI.update(bslpdu, self) # encode the rest of the data bslpdu.put_data( self.pduData ) def decode(self, bslpdu): BSLCI.update(self, bslpdu) # get the rest of the data self.pduData = bslpdu.get_data(len(bslpdu.pduData)) register_bslpdu_type(DeviceToDeviceAPDU) # # RouterToRouterNPDU # class RouterToRouterNPDU(BSLPDU): messageType = BSLCI.routerToRouterNPDU def __init__(self, *args, **kwargs): super(RouterToRouterNPDU, self).__init__(*args, **kwargs) self.bslciFunction = BSLCI.routerToRouterNPDU self.bslciLength = 4 + len(self.pduData) def encode(self, bslpdu): # make sure the length is correct self.bslciLength = 4 + len(self.pduData) BSLCI.update(bslpdu, self) # encode the rest of the data bslpdu.put_data( self.pduData ) def decode(self, bslpdu): BSLCI.update(self, bslpdu) # get the rest of the data self.pduData = bslpdu.get_data(len(bslpdu.pduData)) register_bslpdu_type(RouterToRouterNPDU) # # ProxyToServerUnicastNPDU # class ProxyToServerUnicastNPDU(BSLPDU): _debug_contents = ('bslciAddress',) messageType = BSLCI.proxyToServerUnicastNPDU def __init__(self, addr=None, *args, **kwargs): super(ProxyToServerUnicastNPDU, self).__init__(*args, **kwargs) self.bslciFunction = BSLCI.proxyToServerUnicastNPDU self.bslciLength = 5 + len(self.pduData) self.bslciAddress = addr if addr is not None: self.bslciLength += addr.addrLen def encode(self, bslpdu): addrLen = self.bslciAddress.addrLen # make sure the length is correct self.bslciLength = 5 + addrLen + len(self.pduData) BSLCI.update(bslpdu, self) # encode the address bslpdu.put(addrLen) bslpdu.put_data( self.bslciAddress.addrAddr ) # encode the rest of the data bslpdu.put_data( self.pduData ) def decode(self, bslpdu): BSLCI.update(self, bslpdu) # get the address addrLen = bslpdu.get() self.bslciAddress = LocalStation(bslpdu.get_data(addrLen)) # get the rest of the data self.pduData = bslpdu.get_data(len(bslpdu.pduData)) register_bslpdu_type(ProxyToServerUnicastNPDU) # # ProxyToServerBroadcastNPDU # class ProxyToServerBroadcastNPDU(BSLPDU): _debug_contents = ('bslciAddress',) messageType = BSLCI.proxyToServerBroadcastNPDU def __init__(self, addr=None, *args, **kwargs): super(ProxyToServerBroadcastNPDU, self).__init__(*args, **kwargs) self.bslciFunction = BSLCI.proxyToServerBroadcastNPDU self.bslciLength = 5 + len(self.pduData) self.bslciAddress = addr if addr is not None: self.bslciLength += addr.addrLen def encode(self, bslpdu): addrLen = self.bslciAddress.addrLen # make sure the length is correct self.bslciLength = 5 + addrLen + len(self.pduData) BSLCI.update(bslpdu, self) # encode the address bslpdu.put(addrLen) bslpdu.put_data( self.bslciAddress.addrAddr ) # encode the rest of the data bslpdu.put_data( self.pduData ) def decode(self, bslpdu): BSLCI.update(self, bslpdu) # get the address addrLen = bslpdu.get() self.bslciAddress = LocalStation(bslpdu.get_data(addrLen)) # get the rest of the data self.pduData = bslpdu.get_data(len(bslpdu.pduData)) register_bslpdu_type(ProxyToServerBroadcastNPDU) # # ServerToProxyUnicastNPDU # class ServerToProxyUnicastNPDU(BSLPDU): _debug_contents = ('bslciAddress',) messageType = BSLCI.serverToProxyUnicastNPDU def __init__(self, addr=None, *args, **kwargs): super(ServerToProxyUnicastNPDU, self).__init__(*args, **kwargs) self.bslciFunction = BSLCI.serverToProxyUnicastNPDU self.bslciLength = 5 + len(self.pduData) self.bslciAddress = addr if addr is not None: self.bslciLength += addr.addrLen def encode(self, bslpdu): addrLen = self.bslciAddress.addrLen # make sure the length is correct self.bslciLength = 5 + addrLen + len(self.pduData) BSLCI.update(bslpdu, self) # encode the address bslpdu.put(addrLen) bslpdu.put_data( self.bslciAddress.addrAddr ) # encode the rest of the data bslpdu.put_data( self.pduData ) def decode(self, bslpdu): BSLCI.update(self, bslpdu) # get the address addrLen = bslpdu.get() self.bslciAddress = LocalStation(bslpdu.get_data(addrLen)) # get the rest of the data self.pduData = bslpdu.get_data(len(bslpdu.pduData)) register_bslpdu_type(ServerToProxyUnicastNPDU) # # ServerToProxyBroadcastNPDU # class ServerToProxyBroadcastNPDU(BSLPDU): messageType = BSLCI.serverToProxyBroadcastNPDU def __init__(self, *args, **kwargs): super(ServerToProxyBroadcastNPDU, self).__init__(*args, **kwargs) self.bslciFunction = BSLCI.serverToProxyBroadcastNPDU self.bslciLength = 4 + len(self.pduData) def encode(self, bslpdu): BSLCI.update(bslpdu, self) # encode the rest of the data bslpdu.put_data( self.pduData ) def decode(self, bslpdu): BSLCI.update(self, bslpdu) # get the rest of the data self.pduData = bslpdu.get_data(len(bslpdu.pduData)) register_bslpdu_type(ServerToProxyBroadcastNPDU) # # ClientToLESUnicastNPDU # class ClientToLESUnicastNPDU(BSLPDU): _debug_contents = ('bslciAddress',) messageType = BSLCI.clientToLESUnicastNPDU def __init__(self, addr=None, *args, **kwargs): super(ClientToLESUnicastNPDU, self).__init__(*args, **kwargs) self.bslciFunction = BSLCI.clientToLESUnicastNPDU self.bslciLength = 5 + len(self.pduData) self.bslciAddress = addr if addr is not None: self.bslciLength += addr.addrLen def encode(self, bslpdu): addrLen = self.bslciAddress.addrLen # make sure the length is correct self.bslciLength = 5 + addrLen + len(self.pduData) BSLCI.update(bslpdu, self) # encode the address bslpdu.put(addrLen) bslpdu.put_data( self.bslciAddress.addrAddr ) # encode the rest of the data bslpdu.put_data( self.pduData ) def decode(self, bslpdu): BSLCI.update(self, bslpdu) # get the address addrLen = bslpdu.get() self.bslciAddress = LocalStation(bslpdu.get_data(addrLen)) # get the rest of the data self.pduData = bslpdu.get_data(len(bslpdu.pduData)) register_bslpdu_type(ClientToLESUnicastNPDU) # # ClientToLESBroadcastNPDU # class ClientToLESBroadcastNPDU(BSLPDU): _debug_contents = ('bslciAddress',) messageType = BSLCI.clientToLESBroadcastNPDU def __init__(self, addr=None, *args, **kwargs): super(ClientToLESBroadcastNPDU, self).__init__(*args, **kwargs) self.bslciFunction = BSLCI.clientToLESBroadcastNPDU self.bslciLength = 5 + len(self.pduData) self.bslciAddress = addr if addr is not None: self.bslciLength += addr.addrLen def encode(self, bslpdu): addrLen = self.bslciAddress.addrLen # make sure the length is correct self.bslciLength = 5 + addrLen + len(self.pduData) BSLCI.update(bslpdu, self) # encode the address bslpdu.put(addrLen) bslpdu.put_data( self.bslciAddress.addrAddr ) # encode the rest of the data bslpdu.put_data( self.pduData ) def decode(self, bslpdu): BSLCI.update(self, bslpdu) # get the address addrLen = bslpdu.get() self.bslciAddress = LocalStation(bslpdu.get_data(addrLen)) # get the rest of the data self.pduData = bslpdu.get_data(len(bslpdu.pduData)) register_bslpdu_type(ClientToLESBroadcastNPDU) # # LESToClientUnicastNPDU # class LESToClientUnicastNPDU(BSLPDU): _debug_contents = ('bslciAddress',) messageType = BSLCI.lesToClientUnicastNPDU def __init__(self, addr=None, *args, **kwargs): super(LESToClientUnicastNPDU, self).__init__(*args, **kwargs) self.bslciFunction = BSLCI.lesToClientUnicastNPDU self.bslciLength = 5 + len(self.pduData) self.bslciAddress = addr if addr is not None: self.bslciLength += addr.addrLen def encode(self, bslpdu): addrLen = self.bslciAddress.addrLen # make sure the length is correct self.bslciLength = 5 + addrLen + len(self.pduData) BSLCI.update(bslpdu, self) # encode the address bslpdu.put(addrLen) bslpdu.put_data( self.bslciAddress.addrAddr ) # encode the rest of the data bslpdu.put_data( self.pduData ) def decode(self, bslpdu): BSLCI.update(self, bslpdu) # get the address addrLen = bslpdu.get() self.bslciAddress = LocalStation(bslpdu.get_data(addrLen)) # get the rest of the data self.pduData = bslpdu.get_data(len(bslpdu.pduData)) register_bslpdu_type(LESToClientUnicastNPDU) # # LESToClientBroadcastNPDU # class LESToClientBroadcastNPDU(BSLPDU): _debug_contents = ('bslciAddress',) messageType = BSLCI.lesToClientBroadcastNPDU def __init__(self, addr=None, *args, **kwargs): super(LESToClientBroadcastNPDU, self).__init__(*args, **kwargs) self.bslciFunction = BSLCI.lesToClientBroadcastNPDU self.bslciLength = 5 + len(self.pduData) self.bslciAddress = addr if addr is not None: self.bslciLength += addr.addrLen def encode(self, bslpdu): addrLen = self.bslciAddress.addrLen # make sure the length is correct self.bslciLength = 5 + addrLen + len(self.pduData) BSLCI.update(bslpdu, self) # encode the address bslpdu.put(addrLen) bslpdu.put_data( self.bslciAddress.addrAddr ) # encode the rest of the data bslpdu.put_data( self.pduData ) def decode(self, bslpdu): BSLCI.update(self, bslpdu) # get the address addrLen = bslpdu.get() self.bslciAddress = LocalStation(bslpdu.get_data(addrLen)) # get the rest of the data self.pduData = bslpdu.get_data(len(bslpdu.pduData)) register_bslpdu_type(LESToClientBroadcastNPDU) # # ClientToServerUnicastAPDU # class ClientToServerUnicastAPDU(BSLPDU): _debug_contents = ('bslciAddress',) messageType = BSLCI.clientToServerUnicastAPDU def __init__(self, addr=None, *args, **kwargs): super(ClientToServerUnicastAPDU, self).__init__(*args, **kwargs) self.bslciFunction = BSLCI.clientToServerUnicastAPDU self.bslciLength = 5 + len(self.pduData) self.bslciAddress = addr if addr is not None: self.bslciLength += addr.addrLen def encode(self, bslpdu): addrLen = self.bslciAddress.addrLen # make sure the length is correct self.bslciLength = 5 + addrLen + len(self.pduData) BSLCI.update(bslpdu, self) # encode the address bslpdu.put(addrLen) bslpdu.put_data( self.bslciAddress.addrAddr ) # encode the rest of the data bslpdu.put_data( self.pduData ) def decode(self, bslpdu): BSLCI.update(self, bslpdu) # get the address addrLen = bslpdu.get() self.bslciAddress = LocalStation(bslpdu.get_data(addrLen)) # get the rest of the data self.pduData = bslpdu.get_data(len(bslpdu.pduData)) register_bslpdu_type(ClientToServerUnicastAPDU) # # ClientToServerBroadcastAPDU # class ClientToServerBroadcastAPDU(BSLPDU): _debug_contents = ('bslciAddress',) messageType = BSLCI.clientToServerBroadcastAPDU def __init__(self, addr=None, *args, **kwargs): super(ClientToServerBroadcastAPDU, self).__init__(*args, **kwargs) self.bslciFunction = BSLCI.clientToServerBroadcastAPDU self.bslciLength = 5 + len(self.pduData) self.bslciAddress = addr if addr is not None: self.bslciLength += addr.addrLen def encode(self, bslpdu): addrLen = self.bslciAddress.addrLen # make sure the length is correct self.bslciLength = 5 + addrLen + len(self.pduData) BSLCI.update(bslpdu, self) # encode the address bslpdu.put(addrLen) bslpdu.put_data( self.bslciAddress.addrAddr ) # encode the rest of the data bslpdu.put_data( self.pduData ) def decode(self, bslpdu): BSLCI.update(self, bslpdu) # get the address addrLen = bslpdu.get() self.bslciAddress = LocalStation(bslpdu.get_data(addrLen)) # get the rest of the data self.pduData = bslpdu.get_data(len(bslpdu.pduData)) register_bslpdu_type(ClientToServerBroadcastAPDU) # # ServerToClientUnicastAPDU # class ServerToClientUnicastAPDU(BSLPDU): _debug_contents = ('bslciAddress',) messageType = BSLCI.serverToClientUnicastAPDU def __init__(self, addr=None, *args, **kwargs): super(ServerToClientUnicastAPDU, self).__init__(*args, **kwargs) self.bslciFunction = BSLCI.serverToClientUnicastAPDU self.bslciLength = 5 + len(self.pduData) self.bslciAddress = addr if addr is not None: self.bslciLength += addr.addrLen def encode(self, bslpdu): addrLen = self.bslciAddress.addrLen # make sure the length is correct self.bslciLength = 5 + addrLen + len(self.pduData) BSLCI.update(bslpdu, self) # encode the address bslpdu.put(addrLen) bslpdu.put_data( self.bslciAddress.addrAddr ) # encode the rest of the data bslpdu.put_data( self.pduData ) def decode(self, bslpdu): BSLCI.update(self, bslpdu) # get the address addrLen = bslpdu.get() self.bslciAddress = LocalStation(bslpdu.get_data(addrLen)) # get the rest of the data self.pduData = bslpdu.get_data(len(bslpdu.pduData)) register_bslpdu_type(ServerToClientUnicastAPDU) # # ServerToClientBroadcastAPDU # class ServerToClientBroadcastAPDU(BSLPDU): _debug_contents = ('bslciAddress',) messageType = BSLCI.serverToClientBroadcastAPDU def __init__(self, addr=None, *args, **kwargs): super(ServerToClientBroadcastAPDU, self).__init__(*args, **kwargs) self.bslciFunction = BSLCI.serverToClientBroadcastAPDU self.bslciLength = 5 + len(self.pduData) self.bslciAddress = addr if addr is not None: self.bslciLength += addr.addrLen def encode(self, bslpdu): addrLen = self.bslciAddress.addrLen # make sure the length is correct self.bslciLength = 5 + addrLen + len(self.pduData) BSLCI.update(bslpdu, self) # encode the address bslpdu.put(addrLen) bslpdu.put_data( self.bslciAddress.addrAddr ) # encode the rest of the data bslpdu.put_data( self.pduData ) def decode(self, bslpdu): BSLCI.update(self, bslpdu) # get the address addrLen = bslpdu.get() self.bslciAddress = LocalStation(bslpdu.get_data(addrLen)) # get the rest of the data self.pduData = bslpdu.get_data(len(bslpdu.pduData)) register_bslpdu_type(ServerToClientBroadcastAPDU) PK1H~++bacpypes/constructeddata.py#!/usr/bin/python """ Constructed Data """ import sys from .errors import DecodingError from .debugging import ModuleLogger, bacpypes_debugging from .primitivedata import Atomic, ClosingTag, OpeningTag, Tag, TagList, \ Unsigned # some debugging _debug = 0 _log = ModuleLogger(globals()) # # Element # class Element: def __init__(self, name, klass, context=None, optional=False): self.name = name self.klass = klass self.context = context self.optional = optional def __repr__(self): desc = "%s(%s" % (self.__class__.__name__, self.name) desc += " " + self.klass.__name__ if self.context is not None: desc += ", context=%r" % (self.context,) if self.optional: desc += ", optional" desc += ")" return '<' + desc + ' instance at 0x%08x' % (id(self),) + '>' # # Sequence # @bacpypes_debugging class Sequence(object): sequenceElements = [] def __init__(self, *args, **kwargs): """ Create a sequence element, optionally providing attribute/property values. """ if _debug: Sequence._debug("__init__ %r %r", args, kwargs) # split out the keyword arguments that belong to this class my_kwargs = {} other_kwargs = {} for element in self.sequenceElements: if element.name in kwargs: my_kwargs[element.name] = kwargs[element.name] for kw in kwargs: if kw not in my_kwargs: other_kwargs[kw] = kwargs[kw] if _debug: Sequence._debug(" - my_kwargs: %r", my_kwargs) if _debug: Sequence._debug(" - other_kwargs: %r", other_kwargs) # call some superclass, if there is one super(Sequence, self).__init__(*args, **other_kwargs) # set the attribute/property values for the ones provided for element in self.sequenceElements: setattr(self, element.name, my_kwargs.get(element.name, None)) def encode(self, taglist): """ """ if _debug: Sequence._debug("encode %r", taglist) global _sequence_of_classes # make sure we're dealing with a tag list if not isinstance(taglist, TagList): raise TypeError("TagList expected") for element in self.sequenceElements: value = getattr(self, element.name, None) if element.optional and value is None: continue if not element.optional and value is None: raise AttributeError("'%s' is a required element of %s" % (element.name,self.__class__.__name__)) if element.klass in _sequence_of_classes: # might need to encode an opening tag if element.context is not None: taglist.append(OpeningTag(element.context)) if _debug: Sequence._debug(" - build sequence helper: %r %r", element.klass, value) helper = element.klass(value) # encode the value helper.encode(taglist) # might need to encode a closing tag if element.context is not None: taglist.append(ClosingTag(element.context)) elif issubclass(element.klass, (Atomic, AnyAtomic)): # a helper cooperates between the atomic value and the tag if _debug: Sequence._debug(" - build helper: %r %r", element.klass, value) helper = element.klass(value) # build a tag and encode the data into it tag = Tag() helper.encode(tag) # convert it to context encoding iff necessary if element.context is not None: tag = tag.app_to_context(element.context) # now append the tag taglist.append(tag) elif isinstance(value, element.klass): # might need to encode an opening tag if element.context is not None: taglist.append(OpeningTag(element.context)) # encode the value value.encode(taglist) # might need to encode a closing tag if element.context is not None: taglist.append(ClosingTag(element.context)) else: raise TypeError("'%s' must be of type %s" % (element.name, element.klass.__name__)) def decode(self, taglist): if _debug: Sequence._debug("decode %r", taglist) # make sure we're dealing with a tag list if not isinstance(taglist, TagList): raise TypeError("TagList expected") for element in self.sequenceElements: tag = taglist.Peek() # no more elements if tag is None: if element.optional: # omitted optional element setattr(self, element.name, None) elif element.klass in _sequence_of_classes: # empty list setattr(self, element.name, []) else: raise AttributeError("'%s' is a required element of %s" % (element.name,self.__class__.__name__)) # we have been enclosed in a context elif tag.tagClass == Tag.closingTagClass: if not element.optional: raise AttributeError("'%s' is a required element of %s" % (element.name,self.__class__.__name__)) # omitted optional element setattr(self, element.name, None) # check for a sequence element elif element.klass in _sequence_of_classes: # check for context encoding if element.context is not None: if tag.tagClass != Tag.openingTagClass or tag.tagNumber != element.context: if not element.optional: raise DecodingError("'%s' expected opening tag %d" % (element.name, element.context)) else: # omitted optional element setattr(self, element.name, []) continue taglist.Pop() # a helper cooperates between the atomic value and the tag helper = element.klass() helper.decode(taglist) # now save the value setattr(self, element.name, helper.value) # check for context closing tag if element.context is not None: tag = taglist.Pop() if tag.tagClass != Tag.closingTagClass or tag.tagNumber != element.context: raise DecodingError("'%s' expected closing tag %d" % (element.name, element.context)) # check for an atomic element elif issubclass(element.klass, Atomic): # convert it to application encoding if element.context is not None: if tag.tagClass != Tag.contextTagClass or tag.tagNumber != element.context: if not element.optional: raise DecodingError("'%s' expected context tag %d" % (element.name, element.context)) else: setattr(self, element.name, None) continue tag = tag.context_to_app(element.klass._app_tag) else: if tag.tagClass != Tag.applicationTagClass or tag.tagNumber != element.klass._app_tag: if not element.optional: raise DecodingError("'%s' expected application tag %s" % (element.name, Tag._app_tag_name[element.klass._app_tag])) else: setattr(self, element.name, None) continue # consume the tag taglist.Pop() # a helper cooperates between the atomic value and the tag helper = element.klass(tag) # now save the value setattr(self, element.name, helper.value) # check for an AnyAtomic element elif issubclass(element.klass, AnyAtomic): # convert it to application encoding if element.context is not None: if tag.tagClass != Tag.contextTagClass or tag.tagNumber != element.context: if not element.optional: raise DecodingError("'%s' expected context tag %d" % (element.name, element.context)) else: setattr(self, element.name, None) continue tag = tag.context_to_app(element.klass._app_tag) else: if tag.tagClass != Tag.applicationTagClass: if not element.optional: raise DecodingError("'%s' expected application tag" % (element.name,)) else: setattr(self, element.name, None) continue # consume the tag taglist.Pop() # a helper cooperates between the atomic value and the tag helper = element.klass(tag) # now save the value setattr(self, element.name, helper.value) # some kind of structure else: if element.context is not None: if tag.tagClass != Tag.openingTagClass or tag.tagNumber != element.context: if not element.optional: raise DecodingError("'%s' expected opening tag %d" % (element.name, element.context)) else: setattr(self, element.name, None) continue taglist.Pop() try: # make a backup of the tag list in case the structure manages to # decode some content but not all of it. This is not supposed to # happen if the ASN.1 has been formed correctly. backup = taglist.tagList[:] # build a value and decode it value = element.klass() value.decode(taglist) # save the result setattr(self, element.name, value) except DecodingError: # if the context tag was matched, the substructure has to be decoded # correctly. if element.context is None and element.optional: # omitted optional element setattr(self, element.name, None) # restore the backup taglist.tagList = backup else: raise if element.context is not None: tag = taglist.Pop() if (not tag) or tag.tagClass != Tag.closingTagClass or tag.tagNumber != element.context: raise DecodingError("'%s' expected closing tag %d" % (element.name, element.context)) def debug_contents(self, indent=1, file=sys.stdout, _ids=None): global _sequence_of_classes for element in self.sequenceElements: value = getattr(self, element.name, None) if element.optional and value is None: continue if not element.optional and value is None: file.write("%s'%s' is a required element of %s\n" % (" " * indent, element.name, self.__class__.__name__)) continue if element.klass in _sequence_of_classes: file.write("%s%s\n" % (" " * indent, element.name)) helper = element.klass(value) helper.debug_contents(indent+1, file, _ids) elif issubclass(element.klass, (Atomic, AnyAtomic)): file.write("%s%s = %r\n" % (" " * indent, element.name, value)) elif isinstance(value, element.klass): file.write("%s%s\n" % (" " * indent, element.name)) value.debug_contents(indent+1, file, _ids) else: file.write("%s'%s' must be a %s\n" % (" " * indent, element.name, element.klass.__name__)) def dict_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" if _debug: Sequence._debug("dict_contents use_dict=%r as_class=%r", use_dict, as_class) # make/extend the dictionary of content if use_dict is None: use_dict = as_class() # loop through the elements for element in self.sequenceElements: value = getattr(self, element.name, None) if value is None: continue if element.klass in _sequence_of_classes: helper = element.klass(value) mapped_value = helper.dict_contents(as_class=as_class) elif issubclass(element.klass, Atomic): mapped_value = value ### ambiguous elif issubclass(element.klass, AnyAtomic): mapped_value = value.value ### ambiguous elif isinstance(value, element.klass): mapped_value = value.dict_contents(as_class=as_class) use_dict.__setitem__(element.name, mapped_value) else: continue # update the dictionary being built use_dict.__setitem__(element.name, mapped_value) # return what we built/updated return use_dict # # SequenceOf # _sequence_of_map = {} _sequence_of_classes = {} @bacpypes_debugging def SequenceOf(klass): """Function to return a class that can encode and decode a list of some other type.""" if _debug: SequenceOf._debug("SequenceOf %r", klass) global _sequence_of_map global _sequence_of_classes, _array_of_classes # if this has already been built, return the cached one if klass in _sequence_of_map: if _debug: SequenceOf._debug(" - found in cache") return _sequence_of_map[klass] # no SequenceOf(SequenceOf(...)) allowed if klass in _sequence_of_classes: raise TypeError("nested sequences disallowed") # no SequenceOf(ArrayOf(...)) allowed if klass in _array_of_classes: raise TypeError("sequences of arrays disallowed") # define a generic class for lists @bacpypes_debugging class _SequenceOf: subtype = None def __init__(self, value=None): if _debug: _SequenceOf._debug("(%r)__init__ %r (subtype=%r)", self.__class__.__name__, value, self.subtype) if value is None: self.value = [] elif isinstance(value, list): self.value = value else: raise TypeError("invalid constructor datatype") def append(self, value): if issubclass(self.subtype, Atomic): pass elif issubclass(self.subtype, AnyAtomic) and not isinstance(value, Atomic): raise TypeError("instance of an atomic type required") elif not isinstance(value, self.subtype): raise TypeError("%s value required" % (self.subtype.__name__,)) self.value.append(value) def __len__(self): return len(self.value) def __getitem__(self, item): return self.value[item] def encode(self, taglist): if _debug: _SequenceOf._debug("(%r)encode %r", self.__class__.__name__, taglist) for value in self.value: if issubclass(self.subtype, (Atomic, AnyAtomic)): # a helper cooperates between the atomic value and the tag helper = self.subtype(value) # build a tag and encode the data into it tag = Tag() helper.encode(tag) # now encode the tag taglist.append(tag) elif isinstance(value, self.subtype): # it must have its own encoder value.encode(taglist) else: raise TypeError("%s must be a %s" % (value, self.subtype.__name__)) def decode(self, taglist): if _debug: _SequenceOf._debug("(%r)decode %r", self.__class__.__name__, taglist) while len(taglist) != 0: tag = taglist.Peek() if tag.tagClass == Tag.closingTagClass: return if issubclass(self.subtype, (Atomic, AnyAtomic)): if _debug: _SequenceOf._debug(" - building helper: %r %r", self.subtype, tag) taglist.Pop() # a helper cooperates between the atomic value and the tag helper = self.subtype(tag) # save the value self.value.append(helper.value) else: if _debug: _SequenceOf._debug(" - building value: %r", self.subtype) # build an element value = self.subtype() # let it decode itself value.decode(taglist) # save what was built self.value.append(value) def debug_contents(self, indent=1, file=sys.stdout, _ids=None): i = 0 for value in self.value: if issubclass(self.subtype, (Atomic, AnyAtomic)): file.write("%s[%d] = %r\n" % (" " * indent, i, value)) elif isinstance(value, self.subtype): file.write("%s[%d]" % (" " * indent, i)) value.debug_contents(indent+1, file, _ids) else: file.write("%s[%d] %s must be a %s" % (" " * indent, i, value, self.subtype.__name__)) i += 1 def dict_contents(self, use_dict=None, as_class=dict): # return sequences as arrays mapped_value = [] for value in self.value: if issubclass(self.subtype, Atomic): mapped_value.append(value) ### ambiguous elif issubclass(self.subtype, AnyAtomic): mapped_value.append(value.value) ### ambiguous elif isinstance(value, self.subtype): mapped_value.append(value.dict_contents(as_class=as_class)) # return what we built return mapped_value # constrain it to a list of a specific type of item setattr(_SequenceOf, 'subtype', klass) _SequenceOf.__name__ = 'SequenceOf' + klass.__name__ if _debug: SequenceOf._debug(" - build this class: %r", _SequenceOf) # cache this type _sequence_of_map[klass] = _SequenceOf _sequence_of_classes[_SequenceOf] = 1 # return this new type return _SequenceOf # # Array # # Arrays of things are a derived class of Array to make it easier to check # to see if a property is an array of something. # class Array(object): pass # # ArrayOf # _array_of_map = {} _array_of_classes = {} def ArrayOf(klass): """Function to return a class that can encode and decode a list of some other type.""" global _array_of_map global _array_of_classes, _sequence_of_classes # if this has already been built, return the cached one if klass in _array_of_map: return _array_of_map[klass] # no ArrayOf(ArrayOf(...)) allowed if klass in _array_of_classes: raise TypeError("nested arrays disallowed") # no ArrayOf(SequenceOf(...)) allowed if klass in _sequence_of_classes: raise TypeError("arrays of SequenceOf disallowed") # define a generic class for arrays @bacpypes_debugging class ArrayOf(Array): subtype = None def __init__(self, value=None): if value is None: self.value = [0] elif isinstance(value, list): self.value = [len(value)] self.value.extend(value) else: raise TypeError("invalid constructor datatype") def append(self, value): if issubclass(self.subtype, Atomic): pass elif issubclass(self.subtype, AnyAtomic) and not isinstance(value, Atomic): raise TypeError("instance of an atomic type required") elif not isinstance(value, self.subtype): raise TypeError("%s value required" % (self.subtype.__name__,)) self.value.append(value) self.value[0] = len(self.value) - 1 def __len__(self): return self.value[0] def __getitem__(self, item): # no wrapping index if (item < 0) or (item > self.value[0]): raise IndexError("index out of range") return self.value[item] def __setitem__(self, item, value): # no wrapping index if (item < 1) or (item > self.value[0]): raise IndexError("index out of range") # special length handling for index 0 if item == 0: if value < self.value[0]: # trim self.value = self.value[0:value + 1] elif value > self.value[0]: # extend self.value.extend( [None] * (value - self.value[0]) ) else: return self.value[0] = value else: self.value[item] = value def __delitem__(self, item): # no wrapping index if (item < 1) or (item > self.value[0]): raise IndexError("index out of range") # delete the item and update the length del self.value[item] self.value[0] -= 1 def index(self, value): # only search through values for i in range(1, self.value[0] + 1): if value == self.value[i]: return i # not found raise ValueError("%r not in array" % (value,)) def encode(self, taglist): if _debug: ArrayOf._debug("(%r)encode %r", self.__class__.__name__, taglist) for value in self.value[1:]: if issubclass(self.subtype, (Atomic, AnyAtomic)): # a helper cooperates between the atomic value and the tag helper = self.subtype(value) # build a tag and encode the data into it tag = Tag() helper.encode(tag) # now encode the tag taglist.append(tag) elif isinstance(value, self.subtype): # it must have its own encoder value.encode(taglist) else: raise TypeError("%s must be a %s" % (value, self.subtype.__name__)) def decode(self, taglist): if _debug: ArrayOf._debug("(%r)decode %r", self.__class__.__name__, taglist) # start with an empty array self.value = [0] while len(taglist) != 0: tag = taglist.Peek() if tag.tagClass == Tag.closingTagClass: break if issubclass(self.subtype, (Atomic, AnyAtomic)): if _debug: ArrayOf._debug(" - building helper: %r %r", self.subtype, tag) taglist.Pop() # a helper cooperates between the atomic value and the tag helper = self.subtype(tag) # save the value self.value.append(helper.value) else: if _debug: ArrayOf._debug(" - building value: %r", self.subtype) # build an element value = self.subtype() # let it decode itself value.decode(taglist) # save what was built self.value.append(value) # update the length self.value[0] = len(self.value) - 1 def encode_item(self, item, taglist): if _debug: ArrayOf._debug("(%r)encode_item %r %r", self.__class__.__name__, item, taglist) if item == 0: # a helper cooperates between the atomic value and the tag helper = Unsigned(self.value[0]) # build a tag and encode the data into it tag = Tag() helper.encode(tag) # now encode the tag taglist.append(tag) else: value = self.value[item] if issubclass(self.subtype, (Atomic, AnyAtomic)): # a helper cooperates between the atomic value and the tag helper = self.subtype(self.value[item]) # build a tag and encode the data into it tag = Tag() helper.encode(tag) # now encode the tag taglist.append(tag) elif isinstance(value, self.subtype): # it must have its own encoder value.encode(taglist) else: raise TypeError("%s must be a %s" % (value, self.subtype.__name__)) def decode_item(self, item, taglist): if _debug: ArrayOf._debug("(%r)decode_item %r %r", self.__class__.__name__, item, taglist) if item == 0: # a helper cooperates between the atomic value and the tag helper = Unsigned(taglist.Pop()) # save the value self.value = helper.value elif issubclass(self.subtype, (Atomic, AnyAtomic)): if _debug: ArrayOf._debug(" - building helper: %r", self.subtype) # a helper cooperates between the atomic value and the tag helper = self.subtype(taglist.Pop()) # save the value self.value = helper.value else: if _debug: ArrayOf._debug(" - building value: %r", self.subtype) # build an element value = self.subtype() # let it decode itself value.decode(taglist) # save what was built self.value = value def debug_contents(self, indent=1, file=sys.stdout, _ids=None): try: value_list = enumerate(self.value) except TypeError: file.write("%s(non-sequence) %r\n" % (" " * indent, self.value)) return for i, value in value_list: if i == 0: file.write("%slength = %d\n" % (" " * indent, value)) elif issubclass(self.subtype, (Atomic, AnyAtomic)): file.write("%s[%d] = %r\n" % (" " * indent, i, value)) elif isinstance(value, self.subtype): file.write("%s[%d]\n" % (" " * indent, i)) value.debug_contents(indent+1, file, _ids) else: file.write("%s%s must be a %s" % (" " * indent, value, self.subtype.__name__)) def dict_contents(self, use_dict=None, as_class=dict): # return arrays as arrays mapped_value = [] for value in self.value: if issubclass(self.subtype, Atomic): mapped_value.append(value) ### ambiguous elif issubclass(self.subtype, AnyAtomic): mapped_value.append(value.value) ### ambiguous elif isinstance(value, self.subtype): mapped_value.append(value.dict_contents(as_class=as_class)) # return what we built return mapped_value # constrain it to a list of a specific type of item setattr(ArrayOf, 'subtype', klass) ArrayOf.__name__ = 'ArrayOf' + klass.__name__ # cache this type _array_of_map[klass] = ArrayOf _array_of_classes[ArrayOf] = 1 # return this new type return ArrayOf # # Choice # @bacpypes_debugging class Choice(object): choiceElements = [] def __init__(self, **kwargs): """ Create a choice element, optionally providing attribute/property values. There should only be one, but that is not strictly enforced. """ if _debug: Choice._debug("__init__ %r", kwargs) # split out the keyword arguments that belong to this class my_kwargs = {} other_kwargs = {} for element in self.choiceElements: if element.name in kwargs: my_kwargs[element.name] = kwargs[element.name] for kw in kwargs: if kw not in my_kwargs: other_kwargs[kw] = kwargs[kw] if _debug: Choice._debug(" - my_kwargs: %r", my_kwargs) if _debug: Choice._debug(" - other_kwargs: %r", other_kwargs) # call some superclass, if there is one super(Choice, self).__init__(**other_kwargs) # set the attribute/property values for the ones provided for element in self.choiceElements: setattr(self, element.name, my_kwargs.get(element.name, None)) def encode(self, taglist): if _debug: Choice._debug("(%r)encode %r", self.__class__.__name__, taglist) for element in self.choiceElements: value = getattr(self, element.name, None) if value is None: continue if issubclass(element.klass, (Atomic, AnyAtomic)): # a helper cooperates between the atomic value and the tag helper = element.klass(value) # build a tag and encode the data into it tag = Tag() helper.encode(tag) # convert it to context encoding if element.context is not None: tag = tag.app_to_context(element.context) # now encode the tag taglist.append(tag) break elif isinstance(value, element.klass): # encode an opening tag if element.context is not None: taglist.append(OpeningTag(element.context)) # encode the value value.encode(taglist) # encode a closing tag if element.context is not None: taglist.append(ClosingTag(element.context)) break else: raise TypeError("'%s' must be a %s" % (element.name, element.klass.__name__)) else: raise AttributeError("missing choice of %s" % (self.__class__.__name__,)) def decode(self, taglist): if _debug: Choice._debug("(%r)decode %r", self.__class__.__name__, taglist) # peek at the element tag = taglist.Peek() if tag is None: raise AttributeError("missing choice of %s" % (self.__class__.__name__,)) if tag.tagClass == Tag.closingTagClass: raise AttributeError("missing choice of %s" % (self.__class__.__name__,)) # keep track of which one was found foundElement = {} # figure out which choice it is for element in self.choiceElements: if _debug: Choice._debug(" - checking choice: %s", element.name) # check for a sequence element if element.klass in _sequence_of_classes: # check for context encoding if element.context is None: raise NotImplementedError("choice of a SequenceOf must be context encoded") # match the context tag number if tag.tagClass != Tag.contextTagClass or tag.tagNumber != element.context: continue taglist.Pop() # a helper cooperates between the atomic value and the tag helper = element.klass() helper.decode(taglist) # now save the value foundElement[element.name] = helper.value # check for context 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)) # done if _debug: Choice._debug(" - found choice (sequence)") break # check for an atomic element elif issubclass(element.klass, (Atomic, AnyAtomic)): # convert it to application encoding if element.context is not None: if tag.tagClass != Tag.contextTagClass or tag.tagNumber != element.context: continue tag = tag.context_to_app(element.klass._app_tag) else: if tag.tagClass != Tag.applicationTagClass or tag.tagNumber != element.klass._app_tag: continue # consume the tag taglist.Pop() # a helper cooperates between the atomic value and the tag helper = element.klass(tag) # now save the value foundElement[element.name] = helper.value # done if _debug: Choice._debug(" - found choice (atomic)") break # some kind of structure else: # check for context encoding if element.context is None: raise NotImplementedError("choice of non-atomic data must be context encoded") if tag.tagClass != Tag.openingTagClass or tag.tagNumber != element.context: continue taglist.Pop() # build a value and decode it value = element.klass() value.decode(taglist) # now save the value foundElement[element.name] = value # 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)) # done if _debug: Choice._debug(" - found choice (structure)") break else: raise AttributeError("missing choice of %s" % (self.__class__.__name__,)) # now save the value and None everywhere else for element in self.choiceElements: setattr(self, element.name, foundElement.get(element.name, None)) def debug_contents(self, indent=1, file=sys.stdout, _ids=None): for element in self.choiceElements: value = getattr(self, element.name, None) if value is None: continue elif issubclass(element.klass, (Atomic, AnyAtomic)): file.write("%s%s = %r\n" % (" " * indent, element.name, value)) break elif isinstance(value, element.klass): file.write("%s%s\n" % (" " * indent, element.name)) value.debug_contents(indent+1, file, _ids) break else: file.write("%s%s must be a %s" % (" " * indent, element.name, element.klass.__name__)) else: file.write("%smissing choice of %s" % (" " * indent, self.__class__.__name__)) def dict_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" if _debug: _log.debug("dict_contents use_dict=%r as_class=%r", use_dict, as_class) # make/extend the dictionary of content if use_dict is None: use_dict = as_class() # look for the chosen element for element in self.choiceElements: value = getattr(self, element.name, None) if value is None: continue if issubclass(element.klass, Atomic): mapped_value = value ### ambiguous elif issubclass(element.klass, AnyAtomic): mapped_value = value.value ### ambiguous elif isinstance(value, element.klass): mapped_value = value.dict_contents(as_class=as_class) use_dict.__setitem__(element.name, mapped_value) break # return what we built/updated return use_dict # # Any # @bacpypes_debugging class Any: def __init__(self, *args): self.tagList = TagList() # cast in the args for arg in args: self.cast_in(arg) def encode(self, taglist): if _debug: Any._debug("encode %r", taglist) taglist.extend(self.tagList) def decode(self, taglist): if _debug: Any._debug("decode %r", taglist) lvl = 0 while len(taglist) != 0: tag = taglist.Peek() if tag.tagClass == Tag.openingTagClass: lvl += 1 elif tag.tagClass == Tag.closingTagClass: lvl -= 1 if lvl < 0: break self.tagList.append(taglist.Pop()) # make sure everything balances if lvl > 0: raise DecodingError("mismatched open/close tags") def cast_in(self, element): """encode the element into the internal tag list.""" if _debug: Any._debug("cast_in %r", element) t = TagList() if isinstance(element, Atomic): tag = Tag() element.encode(tag) t.append(tag) elif isinstance(element, AnyAtomic): tag = Tag() element.value.encode(tag) t.append(tag) else: element.encode(t) self.tagList.extend(t.tagList) def cast_out(self, klass): """Interpret the content as a particular class.""" if _debug: Any._debug("cast_out %r", klass) # check for a sequence element if klass in _sequence_of_classes: # build a sequence 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 what was built return helper.value # check for an array element elif klass in _array_of_classes: # build a sequence 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 what was built with Python list semantics return helper.value[1:] elif issubclass(klass, (Atomic, AnyAtomic)): # make sure there's only one piece if len(self.tagList) == 0: raise DecodingError("missing cast component") if len(self.tagList) > 1: raise DecodingError("too many cast components") if _debug: Any._debug(" - building helper: %r", klass) # a helper cooperates between the atomic value and the tag helper = klass(self.tagList[0]) # return the value return helper.value else: if _debug: Any._debug(" - building value: %r", klass) # build an element value = klass() # make a copy of the tag list t = TagList(self.tagList[:]) # let it decode itself value.decode(t) # make sure everything was consumed if len(t) != 0: raise DecodingError("incomplete cast") # return what was built return value def is_application_class_null(self): if _debug: Any._debug("is_application_class_null") return (len(self.tagList) == 1) and (self.tagList[0].tagClass == Tag.applicationTagClass) and (self.tagList[0].tagNumber == Tag.nullAppTag) def debug_contents(self, indent=1, file=sys.stdout, _ids=None): self.tagList.debug_contents(indent, file, _ids) def dict_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" if _debug: Any._debug("dict_contents use_dict=%r as_class=%r", use_dict, as_class) # result will be a list rslt_list = [] # loop through the tags for tag in self.tagList: # build a tag thing use_dict = as_class() # save the pieces use_dict.__setitem__('class', tag.tagClass) use_dict.__setitem__('number', tag.tagNumber) use_dict.__setitem__('lvt', tag.tagLVT) ### use_dict.__setitem__('data', '.'.join('%02X' % ord(c) for c in tag.tagData)) # add it to the list rslt_list = use_dict # return what we built return rslt_list # # AnyAtomic # @bacpypes_debugging class AnyAtomic: def __init__(self, arg=None): if _debug: AnyAtomic._debug("__init__ %r", arg) # default to no value self.value = None if arg is None: pass elif isinstance(arg, Atomic): self.value = arg elif isinstance(arg, Tag): self.value = arg.app_to_object() else: raise TypeError("invalid constructor datatype") def encode(self, tag): if _debug: AnyAtomic._debug("encode %r", tag) self.value.encode(tag) def decode(self, tag): if _debug: AnyAtomic._debug("decode %r", tag) if (tag.tagClass != Tag.applicationTagClass): raise ValueError("application tag required") # get the data self.value = tag.app_to_object() def __str__(self): return "AnyAtomic(%s)" % (str(self.value), ) def __repr__(self): desc = self.__module__ + '.' + self.__class__.__name__ if self.value: desc += "(" + self.value.__class__.__name__ + ")" desc += ' ' + str(self.value) return '<' + desc + ' instance at 0x%08x' % (id(self),) + '>' PKH $bacpypes/__init__.py#!/usr/bin/python """BACnet Python Package""" # # Platform Check # import sys as _sys import warnings as _warnings _supported_platforms = ('linux', 'win32', 'darwin') if _sys.platform not in _supported_platforms: _warnings.warn("unsupported platform", RuntimeWarning) # # Project Metadata # __version__ = '0.13.8' __author__ = 'Joel Bender' __email__ = 'joel@carrickbender.com' # # Communications Core Modules # from . import comm from . import task from . import singleton # # Link Layer Modules # from . import pdu from . import vlan # # Network Layer Modules # from . import npdu from . import netservice # # Virtual Link Layer Modules # from . import bvll from . import bvllservice from . import bsll from . import bsllservice # # Application Layer Modules # from . import primitivedata from . import constructeddata from . import basetypes from . import object from . import apdu from . import app from . import appservice # # Analysis # from . import analysis PKG5H𡷂$E$Ebacpypes/pdu.py#!/usr/bin/python """ PDU """ import re import socket import struct from .debugging import ModuleLogger, bacpypes_debugging, btox, xtob from .comm import PCI as _PCI, PDUData # pack/unpack constants _short_mask = 0xFFFF _long_mask = 0xFFFFFFFF # some debugging _debug = 0 _log = ModuleLogger(globals()) # # Address # ip_address_mask_port_re = re.compile(r'^(?:(\d+):)?(\d+\.\d+\.\d+\.\d+)(?:/(\d+))?(?::(\d+))?$') ethernet_re = re.compile(r'^([0-9A-Fa-f][0-9A-Fa-f][:]){5}([0-9A-Fa-f][0-9A-Fa-f])$' ) @bacpypes_debugging class Address: nullAddr = 0 localBroadcastAddr = 1 localStationAddr = 2 remoteBroadcastAddr = 3 remoteStationAddr = 4 globalBroadcastAddr = 5 def __init__(self, *args): if _debug: Address._debug("__init__ %r", args) self.addrType = Address.nullAddr self.addrNet = None self.addrLen = 0 self.addrAddr = b'' if len(args) == 1: self.decode_address(args[0]) elif len(args) == 2: self.decode_address(args[1]) if self.addrType == Address.localStationAddr: self.addrType = Address.remoteStationAddr self.addrNet = args[0] elif self.addrType == Address.localBroadcastAddr: self.addrType = Address.remoteBroadcastAddr self.addrNet = args[0] else: raise ValueError("unrecognized address ctor form") def decode_address(self, addr): """Initialize the address from a string. Lots of different forms are supported.""" if _debug: Address._debug("decode_address %r (%s)", addr, type(addr)) # start out assuming this is a local station self.addrType = Address.localStationAddr self.addrNet = None if addr == "*": if _debug: Address._debug(" - localBroadcast") self.addrType = Address.localBroadcastAddr self.addrNet = None self.addrAddr = None self.addrLen = None elif addr == "*:*": if _debug: Address._debug(" - globalBroadcast") self.addrType = Address.globalBroadcastAddr self.addrNet = None self.addrAddr = None self.addrLen = None elif isinstance(addr, int): if _debug: Address._debug(" - int") if (addr < 0) or (addr >= 256): raise ValueError("address out of range") self.addrAddr = struct.pack('B', addr) self.addrLen = 1 elif isinstance(addr, (bytes, bytearray)): if _debug: Address._debug(" - bytes or bytearray") self.addrAddr = bytes(addr) self.addrLen = len(addr) if self.addrLen == 6: self.addrIP = struct.unpack('!L', addr[:4])[0] self.addrMask = (1 << 32) - 1 self.addrHost = (self.addrIP & ~self.addrMask) self.addrSubnet = (self.addrIP & self.addrMask) self.addrPort = struct.unpack(">H", addr[4:])[0] self.addrTuple = (socket.inet_ntoa(addr[:4]), self.addrPort) self.addrBroadcastTuple = ('255.255.255.255', self.addrPort) elif isinstance(addr, str): if _debug: Address._debug(" - str") m = ip_address_mask_port_re.match(addr) if m: if _debug: Address._debug(" - IP address") net, addr, mask, port = m.groups() if not mask: mask = '32' if not port: port = '47808' if _debug: Address._debug(" - net, addr, mask, port: %r, %r, %r, %r", net, addr, mask, port) if net: net = int(net) if (net >= 65535): raise ValueError("network out of range") self.addrType = Address.remoteStationAddr self.addrNet = net self.addrPort = int(port) self.addrTuple = (addr, self.addrPort) addrstr = socket.inet_aton(addr) self.addrIP = struct.unpack('!L', addrstr)[0] self.addrMask = (_long_mask << (32 - int(mask))) & _long_mask self.addrHost = (self.addrIP & ~self.addrMask) self.addrSubnet = (self.addrIP & self.addrMask) bcast = (self.addrSubnet | ~self.addrMask) self.addrBroadcastTuple = (socket.inet_ntoa(struct.pack('!L', bcast & _long_mask)), self.addrPort) self.addrAddr = addrstr + struct.pack('!H', self.addrPort & _short_mask) self.addrLen = 6 elif ethernet_re.match(addr): if _debug: Address._debug(" - ethernet") self.addrAddr = xtob(addr, ':') self.addrLen = len(self.addrAddr) elif re.match(r"^\d+$", addr): if _debug: Address._debug(" - int") addr = int(addr) if (addr > 255): raise ValueError("address out of range") self.addrAddr = struct.pack('B', addr) self.addrLen = 1 elif re.match(r"^\d+:[*]$", addr): if _debug: Address._debug(" - remote broadcast") addr = int(addr[:-2]) if (addr >= 65535): raise ValueError("network out of range") self.addrType = Address.remoteBroadcastAddr self.addrNet = addr self.addrAddr = None self.addrLen = None elif re.match(r"^\d+:\d+$",addr): if _debug: Address._debug(" - remote station") net, addr = addr.split(':') net = int(net) addr = int(addr) if (net >= 65535): raise ValueError("network out of range") if (addr > 255): raise ValueError("address out of range") self.addrType = Address.remoteStationAddr self.addrNet = net self.addrAddr = struct.pack('B', addr) self.addrLen = 1 elif re.match(r"^0x([0-9A-Fa-f][0-9A-Fa-f])+$",addr): if _debug: Address._debug(" - modern hex string") self.addrAddr = xtob(addr[2:]) self.addrLen = len(self.addrAddr) elif re.match(r"^X'([0-9A-Fa-f][0-9A-Fa-f])+'$",addr): if _debug: Address._debug(" - old school hex string") self.addrAddr = xtob(addr[2:-1]) self.addrLen = len(self.addrAddr) elif re.match(r"^\d+:0x([0-9A-Fa-f][0-9A-Fa-f])+$",addr): if _debug: Address._debug(" - remote station with modern hex string") net, addr = addr.split(':') net = int(net) if (net >= 65535): raise ValueError("network out of range") self.addrType = Address.remoteStationAddr self.addrNet = net self.addrAddr = xtob(addr[2:]) self.addrLen = len(self.addrAddr) elif re.match(r"^\d+:X'([0-9A-Fa-f][0-9A-Fa-f])+'$",addr): if _debug: Address._debug(" - remote station with old school hex string") net, addr = addr.split(':') net = int(net) if (net >= 65535): raise ValueError("network out of range") self.addrType = Address.remoteStationAddr self.addrNet = net self.addrAddr = xtob(addr[2:-1]) self.addrLen = len(self.addrAddr) else: raise ValueError("unrecognized format") elif isinstance(addr, tuple): addr, port = addr self.addrPort = int(port) if isinstance(addr, str): if not addr: # when ('', n) is passed it is the local host address, but that # could be more than one on a multihomed machine, the empty string # means "any". addrstr = b'\0\0\0\0' else: addrstr = socket.inet_aton(addr) self.addrTuple = (addr, self.addrPort) elif isinstance(addr, int): addrstr = struct.pack('!L', addr & _long_mask) self.addrTuple = (socket.inet_ntoa(addrstr), self.addrPort) else: raise TypeError("tuple must be (string, port) or (long, port)") if _debug: Address._debug(" - addrstr: %r", addrstr) self.addrIP = struct.unpack('!L', addrstr)[0] self.addrMask = _long_mask self.addrHost = None self.addrSubnet = None self.addrBroadcastTuple = self.addrTuple self.addrAddr = addrstr + struct.pack('!H', self.addrPort & _short_mask) self.addrLen = 6 else: raise TypeError("integer, string or tuple required") def __str__(self): if self.addrType == Address.nullAddr: return 'Null' elif self.addrType == Address.localBroadcastAddr: return '*' elif self.addrType == Address.localStationAddr: rslt = '' if self.addrLen == 1: rslt += str(self.addrAddr[0]) else: port = struct.unpack('!H', self.addrAddr[-2:])[0] if (len(self.addrAddr) == 6) and (port >= 47808) and (port <= 47823): rslt += '.'.join(["%d" % (x) for x in self.addrAddr[0:4]]) if port != 47808: rslt += ':' + str(port) else: rslt += '0x' + btox(self.addrAddr) return rslt elif self.addrType == Address.remoteBroadcastAddr: return '%d:*' % (self.addrNet,) elif self.addrType == Address.remoteStationAddr: rslt = '%d:' % (self.addrNet,) if self.addrLen == 1: rslt += str(self.addrAddr[0]) else: port = struct.unpack('!H', self.addrAddr[-2:])[0] if (len(self.addrAddr) == 6) and (port >= 47808) and (port <= 47823): rslt += '.'.join(["%d" % (x) for x in self.addrAddr[0:4]]) if port != 47808: rslt += ':' + str(port) else: rslt += '0x' + btox(self.addrAddr) return rslt elif self.addrType == Address.globalBroadcastAddr: return '*:*' else: raise TypeError("unknown address type %d" % self.addrType) def __repr__(self): return "<%s %s>" % (self.__class__.__name__, self.__str__()) def __hash__(self): return hash( (self.addrType, self.addrNet, self.addrAddr) ) def __eq__(self,arg): # try an coerce it into an address if not isinstance(arg, Address): arg = Address(arg) # all of the components must match return (self.addrType == arg.addrType) and (self.addrNet == arg.addrNet) and (self.addrAddr == arg.addrAddr) def __ne__(self,arg): return not self.__eq__(arg) def dict_contents(self, use_dict=None, as_class=None): """Return the contents of an object as a dict.""" if _debug: _log.debug("dict_contents use_dict=%r as_class=%r", use_dict, as_class) # exception to the rule of returning a dict return str(self) # # pack_ip_addr, unpack_ip_addr # def pack_ip_addr(addr): """Given an IP address tuple like ('1.2.3.4', 47808) return the six-octet string useful for a BACnet address.""" addr, port = addr return socket.inet_aton(addr) + struct.pack('!H', port & _short_mask) def unpack_ip_addr(addr): """Given a six-octet BACnet address, return an IP address tuple.""" if isinstance(addr, bytearray): addr = bytes(addr) return (socket.inet_ntoa(addr[0:4]), struct.unpack('!H', addr[4:6])[0]) # # LocalStation # class LocalStation(Address): def __init__(self, addr): self.addrType = Address.localStationAddr self.addrNet = None if isinstance(addr, int): if (addr < 0) or (addr >= 256): raise ValueError("address out of range") self.addrAddr = struct.pack('B', addr) self.addrLen = 1 elif isinstance(addr, (bytes, bytearray)): if _debug: Address._debug(" - bytes or bytearray") self.addrAddr = bytes(addr) self.addrLen = len(addr) else: raise TypeError("integer, bytes or bytearray required") # # RemoteStation # class RemoteStation(Address): def __init__(self, net, addr): if not isinstance(net, int): raise TypeError("integer network required") if (net < 0) or (net >= 65535): raise ValueError("network out of range") self.addrType = Address.remoteStationAddr self.addrNet = net if isinstance(addr, int): if (addr < 0) or (addr >= 256): raise ValueError("address out of range") self.addrAddr = struct.pack('B', addr) self.addrLen = 1 elif isinstance(addr, (bytes, bytearray)): if _debug: Address._debug(" - bytes or bytearray") self.addrAddr = bytes(addr) self.addrLen = len(addr) else: raise TypeError("integer, bytes or bytearray required") # # LocalBroadcast # class LocalBroadcast(Address): def __init__(self): self.addrType = Address.localBroadcastAddr self.addrNet = None self.addrAddr = None self.addrLen = None # # RemoteBroadcast # class RemoteBroadcast(Address): def __init__(self, net): if not isinstance(net, int): raise TypeError("integer network required") if (net < 0) or (net >= 65535): raise ValueError("network out of range") self.addrType = Address.remoteBroadcastAddr self.addrNet = net self.addrAddr = None self.addrLen = None # # GlobalBroadcast # class GlobalBroadcast(Address): def __init__(self): self.addrType = Address.globalBroadcastAddr self.addrNet = None self.addrAddr = None self.addrLen = None # # PCI # @bacpypes_debugging class PCI(_PCI): _debug_contents = ('pduExpectingReply', 'pduNetworkPriority') def __init__(self, *args, **kwargs): if _debug: PCI._debug("__init__ %r %r", args, kwargs) # split out the keyword arguments that belong to this class my_kwargs = {} other_kwargs = {} for element in ('expectingReply', 'networkPriority'): if element in kwargs: my_kwargs[element] = kwargs[element] for kw in kwargs: if kw not in my_kwargs: other_kwargs[kw] = kwargs[kw] if _debug: PCI._debug(" - my_kwargs: %r", my_kwargs) if _debug: PCI._debug(" - other_kwargs: %r", other_kwargs) # call some superclass, if there is one super(PCI, self).__init__(*args, **other_kwargs) # set the attribute/property values for the ones provided self.pduExpectingReply = my_kwargs.get('expectingReply', 0) # see 6.2.2 (1 or 0) self.pduNetworkPriority = my_kwargs.get('networkPriority', 0) # see 6.2.2 (0..3) def update(self, pci): """Copy the PCI fields.""" _PCI.update(self, pci) # now do the BACnet PCI fields self.pduExpectingReply = pci.pduExpectingReply self.pduNetworkPriority = pci.pduNetworkPriority def pci_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" if _debug: PCI._debug("pci_contents use_dict=%r as_class=%r", use_dict, as_class) # make/extend the dictionary of content if use_dict is None: use_dict = as_class() # call the parent class _PCI.pci_contents(self, use_dict=use_dict, as_class=as_class) # save the values use_dict.__setitem__('expectingReply', self.pduExpectingReply) use_dict.__setitem__('networkPriority', self.pduNetworkPriority) # return what we built/updated return use_dict def dict_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" if _debug: PCI._debug("dict_contents use_dict=%r as_class=%r", use_dict, as_class) return self.pci_contents(use_dict=use_dict, as_class=as_class) # # PDU # @bacpypes_debugging class PDU(PCI, PDUData): def __init__(self, *args, **kwargs): if _debug: PDU._debug("__init__ %r %r", args, kwargs) super(PDU, self).__init__(*args, **kwargs) def __str__(self): return '<%s %s -> %s : %s>' % (self.__class__.__name__, self.pduSource, self.pduDestination, btox(self.pduData,'.')) def dict_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" if _debug: PDUData._debug("dict_contents use_dict=%r as_class=%r", use_dict, as_class) # make/extend the dictionary of content if use_dict is None: use_dict = as_class() # call into the two base classes self.pci_contents(use_dict=use_dict, as_class=as_class) self.pdudata_contents(use_dict=use_dict, as_class=as_class) # return what we built/updated return use_dict PKj0GZuIuIbacpypes/comm.py#!/usr/bin/python """ Communications Module """ import sys import struct from copy import copy as _copy from .errors import DecodingError, ConfigurationError from .debugging import ModuleLogger, DebugContents, bacpypes_debugging, btox # some debugging _debug = 0 _log = ModuleLogger(globals()) # prevent short/long struct overflow _short_mask = 0xFFFF _long_mask = 0xFFFFFFFF # maps of named clients and servers client_map = {} server_map = {} # maps of named SAPs and ASEs service_map = {} element_map = {} # # PCI # @bacpypes_debugging class PCI(DebugContents): _debug_contents = ('pduUserData+', 'pduSource', 'pduDestination') def __init__(self, *args, **kwargs): if _debug: PCI._debug("__init__ %r %r", args, kwargs) # split out the keyword arguments that belong to this class my_kwargs = {} other_kwargs = {} for element in ('user_data', 'source', 'destination'): if element in kwargs: my_kwargs[element] = kwargs[element] for kw in kwargs: if kw not in my_kwargs: other_kwargs[kw] = kwargs[kw] if _debug: PCI._debug(" - my_kwargs: %r", my_kwargs) if _debug: PCI._debug(" - other_kwargs: %r", other_kwargs) # call some superclass, if there is one super(PCI, self).__init__(*args, **other_kwargs) # pick up some optional kwargs self.pduUserData = my_kwargs.get('user_data', None) self.pduSource = my_kwargs.get('source', None) self.pduDestination = my_kwargs.get('destination', None) def update(self, pci): """Copy the PCI fields.""" self.pduUserData = pci.pduUserData self.pduSource = pci.pduSource self.pduDestination = pci.pduDestination def pci_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" if _debug: PCI._debug("pci_contents use_dict=%r as_class=%r", use_dict, as_class) # make/extend the dictionary of content if use_dict is None: use_dict = as_class() # save the values for k, v in (('user_data', self.pduUserData), ('source', self.pduSource), ('destination', self.pduDestination)): if _debug: PCI._debug(" - %r: %r", k, v) if v is None: continue if hasattr(v, 'dict_contents'): v = v.dict_contents(as_class=as_class) use_dict.__setitem__(k, v) # return what we built/updated return use_dict def dict_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" if _debug: PCI._debug("dict_contents use_dict=%r as_class=%r", use_dict, as_class) return self.pci_contents(use_dict=use_dict, as_class=as_class) # # PDUData # @bacpypes_debugging class PDUData(object): def __init__(self, data=None, *args, **kwargs): if _debug: PDUData._debug("__init__ %r %r %r", data, args, kwargs) # this call will fail if there are args or kwargs, but not if there # is another class in the __mro__ of this thing being constructed super(PDUData, self).__init__(*args, **kwargs) # function acts like a copy constructor if isinstance(data, PDUData) or isinstance(data, PDU): self.pduData = _copy(data.pduData) elif data is None: self.pduData = bytearray() elif isinstance(data, (bytes, bytearray)): self.pduData = bytearray(data) else: raise TypeError("bytes or bytearray expected") def get(self): if len(self.pduData) == 0: raise DecodingError("no more packet data") octet = self.pduData[0] del self.pduData[0] return octet def get_data(self, dlen): if len(self.pduData) < dlen: raise DecodingError("no more packet data") data = self.pduData[:dlen] del self.pduData[:dlen] return data def get_short(self): return struct.unpack('>H',self.get_data(2))[0] def get_long(self): return struct.unpack('>L',self.get_data(4))[0] def put(self, n): # pduData is a bytearray self.pduData += bytes([n]) def put_data(self, data): if isinstance(data, bytes): pass elif isinstance(data, bytearray): pass elif isinstance(data, list): data = bytes(data) else: raise TypeError("data must be bytes, bytearray, or a list") # regular append works self.pduData += data def put_short(self, n): self.pduData += struct.pack('>H',n & _short_mask) def put_long(self, n): self.pduData += struct.pack('>L',n & _long_mask) def debug_contents(self, indent=1, file=sys.stdout, _ids=None): if isinstance(self.pduData, bytearray): if len(self.pduData) > 20: hexed = btox(self.pduData[:20],'.') + "..." else: hexed = btox(self.pduData,'.') file.write("%spduData = x'%s'\n" % (' ' * indent, hexed)) else: file.write("%spduData = %r\n" % (' ' * indent, self.pduData)) def pdudata_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" if _debug: PDUData._debug("pdudata_contents use_dict=%r as_class=%r", use_dict, as_class) # make/extend the dictionary of content if use_dict is None: use_dict = as_class() # add the data if it is not None v = self.pduData if v is not None: if isinstance(v, bytearray): v = btox(v) elif hasattr(v, 'dict_contents'): v = v.dict_contents(as_class=as_class) use_dict.__setitem__('data', v) # return what we built/updated return use_dict def dict_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" if _debug: PDUData._debug("dict_contents use_dict=%r as_class=%r", use_dict, as_class) return self.pdudata_contents(use_dict=use_dict, as_class=as_class) # # PDU # @bacpypes_debugging class PDU(PCI, PDUData): def __init__(self, data='', **kwargs): if _debug: PDU._debug("__init__ %r %r", data, kwargs) # pick up some optional kwargs user_data = kwargs.get('user_data', None) source = kwargs.get('source', None) destination = kwargs.get('destination', None) # carry source and destination from another PDU # so this can act like a copy constructor if isinstance(data, PDU): # allow parameters to override values user_data = user_data or data.pduUserData source = source or data.pduSource destination = destination or data.pduDestination # now continue on PCI.__init__(self, user_data=user_data, source=source, destination=destination) PDUData.__init__(self, data) def __str__(self): return '<%s %s -> %s : %s>' % (self.__class__.__name__, self.pduSource, self.pduDestination, btox(self.pduData, '.') ) def dict_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" if _debug: PDUData._debug("dict_contents use_dict=%r as_class=%r", use_dict, as_class) # make/extend the dictionary of content if use_dict is None: use_dict = as_class() # call into the two base classes self.pci_contents(use_dict=use_dict, as_class=as_class) self.pdudata_contents(use_dict=use_dict, as_class=as_class) # return what we built/updated return use_dict # # Client # @bacpypes_debugging class Client: def __init__(self, cid=None): if _debug: Client._debug("__init__ cid=%r", cid) self.clientID = cid self.clientPeer = None if cid is not None: if cid in client_map: raise ConfigurationError("already a client {!r}".format(cid)) client_map[cid] = self # automatically bind if cid in server_map: server = server_map[cid] if server.serverPeer: raise ConfigurationError("server {!r} already bound".format(cid)) bind(self, server) def request(self, *args, **kwargs): if _debug: Client._debug("request %r %r", args, kwargs) if not self.clientPeer: raise ConfigurationError("unbound client") self.clientPeer.indication(*args, **kwargs) def confirmation(self, *args, **kwargs): raise NotImplementedError("confirmation must be overridden") # # Server # @bacpypes_debugging class Server: def __init__(self, sid=None): if _debug: Server._debug("__init__ sid=%r", sid) self.serverID = sid self.serverPeer = None if sid is not None: if sid in server_map: raise RuntimeError("already a server {!r}".format(sid)) server_map[sid] = self # automatically bind if sid in client_map: client = client_map[sid] if client.clientPeer: raise ConfigurationError("client {!r} already bound".format(sid)) bind(client, self) def indication(self, *args, **kwargs): raise NotImplementedError("indication must be overridden") def response(self, *args, **kwargs): if _debug: Server._debug("response %r %r", args, kwargs) if not self.serverPeer: raise ConfigurationError("unbound server") self.serverPeer.confirmation(*args, **kwargs) # # Debug # @bacpypes_debugging class Debug(Client, Server): def __init__(self, label=None, cid=None, sid=None): if _debug: Debug._debug("__init__ label=%r cid=%r sid=%r", label, cid, sid) Client.__init__(self, cid) Server.__init__(self, sid) # save the label self.label = label def confirmation(self, *args, **kwargs): print("Debug({!s}).confirmation".format(self.label)) for i, arg in enumerate(args): print(" - args[{:d}]: {!r}".format(i, arg)) if hasattr(arg, 'debug_contents'): arg.debug_contents(2) for key, value in kwargs.items(): print(" - kwargs[{!r}]: {!r}".format(key, value)) if hasattr(value, 'debug_contents'): value.debug_contents(2) if self.serverPeer: self.response(*args, **kwargs) def indication(self, *args, **kwargs): print("Debug({!s}).indication".format(self.label)) for i, arg in enumerate(args): print(" - args[{:d}]: {!r}".format(i, arg)) if hasattr(arg, 'debug_contents'): arg.debug_contents(2) for key, value in kwargs.items(): print(" - kwargs[{!r}]: {!r}".format(key, value)) if hasattr(value, 'debug_contents'): value.debug_contents(2) if self.clientPeer: self.request(*args, **kwargs) # # Echo # @bacpypes_debugging class Echo(Client, Server): def __init__(self, cid=None, sid=None): if _debug: Echo._debug("__init__ cid=%r sid=%r", cid, sid) Client.__init__(self, cid) Server.__init__(self, sid) def confirmation(self, *args, **kwargs): if _debug: Echo._debug("confirmation %r %r", args, kwargs) self.request(*args, **kwargs) def indication(self, *args, **kwargs): if _debug: Echo._debug("indication %r %r", args, kwargs) self.response(*args, **kwargs) # # ServiceAccessPoint # # Note that the SAP functions have been renamed so a derived class # can inherit from both Client, Service, and ServiceAccessPoint # at the same time. # @bacpypes_debugging class ServiceAccessPoint: def __init__(self, sapID=None): if _debug: ServiceAccessPoint._debug("__init__(%s)", sapID) self.serviceID = sapID self.serviceElement = None if sapID is not None: if sapID in service_map: raise ConfigurationError("already a service access point {!r}".format(sapID)) service_map[sapID] = self # automatically bind if sapID in element_map: element = element_map[sapID] if element.elementService: raise ConfigurationError("application service element {!r} already bound".format(sapID)) bind(element, self) def sap_request(self, *args, **kwargs): if _debug: ServiceAccessPoint._debug("sap_request(%s) %r %r", self.serviceID, args, kwargs) if not self.serviceElement: raise ConfigurationError("unbound service access point") self.serviceElement.indication(*args, **kwargs) def sap_indication(self, *args, **kwargs): raise NotImplementedError("sap_indication must be overridden") def sap_response(self, *args, **kwargs): if _debug: ServiceAccessPoint._debug("sap_response(%s) %r %r", self.serviceID, args, kwargs) if not self.serviceElement: raise ConfigurationError("unbound service access point") self.serviceElement.confirmation(*args,**kwargs) def sap_confirmation(self, *args, **kwargs): raise NotImplementedError("sap_confirmation must be overridden") # # ApplicationServiceElement # @bacpypes_debugging class ApplicationServiceElement: def __init__(self, aseID=None): if _debug: ApplicationServiceElement._debug("__init__(%s)", aseID) self.elementID = aseID self.elementService = None if aseID is not None: if aseID in element_map: raise ConfigurationError("already an application service element {!r}".format(aseID)) element_map[aseID] = self # automatically bind if aseID in service_map: service = service_map[aseID] if service.serviceElement: raise ConfigurationError("service access point {!r} already bound".format(aseID)) bind(self, service) def request(self, *args, **kwargs): if _debug: ApplicationServiceElement._debug("request(%s) %r %r", self.elementID, args, kwargs) if not self.elementService: raise ConfigurationError("unbound application service element") self.elementService.sap_indication(*args, **kwargs) def indication(self, *args, **kwargs): raise NotImplementedError("indication must be overridden") def response(self, *args, **kwargs): if _debug: ApplicationServiceElement._debug("response(%s) %r %r", self.elementID, args, kwargs) if not self.elementService: raise ConfigurationError("unbound application service element") self.elementService.sap_confirmation(*args,**kwargs) def confirmation(self, *args, **kwargs): raise NotImplementedError("confirmation must be overridden") # # NullServiceElement # class NullServiceElement(ApplicationServiceElement): def indication(self, *args, **kwargs): pass def confirmation(self, *args, **kwargs): pass # # DebugServiceElement # class DebugServiceElement(ApplicationServiceElement): def indication(self, *args, **kwargs): print("DebugServiceElement({!s}).indication".format(self.elementID)) print(" - args: {!r}".format(args)) print(" - kwargs: {!r}".format(kwargs)) def confirmation(self, *args, **kwargs): print("DebugServiceElement({!s}).confirmation".format(self.elementID)) print(" - args: {!r}".format(args)) print(" - kwargs: {!r}".format(kwargs)) # # bind # @bacpypes_debugging def bind(*args): """bind a list of clients and servers together, top down.""" if _debug: bind._debug("bind %r", args) # generic bind is pairs of names if not args: # find unbound clients and bind them for cid, client in client_map.items(): # skip those that are already bound if client.clientPeer: continue if not cid in server_map: raise RuntimeError("unmatched server {!r}".format(cid)) server = server_map[cid] if server.serverPeer: raise RuntimeError("server already bound %r".format(cid)) bind(client, server) # see if there are any unbound servers for sid, server in server_map.items(): if server.serverPeer: continue if not sid in client_map: raise RuntimeError("unmatched client {!r}".format(sid)) else: raise RuntimeError("mistery unbound server {!r}".format(sid)) # find unbound application service elements and bind them for eid, element in element_map.items(): # skip those that are already bound if element.elementService: continue if not eid in service_map: raise RuntimeError("unmatched element {!r}".format(cid)) service = service_map[eid] if server.serverPeer: raise RuntimeError("service already bound {!r}".format(cid)) bind(element, service) # see if there are any unbound services for sid, service in service_map.items(): if service.serviceElement: continue if not sid in element_map: raise RuntimeError("unmatched service {!r}".format(sid)) else: raise RuntimeError("mistery unbound service {!r}".format(sid)) # go through the argument pairs for i in range(len(args)-1): client = args[i] if _debug: bind._debug(" - client: %r", client) server = args[i+1] if _debug: bind._debug(" - server: %r", server) # make sure we're binding clients and servers if isinstance(client, Client) and isinstance(server, Server): client.clientPeer = server server.serverPeer = client # we could be binding application clients and servers elif isinstance(client, ApplicationServiceElement) and isinstance(server, ServiceAccessPoint): client.elementService = server server.serviceElement = client # error else: raise TypeError("bind() requires a client and server") if _debug: bind._debug(" - bound") PKF 4bacpypes/commandlogging.py#!/usr/bin/python """ Command Logging """ import logging from .debugging import Logging, LoggingFormatter, ModuleLogger from .comm import PDU, Client, Server # some debugging _debug = 0 _log = ModuleLogger(globals()) # # CommandLoggingHandler # class CommandLoggingHandler(logging.Handler): def __init__(self, commander, addr, loggerName): logging.Handler.__init__(self, logging.DEBUG) self.setFormatter(LoggingFormatter()) # save where this stuff goes self.commander = commander self.addr = addr self.loggerName = loggerName def emit(self, record): # use the basic formatting msg = self.format(record) + '\n' # tell the commander self.commander.emit(msg, self.addr) # # CommandLogging # class CommandLogging(Logging): def __init__(self): if _debug: CommandLogging._debug("__init__") # handlers, self.handlers[addr][logger] = handler self.handlers = {} def process_command(self, cmd, addr): if _debug: CommandLogging._debug("process_command %r", cmd, addr) # get the address, find (or build) its list of handlers if addr not in self.handlers: handlers = self.handlers[addr] = {} else: handlers = self.handlers[addr] # split the command into a list of args args = cmd.strip().split() # get the logger name and logger logger = None # second arg is optional, but always a logger name if len(args) > 1: loggerName = args[1] if loggerName in logging.Logger.manager.loggerDict: logger = logging.getLogger(loggerName) if not args: response = '-' elif args[0] == '?': if len(args) == 1: if not handlers: response = 'no handlers' else: response = "handlers: " + ', '.join(loggerName for loggerName in handlers) elif not logger: response = 'not a valid logger name' elif loggerName in handlers: response = 'yes' else: response = 'no' elif args[0] == '+': if not logger: response = 'not a valid logger name' elif loggerName in handlers: response = loggerName + ' already has a handler' else: handler = CommandLoggingHandler(self, addr, loggerName) handlers[loggerName] = handler # add it to the logger logger.addHandler(handler) if not addr: response = "handler to %s added" % (loggerName,) else: response = "handler from %s to %s added" % (addr, loggerName) elif args[0] == '-': if not logger: response = 'not a valid logger name' elif loggerName not in handlers: response = 'no handler for ' + loggerName else: handler = handlers[loggerName] del handlers[loggerName] # remove it from the logger logger.removeHandler(handler) if not addr: response = "handler to %s removed" % (loggerName,) else: response = "handler from %s to %s removed" % (addr, loggerName) else: if _debug: CommandLogging._warning("bad command %r", cmd) response = 'bad command' # return the response return response + '\n' def emit(self, msg, addr): if _debug: CommandLogging._debug("emit %r %r", msg, addr) raise NotImplementedError("emit must be overridden") # # CommandLoggingServer # class CommandLoggingServer(CommandLogging, Server, Logging): def __init__(self): if _debug: CommandLoggingServer._debug("__init__") CommandLogging.__init__(self) def indication(self, pdu): if _debug: CommandLoggingServer._debug("indication %r", pdu) addr = pdu.pduSource resp = self.process_command(pdu.pduData, addr) self.response(PDU(resp, source=addr)) def emit(self, msg, addr): if _debug: CommandLoggingServer._debug("emit %r %r", msg, addr) # pass upstream to the client self.response(PDU(msg, source=addr)) # # CommandLoggingClient # class CommandLoggingClient(CommandLogging, Client, Logging): def __init__(self): if _debug: CommandLoggingClient._debug("__init__") CommandLogging.__init__(self) def confirmation(self, pdu): if _debug: CommandLoggingClient._debug("confirmation %r", pdu) addr = pdu.pduSource resp = self.process_command(pdu.pduData, addr) self.request(PDU(resp, destination=addr)) def emit(self, msg, addr): if _debug: CommandLoggingClient._debug("emit %r %r", msg, addr) # pass downstream to the server self.request(PDU(msg, destination=addr)) PKcF3mmbacpypes/netservice.py#!/usr/bin/python """ Network Service """ from copy import copy as _copy from .debugging import ModuleLogger, DebugContents, bacpypes_debugging from .errors import ConfigurationError from .comm import Client, Server, bind, \ ServiceAccessPoint, ApplicationServiceElement from .pdu import Address, LocalBroadcast, LocalStation, PDU, RemoteStation from .npdu import IAmRouterToNetwork, NPDU, WhoIsRouterToNetwork, npdu_types from .apdu import APDU as _APDU # some debugging _debug = 0 _log = ModuleLogger(globals()) # router status values ROUTER_AVAILABLE = 0 # normal ROUTER_BUSY = 1 # router is busy ROUTER_DISCONNECTED = 2 # could make a connection, but hasn't ROUTER_UNREACHABLE = 3 # cannot route # # NetworkReference # class NetworkReference: """These objects map a network to a router.""" def __init__(self, net, router, status): self.network = net self.router = router self.status = status # # RouterReference # class RouterReference(DebugContents): """These objects map a router; the adapter to talk to it, its address, and a list of networks that it routes to.""" _debug_contents = ('adapter-', 'address', 'networks', 'status') def __init__(self, adapter, addr, nets, status): self.adapter = adapter self.address = addr # local station relative to the adapter self.networks = nets # list of remote networks self.status = status # status as presented by the router # # NetworkAdapter # @bacpypes_debugging class NetworkAdapter(Client, DebugContents): _debug_contents = ('adapterSAP-', 'adapterNet') def __init__(self, sap, net, cid=None): if _debug: NetworkAdapter._debug("__init__ %r (net=%r) cid=%r", sap, net, cid) Client.__init__(self, cid) self.adapterSAP = sap self.adapterNet = net # add this to the list of adapters for the network sap.adapters.append(self) def confirmation(self, pdu): """Decode upstream PDUs and pass them up to the service access point.""" if _debug: NetworkAdapter._debug("confirmation %r (net=%r)", pdu, self.adapterNet) npdu = NPDU(user_data=pdu.pduUserData) npdu.decode(pdu) self.adapterSAP.process_npdu(self, npdu) def process_npdu(self, npdu): """Encode NPDUs from the service access point and send them downstream.""" if _debug: NetworkAdapter._debug("process_npdu %r (net=%r)", npdu, self.adapterNet) pdu = PDU(user_data=npdu.pduUserData) npdu.encode(pdu) self.request(pdu) def EstablishConnectionToNetwork(self, net): pass def DisconnectConnectionToNetwork(self, net): pass # # NetworkServiceAccessPoint # @bacpypes_debugging class NetworkServiceAccessPoint(ServiceAccessPoint, Server, DebugContents): _debug_contents = ('adapters++', 'routers++', 'networks+' , 'localAdapter-', 'localAddress' ) def __init__(self, sap=None, sid=None): if _debug: NetworkServiceAccessPoint._debug("__init__ sap=%r sid=%r", sap, sid) ServiceAccessPoint.__init__(self, sap) Server.__init__(self, sid) self.adapters = [] # list of adapters self.routers = {} # (adapter, address) -> RouterReference self.networks = {} # network -> RouterReference self.localAdapter = None # which one is local self.localAddress = None # what is the local address def bind(self, server, net=None, address=None): """Create a network adapter object and bind.""" if _debug: NetworkServiceAccessPoint._debug("bind %r net=%r address=%r", server, net, address) if (net is None) and self.adapters: raise RuntimeError("already bound") # create an adapter object adapter = NetworkAdapter(self, net) # if the address was given, make it the "local" one if address: self.localAdapter = adapter self.localAddress = address # bind to the server bind(adapter, server) #----- def add_router_references(self, adapter, address, netlist): """Add/update references to routers.""" if _debug: NetworkServiceAccessPoint._debug("add_router_references %r %r %r", adapter, address, netlist) # make a key for the router reference rkey = (adapter, address) for snet in netlist: # see if this is spoofing an existing routing table entry if snet in self.networks: rref = self.networks[snet] if rref.adapter == adapter and rref.address == address: pass # matches current entry else: ### check to see if this source could be a router to the new network # remove the network from the rref i = rref.networks.index(snet) del rref.networks[i] # remove the network del self.networks[snet] ### check to see if it is OK to add the new entry # get the router reference for this router rref = self.routers.get(rkey, None) if rref: if snet not in rref.networks: # add the network rref.networks.append(snet) # reference the snet self.networks[snet] = rref else: # new reference rref = RouterReference( adapter, address, [snet], 0) self.routers[rkey] = rref # reference the snet self.networks[snet] = rref def remove_router_references(self, adapter, address=None): """Add/update references to routers.""" if _debug: NetworkServiceAccessPoint._debug("remove_router_references %r %r", adapter, address) delrlist = [] delnlist = [] # scan through the dictionary of router references for rkey in self.routers.keys(): # rip apart the key radapter, raddress = rkey # pick all references on the adapter, optionally limited to a specific address match = radapter is adapter if match and address is not None: match = (raddress == address) if not match: continue # save it for deletion delrlist.append(rkey) delnlist.extend(self.routers[rkey].networks) if _debug: NetworkServiceAccessPoint._debug(" - delrlist: %r", delrlist) NetworkServiceAccessPoint._debug(" - delnlist: %r", delnlist) # delete the entries for rkey in delrlist: try: del self.routers[rkey] except KeyError: if _debug: NetworkServiceAccessPoint._debug(" - rkey not in self.routers: %r", rkey) for nkey in delnlist: try: del self.networks[nkey] except KeyError: if _debug: NetworkServiceAccessPoint._debug(" - nkey not in self.networks: %r", rkey) #----- def indication(self, pdu): if _debug: NetworkServiceAccessPoint._debug("indication %r", pdu) # make sure our configuration is OK if (not self.adapters): raise ConfigurationError("no adapters") # might be able to relax this restriction if (len(self.adapters) > 1) and (not self.localAdapter): raise ConfigurationError("local adapter must be set") # get the local adapter adapter = self.localAdapter or self.adapters[0] # build a generic APDU apdu = _APDU(user_data=pdu.pduUserData) pdu.encode(apdu) if _debug: NetworkServiceAccessPoint._debug(" - apdu: %r", apdu) # build an NPDU specific to where it is going npdu = NPDU(user_data=pdu.pduUserData) apdu.encode(npdu) if _debug: NetworkServiceAccessPoint._debug(" - npdu: %r", npdu) # the hop count always starts out big npdu.npduHopCount = 255 # local stations given to local adapter if (npdu.pduDestination.addrType == Address.localStationAddr): adapter.process_npdu(npdu) return # local broadcast given to local adapter if (npdu.pduDestination.addrType == Address.localBroadcastAddr): adapter.process_npdu(npdu) return # global broadcast if (npdu.pduDestination.addrType == Address.globalBroadcastAddr): # set the destination npdu.pduDestination = LocalBroadcast() npdu.npduDADR = apdu.pduDestination # send it to all of connected adapters for xadapter in self.adapters: xadapter.process_npdu(npdu) return # remote broadcast if (npdu.pduDestination.addrType != Address.remoteBroadcastAddr) and (npdu.pduDestination.addrType != Address.remoteStationAddr): raise RuntimeError("invalid destination address type: %s" % (npdu.pduDestination.addrType,)) dnet = npdu.pduDestination.addrNet # if the network matches the local adapter it's local if (dnet == adapter.adapterNet): ### log this, the application shouldn't be sending to a remote station address ### when it's a directly connected network raise RuntimeError("addressing problem") # check for an available path if dnet in self.networks: rref = self.networks[dnet] adapter = rref.adapter ### make sure the direct connect is OK, may need to connect ### make sure the peer router is OK, may need to connect # fix the destination npdu.pduDestination = rref.address npdu.npduDADR = apdu.pduDestination # send it along adapter.process_npdu(npdu) return if _debug: NetworkServiceAccessPoint._debug(" - no known path to network, broadcast to discover it") # set the destination npdu.pduDestination = LocalBroadcast() npdu.npduDADR = apdu.pduDestination # send it to all of the connected adapters for xadapter in self.adapters: xadapter.process_npdu(npdu) def process_npdu(self, adapter, npdu): if _debug: NetworkServiceAccessPoint._debug("process_npdu %r %r", adapter, npdu) # make sure our configuration is OK if (not self.adapters): raise ConfigurationError("no adapters") if (len(self.adapters) > 1) and (not self.localAdapter): raise ConfigurationError("local adapter must be set") # check for source routing if npdu.npduSADR and (npdu.npduSADR.addrType != Address.nullAddr): # see if this is attempting to spoof a directly connected network snet = npdu.npduSADR.addrNet for xadapter in self.adapters: if (xadapter is not adapter) and (snet == xadapter.adapterNet): NetworkServiceAccessPoint._warning("spoof?") ### log this return # make a key for the router reference rkey = (adapter, npdu.pduSource) # see if this is spoofing an existing routing table entry if snet in self.networks: rref = self.networks[snet] if rref.adapter == adapter and rref.address == npdu.pduSource: pass # matches current entry else: if _debug: NetworkServiceAccessPoint._debug(" - replaces entry") ### check to see if this source could be a router to the new network # remove the network from the rref i = rref.networks.index(snet) del rref.networks[i] # remove the network del self.networks[snet] # get the router reference for this router rref = self.routers.get(rkey) if rref: if snet not in rref.networks: # add the network rref.networks.append(snet) # reference the snet self.networks[snet] = rref else: # new reference rref = RouterReference( adapter, npdu.pduSource, [snet], 0) self.routers[rkey] = rref # reference the snet self.networks[snet] = rref # check for destination routing if (not npdu.npduDADR) or (npdu.npduDADR.addrType == Address.nullAddr): processLocally = (not self.localAdapter) or (adapter is self.localAdapter) or (npdu.npduNetMessage is not None) forwardMessage = False elif npdu.npduDADR.addrType == Address.remoteBroadcastAddr: if not self.localAdapter: return if (npdu.npduDADR.addrNet == adapter.adapterNet): ### log this, attempt to route to a network the device is already on return processLocally = (npdu.npduDADR.addrNet == self.localAdapter.adapterNet) forwardMessage = True elif npdu.npduDADR.addrType == Address.remoteStationAddr: if not self.localAdapter: return if (npdu.npduDADR.addrNet == adapter.adapterNet): ### log this, attempt to route to a network the device is already on return processLocally = (npdu.npduDADR.addrNet == self.localAdapter.adapterNet) \ and (npdu.npduDADR.addrAddr == self.localAddress.addrAddr) forwardMessage = not processLocally elif npdu.npduDADR.addrType == Address.globalBroadcastAddr: processLocally = True forwardMessage = True else: NetworkServiceAccessPoint._warning("invalid destination address type: %s", npdu.npduDADR.addrType) return if _debug: NetworkServiceAccessPoint._debug(" - processLocally: %r", processLocally) NetworkServiceAccessPoint._debug(" - forwardMessage: %r", forwardMessage) # application or network layer message if npdu.npduNetMessage is None: if processLocally and self.serverPeer: # decode as a generic APDU apdu = _APDU(user_data=npdu.pduUserData) apdu.decode(_copy(npdu)) if _debug: NetworkServiceAccessPoint._debug(" - apdu: %r", apdu) # see if it needs to look routed if (len(self.adapters) > 1) and (adapter != self.localAdapter): # combine the source address if not npdu.npduSADR: apdu.pduSource = RemoteStation( adapter.adapterNet, npdu.pduSource.addrAddr ) else: apdu.pduSource = npdu.npduSADR # map the destination if not npdu.npduDADR: apdu.pduDestination = self.localAddress elif npdu.npduDADR.addrType == Address.globalBroadcastAddr: apdu.pduDestination = npdu.npduDADR elif npdu.npduDADR.addrType == Address.remoteBroadcastAddr: apdu.pduDestination = LocalBroadcast() else: apdu.pduDestination = self.localAddress else: # combine the source address if npdu.npduSADR: apdu.pduSource = npdu.npduSADR else: apdu.pduSource = npdu.pduSource # pass along global broadcast if npdu.npduDADR and npdu.npduDADR.addrType == Address.globalBroadcastAddr: apdu.pduDestination = npdu.npduDADR else: apdu.pduDestination = npdu.pduDestination if _debug: NetworkServiceAccessPoint._debug(" - apdu.pduSource: %r", apdu.pduSource) NetworkServiceAccessPoint._debug(" - apdu.pduDestination: %r", apdu.pduDestination) # pass upstream to the application layer self.response(apdu) if not forwardMessage: return else: if processLocally: if npdu.npduNetMessage not in npdu_types: if _debug: NetworkServiceAccessPoint._debug(" - unknown npdu type: %r", npdu.npduNetMessage) return # do a deeper decode of the NPDU xpdu = npdu_types[npdu.npduNetMessage](user_data=npdu.pduUserData) xpdu.decode(_copy(npdu)) # pass to the service element self.sap_request(adapter, xpdu) if not forwardMessage: return # make sure we're really a router if (len(self.adapters) == 1): return # make sure it hasn't looped if (npdu.npduHopCount == 0): return # build a new NPDU to send to other adapters newpdu = _copy(npdu) # clear out the source and destination newpdu.pduSource = None newpdu.pduDestination = None # decrease the hop count newpdu.npduHopCount -= 1 # set the source address if not npdu.npduSADR: newpdu.npduSADR = RemoteStation( adapter.adapterNet, npdu.pduSource.addrAddr ) else: newpdu.npduSADR = npdu.npduSADR # if this is a broadcast it goes everywhere if npdu.npduDADR.addrType == Address.globalBroadcastAddr: newpdu.pduDestination = LocalBroadcast() for xadapter in self.adapters: if (xadapter is not adapter): xadapter.process_npdu(newpdu) return if (npdu.npduDADR.addrType == Address.remoteBroadcastAddr) \ or (npdu.npduDADR.addrType == Address.remoteStationAddr): dnet = npdu.npduDADR.addrNet # see if this should go to one of our directly connected adapters for xadapter in self.adapters: if dnet == xadapter.adapterNet: if _debug: NetworkServiceAccessPoint._debug(" - found direct connect via %r", xadapter) if (npdu.npduDADR.addrType == Address.remoteBroadcastAddr): newpdu.pduDestination = LocalBroadcast() else: newpdu.pduDestination = LocalStation(npdu.npduDADR.addrAddr) # last leg in routing newpdu.npduDADR = None # send the packet downstream xadapter.process_npdu(newpdu) return # see if we know how to get there if dnet in self.networks: rref = self.networks[dnet] newpdu.pduDestination = rref.address ### check to make sure the router is OK ### check to make sure the network is OK, may need to connect if _debug: NetworkServiceAccessPoint._debug(" - newpdu: %r", newpdu) # send the packet downstream rref.adapter.process_npdu(newpdu) return ### queue this message for reprocessing when the response comes back # try to find a path to the network xnpdu = WhoIsRouterToNetwork(dnet) xnpdu.pduDestination = LocalBroadcast() # send it to all of the connected adapters for xadapter in self.adapters: # skip the horse it rode in on if (xadapter is adapter): continue ### make sure the adapter is OK self.sap_indication(xadapter, xnpdu) ### log this, what to do? return def sap_indication(self, adapter, npdu): if _debug: NetworkServiceAccessPoint._debug("sap_indication %r %r", adapter, npdu) # encode it as a generic NPDU xpdu = NPDU(user_data=npdu.pduUserData) npdu.encode(xpdu) npdu._xpdu = xpdu # tell the adapter to process the NPDU adapter.process_npdu(xpdu) def sap_confirmation(self, adapter, npdu): if _debug: NetworkServiceAccessPoint._debug("sap_confirmation %r %r", adapter, npdu) # encode it as a generic NPDU xpdu = NPDU(user_data=npdu.pduUserData) npdu.encode(xpdu) npdu._xpdu = xpdu # tell the adapter to process the NPDU adapter.process_npdu(xpdu) # # NetworkServiceElement # @bacpypes_debugging class NetworkServiceElement(ApplicationServiceElement): def __init__(self, eid=None): if _debug: NetworkServiceElement._debug("__init__ eid=%r", eid) ApplicationServiceElement.__init__(self, eid) def indication(self, adapter, npdu): if _debug: NetworkServiceElement._debug("indication %r %r", adapter, npdu) # redirect fn = npdu.__class__.__name__ if hasattr(self, fn): getattr(self, fn)(adapter, npdu) def confirmation(self, adapter, npdu): if _debug: NetworkServiceElement._debug("confirmation %r %r", adapter, npdu) # redirect fn = npdu.__class__.__name__ if hasattr(self, fn): getattr(self, fn)(adapter, npdu) #----- def WhoIsRouterToNetwork(self, adapter, npdu): if _debug: NetworkServiceElement._debug("WhoIsRouterToNetwork %r %r", adapter, npdu) # reference the service access point sap = self.elementService if _debug: NetworkServiceElement._debug(" - sap: %r", sap) # if we're not a router, skip it if len(sap.adapters) == 1: if _debug: NetworkServiceElement._debug(" - not a router") return if npdu.wirtnNetwork is None: # requesting all networks if _debug: NetworkServiceElement._debug(" - requesting all networks") # build a list of reachable networks netlist = [] # start with directly connected networks for xadapter in sap.adapters: if (xadapter is not adapter): netlist.append(xadapter.adapterNet) # build a list of other available networks for net, rref in sap.networks.items(): if rref.adapter is not adapter: ### skip those marked unreachable ### skip those that are not available netlist.append(net) if netlist: if _debug: NetworkServiceElement._debug(" - found these: %r", netlist) # build a response iamrtn = IAmRouterToNetwork(netlist, user_data=npdu.pduUserData) iamrtn.pduDestination = npdu.pduSource # send it back self.response(adapter, iamrtn) else: # requesting a specific network if _debug: NetworkServiceElement._debug(" - requesting specific network: %r", npdu.wirtnNetwork) # start with directly connected networks for xadapter in sap.adapters: if (xadapter is not adapter) and (npdu.wirtnNetwork == xadapter.adapterNet): if _debug: NetworkServiceElement._debug(" - found it directly connected") # build a response iamrtn = IAmRouterToNetwork([npdu.wirtnNetwork], user_data=npdu.pduUserData) iamrtn.pduDestination = npdu.pduSource # send it back self.response(adapter, iamrtn) break else: # check for networks I know about if npdu.wirtnNetwork in sap.networks: rref = sap.networks[npdu.wirtnNetwork] if rref.adapter is adapter: if _debug: NetworkServiceElement._debug(" - same net as request") else: if _debug: NetworkServiceElement._debug(" - found on adapter: %r", rref.adapter) # build a response iamrtn = IAmRouterToNetwork([npdu.wirtnNetwork], user_data=npdu.pduUserData) iamrtn.pduDestination = npdu.pduSource # send it back self.response(adapter, iamrtn) else: if _debug: NetworkServiceElement._debug(" - forwarding request to other adapters") # build a request whoisrtn = WhoIsRouterToNetwork(npdu.wirtnNetwork, user_data=npdu.pduUserData) whoisrtn.pduDestination = LocalBroadcast() # if the request had a source, forward it along if npdu.npduSADR: whoisrtn.npduSADR = npdu.npduSADR else: whoisrtn.npduSADR = RemoteStation(adapter.adapterNet, npdu.pduSource.addrAddr) if _debug: NetworkServiceElement._debug(" - whoisrtn: %r", whoisrtn) # send it to all of the (other) adapters for xadapter in sap.adapters: if xadapter is not adapter: if _debug: NetworkServiceElement._debug(" - sending on adapter: %r", xadapter) self.request(xadapter, whoisrtn) def IAmRouterToNetwork(self, adapter, npdu): if _debug: NetworkServiceElement._debug("IAmRouterToNetwork %r %r", adapter, npdu) # pass along to the service access point self.elementService.add_router_references(adapter, npdu.pduSource, npdu.iartnNetworkList) def ICouldBeRouterToNetwork(self, adapter, npdu): if _debug: NetworkServiceElement._debug("ICouldBeRouterToNetwork %r %r", adapter, npdu) # reference the service access point # sap = self.elementService def RejectMessageToNetwork(self, adapter, npdu): if _debug: NetworkServiceElement._debug("RejectMessageToNetwork %r %r", adapter, npdu) # reference the service access point # sap = self.elementService def RouterBusyToNetwork(self, adapter, npdu): if _debug: NetworkServiceElement._debug("RouterBusyToNetwork %r %r", adapter, npdu) # reference the service access point # sap = self.elementService def RouterAvailableToNetwork(self, adapter, npdu): if _debug: NetworkServiceElement._debug("RouterAvailableToNetwork %r %r", adapter, npdu) # reference the service access point # sap = self.elementService def InitializeRoutingTable(self, adapter, npdu): if _debug: NetworkServiceElement._debug("InitializeRoutingTable %r %r", adapter, npdu) # reference the service access point # sap = self.elementService def InitializeRoutingTableAck(self, adapter, npdu): if _debug: NetworkServiceElement._debug("InitializeRoutingTableAck %r %r", adapter, npdu) # reference the service access point # sap = self.elementService def EstablishConnectionToNetwork(self, adapter, npdu): if _debug: NetworkServiceElement._debug("EstablishConnectionToNetwork %r %r", adapter, npdu) # reference the service access point # sap = self.elementService def DisconnectConnectionToNetwork(self, adapter, npdu): if _debug: NetworkServiceElement._debug("DisconnectConnectionToNetwork %r %r", adapter, npdu) # reference the service access point # sap = self.elementService PK*UHOuhbacpypes/apdu.py#!/usr/bin/python """ Application Layer Protocol Data Units """ from .errors import DecodingError from .debugging import ModuleLogger, DebugContents, bacpypes_debugging 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 .basetypes import ChannelValue, DateTime, DeviceAddress, ErrorType, \ EventState, EventTransitionBits, EventType, LifeSafetyOperation, \ NotificationParameters, NotifyType, ObjectPropertyReference, \ PropertyIdentifier, PropertyReference, PropertyValue, RecipientProcess, \ ResultFlags, Segmentation, TimeStamp, VTClass # some debugging _debug = 0 _log = ModuleLogger(globals()) # a dictionary of message type values and classes apdu_types = {} def register_apdu_type(klass): apdu_types[klass.pduType] = klass # a dictionary of confirmed request choices and classes confirmed_request_types = {} def register_confirmed_request_type(klass): confirmed_request_types[klass.serviceChoice] = klass # a dictionary of complex ack choices and classes complex_ack_types = {} def register_complex_ack_type(klass): complex_ack_types[klass.serviceChoice] = klass # a dictionary of unconfirmed request choices and classes unconfirmed_request_types = {} def register_unconfirmed_request_type(klass): unconfirmed_request_types[klass.serviceChoice] = klass # a dictionary of unconfirmed request choices and classes error_types = {} def register_error_type(klass): error_types[klass.serviceChoice] = klass # # encode_max_apdu_segments/decode_max_apdu_segments # def encode_max_apdu_segments(arg): if (arg > 64): return 7 return {None:0, 0:0, 2:1, 4:2, 8:3, 16:4, 32:5, 64:6}.get(arg) def decode_max_apdu_segments(arg): if (arg >= 7): return 128 return {0:None, 1:2, 2:4, 3:8, 4:16, 5:32, 6:64}.get(arg) # # encode_max_apdu_response/decode_max_apdu_response # def encode_max_apdu_response(arg): return {50:0, 128:1, 206:2, 480:3, 1024:4, 1476:5}.get(arg) def decode_max_apdu_response(arg): return {0:50, 1:128, 2:206, 3:480, 4:1024, 5:1476}.get(arg) # # APCI # @bacpypes_debugging class APCI(PCI, DebugContents): _debug_contents = ('apduType', 'apduSeg', 'apduMor', 'apduSA', 'apduSrv' , 'apduNak', 'apduSeq', 'apduWin', 'apduMaxSegs', 'apduMaxResp' , 'apduService', 'apduInvokeID', 'apduAbortRejectReason' ) def __init__(self, *args, **kwargs): if _debug: APCI._debug("__init__ %r %r", args, kwargs) super(APCI, self).__init__(*args, **kwargs) self.apduType = None self.apduSeg = None # segmented self.apduMor = None # more follows self.apduSA = None # segmented response accepted self.apduSrv = None # sent by server self.apduNak = None # negative acknowledgement self.apduSeq = None # sequence number self.apduWin = None # actual/proposed window size self.apduMaxSegs = None # maximum segments accepted (decoded) self.apduMaxResp = None # max response accepted (decoded) self.apduService = None # self.apduInvokeID = None # self.apduAbortRejectReason = None # def update(self, apci): PCI.update(self, apci) self.apduType = apci.apduType self.apduSeg = apci.apduSeg self.apduMor = apci.apduMor self.apduSA = apci.apduSA self.apduSrv = apci.apduSrv self.apduNak = apci.apduNak self.apduSeq = apci.apduSeq self.apduWin = apci.apduWin self.apduMaxSegs = apci.apduMaxSegs self.apduMaxResp = apci.apduMaxResp self.apduService = apci.apduService self.apduInvokeID = apci.apduInvokeID self.apduAbortRejectReason = apci.apduAbortRejectReason def __repr__(self): """Return a string representation of the PDU.""" # start with the class name sname = self.__module__ + '.' + self.__class__.__name__ # expand the type if possible stype = apdu_types.get(self.apduType, None) if stype: stype = stype.__name__ else: stype = '?' # add the invoke ID if it has one if self.apduInvokeID is not None: stype += ',' + str(self.apduInvokeID) # put it together return "<{0}({1}) instance at {2}>".format(sname, stype, hex(id(self))) def encode(self, pdu): """encode the contents of the APCI into the PDU.""" if _debug: APCI._debug("encode %r", pdu) PCI.update(pdu, self) if (self.apduType == ConfirmedRequestPDU.pduType): # PDU type buff = self.apduType << 4 if self.apduSeg: buff += 0x08 if self.apduMor: buff += 0x04 if self.apduSA: buff += 0x02 pdu.put(buff) pdu.put((encode_max_apdu_segments(self.apduMaxSegs) << 4) + encode_max_apdu_response(self.apduMaxResp)) pdu.put(self.apduInvokeID) if self.apduSeg: pdu.put(self.apduSeq) pdu.put(self.apduWin) pdu.put(self.apduService) elif (self.apduType == UnconfirmedRequestPDU.pduType): pdu.put(self.apduType << 4) pdu.put(self.apduService) elif (self.apduType == SimpleAckPDU.pduType): pdu.put(self.apduType << 4) pdu.put(self.apduInvokeID) pdu.put(self.apduService) elif (self.apduType == ComplexAckPDU.pduType): # PDU type buff = self.apduType << 4 if self.apduSeg: buff += 0x08 if self.apduMor: buff += 0x04 pdu.put(buff) pdu.put(self.apduInvokeID) if self.apduSeg: pdu.put(self.apduSeq) pdu.put(self.apduWin) pdu.put(self.apduService) elif (self.apduType == SegmentAckPDU.pduType): # PDU type buff = self.apduType << 4 if self.apduNak: buff += 0x02 if self.apduSrv: buff += 0x01 pdu.put(buff) pdu.put(self.apduInvokeID) pdu.put(self.apduSeq) pdu.put(self.apduWin) elif (self.apduType == ErrorPDU.pduType): pdu.put(self.apduType << 4) pdu.put(self.apduInvokeID) pdu.put(self.apduService) elif (self.apduType == RejectPDU.pduType): pdu.put(self.apduType << 4) pdu.put(self.apduInvokeID) pdu.put(self.apduAbortRejectReason) elif (self.apduType == AbortPDU.pduType): # PDU type buff = self.apduType << 4 if self.apduSrv: buff += 0x01 pdu.put(buff) pdu.put(self.apduInvokeID) pdu.put(self.apduAbortRejectReason) else: raise ValueError("invalid APCI.apduType") def decode(self, pdu): """decode the contents of the PDU into the APCI.""" if _debug: APCI._debug("decode %r", pdu) PCI.update(self, pdu) # decode the first octet buff = pdu.get() # decode the APCI type self.apduType = (buff >> 4) & 0x0F if (self.apduType == ConfirmedRequestPDU.pduType): self.apduSeg = ((buff & 0x08) != 0) self.apduMor = ((buff & 0x04) != 0) self.apduSA = ((buff & 0x02) != 0) buff = pdu.get() self.apduMaxSegs = decode_max_apdu_segments( (buff >> 4) & 0x07 ) self.apduMaxResp = decode_max_apdu_response( buff & 0x0F ) self.apduInvokeID = pdu.get() if self.apduSeg: self.apduSeq = pdu.get() self.apduWin = pdu.get() self.apduService = pdu.get() self.pduData = pdu.pduData elif (self.apduType == UnconfirmedRequestPDU.pduType): self.apduService = pdu.get() self.pduData = pdu.pduData elif (self.apduType == SimpleAckPDU.pduType): self.apduInvokeID = pdu.get() self.apduService = pdu.get() elif (self.apduType == ComplexAckPDU.pduType): self.apduSeg = ((buff & 0x08) != 0) self.apduMor = ((buff & 0x04) != 0) self.apduInvokeID = pdu.get() if self.apduSeg: self.apduSeq = pdu.get() self.apduWin = pdu.get() self.apduService = pdu.get() self.pduData = pdu.pduData elif (self.apduType == SegmentAckPDU.pduType): self.apduNak = ((buff & 0x02) != 0) self.apduSrv = ((buff & 0x01) != 0) self.apduInvokeID = pdu.get() self.apduSeq = pdu.get() self.apduWin = pdu.get() elif (self.apduType == ErrorPDU.pduType): self.apduInvokeID = pdu.get() self.apduService = pdu.get() self.pduData = pdu.pduData elif (self.apduType == RejectPDU.pduType): self.apduInvokeID = pdu.get() self.apduAbortRejectReason = pdu.get() elif (self.apduType == AbortPDU.pduType): self.apduSrv = ((buff & 0x01) != 0) self.apduInvokeID = pdu.get() self.apduAbortRejectReason = pdu.get() self.pduData = pdu.pduData else: raise DecodingError("invalid APDU type") def apci_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" if _debug: APCI._debug("apci_contents use_dict=%r as_class=%r", use_dict, as_class) # make/extend the dictionary of content if use_dict is None: use_dict = as_class() # copy the source and destination to make it easier to search if self.pduSource: use_dict.__setitem__('source', str(self.pduSource)) if self.pduDestination: use_dict.__setitem__('destination', str(self.pduDestination)) # loop through the elements for attr in APCI._debug_contents: value = getattr(self, attr, None) if value is None: continue if attr == 'apduType': mapped_value = apdu_types[self.apduType].__name__ elif attr == 'apduService': if self.apduType in (ConfirmedRequestPDU.pduType, SimpleAckPDU.pduType, ComplexAckPDU.pduType): mapped_value = confirmed_request_types[self.apduService].__name__ elif (self.apduType == UnconfirmedRequestPDU.pduType): mapped_value = unconfirmed_request_types[self.apduService].__name__ elif (self.apduType == ErrorPDU.pduType): mapped_value = error_types[self.apduService].__name__ else: mapped_value = value # save the mapped value use_dict.__setitem__(attr, mapped_value) # return what we built/updated return use_dict # # APDU # class APDU(APCI, PDUData): def __init__(self, *args, **kwargs): if _debug: APDU._debug("__init__ %r %r", args, kwargs) super(APDU, self).__init__(*args, **kwargs) def encode(self, pdu): if _debug: APCI._debug("encode %s", str(pdu)) APCI.encode(self, pdu) pdu.put_data(self.pduData) def decode(self, pdu): if _debug: APCI._debug("decode %s", str(pdu)) APCI.decode(self, pdu) self.pduData = pdu.get_data(len(pdu.pduData)) def apdu_contents(self, use_dict=None, as_class=dict): return PDUData.pdudata_contents(self, use_dict=use_dict, as_class=as_class) def dict_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" if _debug: APDU._debug("dict_contents use_dict=%r as_class=%r", use_dict, as_class) # make/extend the dictionary of content if use_dict is None: use_dict = as_class() # call the parent classes self.apci_contents(use_dict=use_dict, as_class=as_class) self.apdu_contents(use_dict=use_dict, as_class=as_class) # return what we built/updated return use_dict #------------------------------ # # _APDU # # This class masks the encode() and decode() functions of the APDU # so that derived classes use the update function to copy the contents # between PDU's. Otherwise the APCI content would be decoded twice. # class _APDU(APDU): def encode(self, pdu): APCI.update(pdu, self) pdu.put_data(self.pduData) def decode(self, pdu): APCI.update(self, pdu) self.pduData = pdu.get_data(len(pdu.pduData)) def set_context(self, context): self.pduUserData = context.pduUserData self.pduDestination = context.pduSource self.pduExpectingReply = 0 self.pduNetworkPriority = context.pduNetworkPriority self.apduInvokeID = context.apduInvokeID def __repr__(self): """Return a string representation of the APDU.""" # start with the class name sname = self.__module__ + '.' + self.__class__.__name__ # the type is the service stype = str(self.apduService) # add the invoke ID if it has one if self.apduInvokeID is not None: stype += ',' + str(self.apduInvokeID) # put it together return "<{0}({1}) instance at {2}>".format(sname, stype, hex(id(self))) # # ConfirmedRequestPDU # @bacpypes_debugging class ConfirmedRequestPDU(_APDU): pduType = 0 def __init__(self, choice=None, *args, **kwargs): if _debug: ConfirmedRequestPDU._debug("__init__ %r %r %r", choice, args, kwargs) super(ConfirmedRequestPDU, self).__init__(*args, **kwargs) self.apduType = ConfirmedRequestPDU.pduType self.apduService = choice self.pduExpectingReply = 1 register_apdu_type(ConfirmedRequestPDU) # # UnconfirmedRequestPDU # @bacpypes_debugging class UnconfirmedRequestPDU(_APDU): pduType = 1 def __init__(self, choice=None, *args, **kwargs): if _debug: UnconfirmedRequestPDU._debug("__init__ %r %r %r", choice, args, kwargs) super(UnconfirmedRequestPDU, self).__init__(*args, **kwargs) self.apduType = UnconfirmedRequestPDU.pduType self.apduService = choice register_apdu_type(UnconfirmedRequestPDU) # # SimpleAckPDU # @bacpypes_debugging class SimpleAckPDU(_APDU): pduType = 2 def __init__(self, choice=None, invokeID=None, context=None, *args, **kwargs): if _debug: SimpleAckPDU._debug("__init__ %r %r %r %r %r", choice, invokeID, context, args, kwargs) super(SimpleAckPDU, self).__init__(*args, **kwargs) self.apduType = SimpleAckPDU.pduType self.apduService = choice self.apduInvokeID = invokeID # use the context to fill in most of the fields if context is not None: self.apduService = context.apduService self.set_context(context) register_apdu_type(SimpleAckPDU) # # ComplexAckPDU # @bacpypes_debugging class ComplexAckPDU(_APDU): pduType = 3 def __init__(self, choice=None, invokeID=None, context=None, *args, **kwargs): if _debug: ComplexAckPDU._debug("__init__ %r %r %r %r %r", choice, invokeID, context, args, kwargs) super(ComplexAckPDU, self).__init__(*args, **kwargs) self.apduType = ComplexAckPDU.pduType self.apduService = choice self.apduInvokeID = invokeID # use the context to fill in most of the fields if context is not None: self.apduService = context.apduService self.set_context(context) register_apdu_type(ComplexAckPDU) # # SegmentAckPDU # @bacpypes_debugging class SegmentAckPDU(_APDU): pduType = 4 def __init__(self, nak=None, srv=None, invokeID=None, sequenceNumber=None, windowSize=None, *args, **kwargs): if _debug: SegmentAckPDU._debug("__init__ %r %r %r %r %r %r %r", nak, srv, invokeID, sequenceNumber, windowSize, args, kwargs) super(SegmentAckPDU, self).__init__(*args, **kwargs) if nak is None: raise ValueError("nak is None") if srv is None: raise ValueError("srv is None") if invokeID is None: raise ValueError("invokeID is None") if sequenceNumber is None: raise ValueError("sequenceNumber is None") if windowSize is None: raise ValueError("windowSize is None") self.apduType = SegmentAckPDU.pduType self.apduNak = nak self.apduSrv = srv self.apduInvokeID = invokeID self.apduSeq = sequenceNumber self.apduWin = windowSize register_apdu_type(SegmentAckPDU) # # ErrorPDU # @bacpypes_debugging class ErrorPDU(_APDU): pduType = 5 def __init__(self, choice=None, invokeID=None, context=None, *args, **kwargs): if _debug: ErrorPDU._debug("__init__ %r %r %r %r %r", choice, invokeID, context, args, kwargs) super(ErrorPDU, self).__init__(*args, **kwargs) self.apduType = ErrorPDU.pduType self.apduService = choice self.apduInvokeID = invokeID # use the context to fill in most of the fields if context is not None: self.apduService = context.apduService self.set_context(context) register_apdu_type(ErrorPDU) # # RejectPDU # class RejectReason(Enumerated): vendor_range = (64, 255) enumerations = \ { 'other':0 , 'bufferOverflow':1 , 'inconsistentParameters':2 , 'invalidParameterDatatype':3 , 'invalidTag':4 , 'missingRequiredParameter':5 , 'parameterOutOfRange':6 , 'tooManyArguments':7 , 'undefinedEnumeration':8 , 'unrecognizedService':9 } expand_enumerations(RejectReason) @bacpypes_debugging class RejectPDU(_APDU): pduType = 6 def __init__(self, invokeID=None, reason=None, context=None, *args, **kwargs): if _debug: RejectPDU._debug("__init__ %r %r %r %r %r", invokeID, reason, context, args, kwargs) super(RejectPDU, self).__init__(*args, **kwargs) self.apduType = RejectPDU.pduType self.apduInvokeID = invokeID if isinstance(reason, str): reason = RejectReason(reason).get_long() self.apduAbortRejectReason = reason # use the context to fill in most of the fields if context is not None: self.set_context(context) register_apdu_type(RejectPDU) # # AbortPDU # class AbortReason(Enumerated): vendor_range = (64, 255) enumerations = \ { 'other':0 , 'bufferOverflow':1 , 'invalidApduInThisState':2 , 'preemptedByHigherPriorityTask':3 #wtm corrected spelling , 'segmentationNotSupported':4 , 'securityError':5 , 'insufficientSecurity':6 , 'windowSizeOutOfRange':7 , 'applicationExceededReplyTime':8 , 'outOfResources':9 , 'tsmTimeout':10 , 'apduTooLong':11 # 64..255 are available for vendor codes , 'serverTimeout':64 , 'noResponse':65 } expand_enumerations(AbortReason) @bacpypes_debugging class AbortPDU(_APDU): pduType = 7 def __init__(self, srv=None, invokeID=None, reason=None, context=None, *args, **kwargs): if _debug: AbortPDU._debug("__init__ %r %r %r %r %r %r", srv, invokeID, reason, context, args, kwargs) super(AbortPDU, self).__init__(*args, **kwargs) self.apduType = AbortPDU.pduType self.apduSrv = srv self.apduInvokeID = invokeID if isinstance(reason, str): reason = AbortReason(reason).get_long() self.apduAbortRejectReason = reason # use the context to fill in most of the fields if context is not None: self.set_context(context) def __str__(self): try: reason = AbortReason._xlate_table[self.apduAbortRejectReason] except: reason = str(self.apduAbortRejectReason) + '?' return reason register_apdu_type(AbortPDU) #------------------------------ # # APCISequence # @bacpypes_debugging class APCISequence(APCI, Sequence): def __init__(self, *args, **kwargs): if _debug: APCISequence._debug("__init__ %r %r", args, kwargs) super(APCISequence, self).__init__(*args, **kwargs) # start with an empty tag list self._tag_list = None def encode(self, apdu): if _debug: APCISequence._debug("encode %r", apdu) # copy the header fields apdu.update(self) # create a tag list self._tag_list = TagList() Sequence.encode(self, self._tag_list) # encode the tag list self._tag_list.encode(apdu) def decode(self, apdu): if _debug: APCISequence._debug("decode %r", apdu) # copy the header fields self.update(apdu) # create a tag list and decode the rest of the data self._tag_list = TagList() self._tag_list.decode(apdu) # pass the taglist to the Sequence for additional decoding Sequence.decode(self, self._tag_list) # trailing unmatched tags if self._tag_list: if _debug: APCISequence._debug(" - trailing unmatched tags") def apdu_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" if _debug: APCISequence._debug("apdu_contents use_dict=%r as_class=%r", use_dict, as_class) # make/extend the dictionary of content if use_dict is None: use_dict = as_class() # set the function based on the class name use_dict.__setitem__('function', self.__class__.__name__) # fill in from the sequence contents Sequence.dict_contents(self, use_dict=use_dict, as_class=as_class) # return what we built/updated return use_dict # # ConfirmedRequestSequence # @bacpypes_debugging class ConfirmedRequestSequence(APCISequence, ConfirmedRequestPDU): serviceChoice = None def __init__(self, *args, **kwargs): if _debug: ConfirmedRequestSequence._debug("__init__ %r %r", args, kwargs) super(ConfirmedRequestSequence, self).__init__(*args, choice=self.serviceChoice, **kwargs) # # ComplexAckSequence # @bacpypes_debugging class ComplexAckSequence(APCISequence, ComplexAckPDU): serviceChoice = None def __init__(self, *args, **kwargs): if _debug: ComplexAckSequence._debug("__init__ %r %r", args, kwargs) super(ComplexAckSequence, self).__init__(*args, choice=self.serviceChoice, **kwargs) # # UnconfirmedRequestSequence # @bacpypes_debugging class UnconfirmedRequestSequence(APCISequence, UnconfirmedRequestPDU): serviceChoice = None def __init__(self, *args, **kwargs): if _debug: UnconfirmedRequestSequence._debug("__init__ %r %r", args, kwargs) super(UnconfirmedRequestSequence, self).__init__(*args, choice=self.serviceChoice, **kwargs) # # ErrorSequence # @bacpypes_debugging class ErrorSequence(APCISequence, ErrorPDU): serviceChoice = None def __init__(self, *args, **kwargs): if _debug: ErrorSequence._debug("__init__ %r %r", args, kwargs) super(ErrorSequence, self).__init__(*args, choice=self.serviceChoice, **kwargs) #------------------------------ class Error(ErrorSequence): sequenceElements = ErrorType.sequenceElements def __str__(self): return str(self.errorClass) + ": " + str(self.errorCode) error_types[12] = Error error_types[14] = Error class ChangeListError(ErrorSequence): sequenceElements = \ [ Element('errorType', ErrorType, 0) , Element('firstFailedElementNumber', Unsigned, 1) ] def __str__(self): return "change list error, first failed element number " + str(self.firstFailedElementNumber) error_types[8] = ChangeListError error_types[9] = ChangeListError class CreateObjectError(ErrorSequence): sequenceElements = \ [ Element('errorType', ErrorType, 0) , Element('firstFailedElementNumber', Unsigned, 1) ] def __str__(self): return "create object error, first failed element number " + str(self.firstFailedElementNumber) error_types[10] = CreateObjectError class ConfirmedPrivateTransferError(ErrorSequence): sequenceElements = \ [ Element('errorType', ErrorType, 0) , Element('vendorID', Unsigned, 1) , Element('serviceNumber', Unsigned, 2) , Element('errorParameters', Any, 3, True) ] error_types[18] = ConfirmedPrivateTransferError class WritePropertyMultipleError(ErrorSequence): sequenceElements = \ [ Element('errorType', ErrorType, 0) , Element('firstFailedWriteAttempt', ObjectPropertyReference, 1) ] error_types[16] = WritePropertyMultipleError class VTCloseError(ErrorSequence): sequenceElements = \ [ Element('errorType', ErrorType, 0) , Element('listOfVTSessionIdentifiers', SequenceOf(Unsigned), 1, True) ] error_types[22] = VTCloseError #----- class ReadPropertyRequest(ConfirmedRequestSequence): serviceChoice = 12 sequenceElements = \ [ Element('objectIdentifier', ObjectIdentifier, 0) , Element('propertyIdentifier', PropertyIdentifier, 1) , Element('propertyArrayIndex', Unsigned, 2, True) ] register_confirmed_request_type(ReadPropertyRequest) class ReadPropertyACK(ComplexAckSequence): serviceChoice = 12 sequenceElements = \ [ Element('objectIdentifier', ObjectIdentifier, 0) , Element('propertyIdentifier', PropertyIdentifier, 1) , Element('propertyArrayIndex', Unsigned, 2, True) , Element('propertyValue', Any, 3) ] register_complex_ack_type(ReadPropertyACK) #----- class ReadAccessSpecification(Sequence): sequenceElements = \ [ Element('objectIdentifier', ObjectIdentifier, 0) , Element('listOfPropertyReferences', SequenceOf(PropertyReference), 1) ] class ReadPropertyMultipleRequest(ConfirmedRequestSequence): serviceChoice = 14 sequenceElements = \ [ Element('listOfReadAccessSpecs', SequenceOf(ReadAccessSpecification)) ] register_confirmed_request_type(ReadPropertyMultipleRequest) class ReadAccessResultElementChoice(Choice): choiceElements = \ [ Element('propertyValue', Any, 4) , Element('propertyAccessError', ErrorType, 5) ] class ReadAccessResultElement(Sequence): sequenceElements = \ [ Element('propertyIdentifier', PropertyIdentifier, 2) , Element('propertyArrayIndex', Unsigned, 3, True) , Element('readResult', ReadAccessResultElementChoice) ] class ReadAccessResult(Sequence): sequenceElements = \ [ Element('objectIdentifier', ObjectIdentifier, 0) , Element('listOfResults', SequenceOf(ReadAccessResultElement), 1) ] class ReadPropertyMultipleACK(ComplexAckSequence): serviceChoice = 14 sequenceElements = \ [ Element('listOfReadAccessResults', SequenceOf(ReadAccessResult)) ] register_complex_ack_type(ReadPropertyMultipleACK) #----- class RangeByPosition(Sequence): sequenceElements = \ [ Element('referenceIndex', Unsigned) , Element('count', Integer) ] class RangeBySequenceNumber(Sequence): sequenceElements = \ [ Element('referenceIndex', Unsigned) , Element('count', Integer) ] class RangeByTime(Sequence): sequenceElements = \ [ Element('referenceTime', DateTime) , Element('count', Integer) ] class Range(Choice): choiceElements = \ [ Element('byPosition', RangeByPosition, 3) , Element('bySequenceNumber', RangeBySequenceNumber, 6) , Element('byTime', RangeByTime, 7) ] class ReadRangeRequest(ConfirmedRequestSequence): serviceChoice = 26 sequenceElements = \ [ Element('objectIdentifier', ObjectIdentifier, 0) , Element('propertyIdentifier', PropertyIdentifier, 1) , Element('propertyArrayIndex', Unsigned, 2, True) , Element('range', Range, optional=True) ] register_confirmed_request_type(ReadRangeRequest) class ReadRangeACK(ComplexAckSequence): serviceChoice = 26 sequenceElements = \ [ Element('objectIdentifier', ObjectIdentifier, 0) , Element('propertyIdentifier', PropertyIdentifier, 1) , Element('propertyArrayIndex', Unsigned, 2, True) , Element('resultFlags', ResultFlags, 3) , Element('itemCount', Unsigned, 4) , Element('itemData', SequenceOf(Any), 5) , Element('firstSequenceNumber', Unsigned, 6, True) ] register_complex_ack_type(ReadRangeACK) #----- class WritePropertyRequest(ConfirmedRequestSequence): serviceChoice = 15 sequenceElements = \ [ Element('objectIdentifier', ObjectIdentifier, 0) , Element('propertyIdentifier', PropertyIdentifier, 1) , Element('propertyArrayIndex', Unsigned, 2, True) , Element('propertyValue', Any, 3) , Element('priority', Integer, 4, True) ] register_confirmed_request_type(WritePropertyRequest) #----- class WriteAccessSpecification(Sequence): sequenceElements = \ [ Element('objectIdentifier', ObjectIdentifier, 0) , Element('listOfProperties', SequenceOf(PropertyValue), 1) ] class WritePropertyMultipleRequest(ConfirmedRequestSequence): serviceChoice = 16 sequenceElements = \ [ Element('listOfWriteAccessSpecs', SequenceOf(WriteAccessSpecification)) ] register_confirmed_request_type(WritePropertyMultipleRequest) #----- class GroupChannelValue(Sequence): sequenceElements = \ [ Element('channel', Unsigned, 0) , Element('overridingPriority', Unsigned, 1, True) , Element('value', ChannelValue) ] class WriteGroupRequest(UnconfirmedRequestSequence): serviceChoice = 10 sequenceElements = \ [ Element('groupNumber', Unsigned, 0) , Element('writePriority', Unsigned, 1) , Element('changeList', SequenceOf(GroupChannelValue), 2) , Element('inhibitDelay', Boolean, 3, True) ] register_unconfirmed_request_type(WriteGroupRequest) #----- class IAmRequest(UnconfirmedRequestSequence): serviceChoice = 0 sequenceElements = \ [ Element('iAmDeviceIdentifier', ObjectIdentifier) , Element('maxAPDULengthAccepted', Unsigned) , Element('segmentationSupported', Segmentation) , Element('vendorID', Unsigned) ] register_unconfirmed_request_type(IAmRequest) #----- class IHaveRequest(UnconfirmedRequestSequence): serviceChoice = 1 sequenceElements = \ [ Element('deviceIdentifier', ObjectIdentifier) , Element('objectIdentifier', ObjectIdentifier) , Element('objectName', CharacterString) ] register_unconfirmed_request_type(IHaveRequest) #----- class WhoHasLimits(Sequence): sequenceElements = \ [ Element('deviceInstanceRangeLowLimit', Unsigned, 0) , Element('deviceInstanceRangeHighLimit', Unsigned, 1) ] class WhoHasObject(Choice): choiceElements = \ [ Element('objectIdentifier', ObjectIdentifier, 2) , Element('objectName', CharacterString, 3) ] class WhoHasRequest(UnconfirmedRequestSequence): serviceChoice = 7 sequenceElements = \ [ Element('limits', WhoHasLimits, None, True) , Element('object', WhoHasObject) ] register_unconfirmed_request_type(WhoHasRequest) #----- class WhoIsRequest(UnconfirmedRequestSequence): serviceChoice = 8 sequenceElements = \ [ Element('deviceInstanceRangeLowLimit', Unsigned, 0, True) , Element('deviceInstanceRangeHighLimit', Unsigned, 1, True) ] register_unconfirmed_request_type(WhoIsRequest) #----- class EventNotificationParameters(Sequence): sequenceElements = \ [ Element('processIdentifier', Unsigned, 0) , Element('initiatingDeviceIdentifier', ObjectIdentifier, 1) , Element('eventObjectIdentifier', ObjectIdentifier, 2) , Element('timeStamp', TimeStamp, 3) , Element('notificationClass', Unsigned, 4) , Element('priority', Unsigned, 5) , Element('eventType', EventType, 6) , Element('messageText', CharacterString, 7, True) , Element('notifyType', NotifyType, 8) , Element('ackRequired', Boolean, 9, True) , Element('fromState', EventState, 10, True) , Element('toState', EventState, 11) , Element('eventValues', NotificationParameters, 12, True) ] class ConfirmedEventNotificationRequest(ConfirmedRequestSequence): serviceChoice = 2 sequenceElements = EventNotificationParameters.sequenceElements register_confirmed_request_type(ConfirmedEventNotificationRequest) class UnconfirmedEventNotificationRequest(UnconfirmedRequestSequence): serviceChoice = 3 sequenceElements = EventNotificationParameters.sequenceElements register_unconfirmed_request_type(UnconfirmedEventNotificationRequest) #----- class COVNotificationParameters(Sequence): sequenceElements = \ [ Element('subscriberProcessIdentifier', Unsigned, 0) , Element('initiatingDeviceIdentifier', ObjectIdentifier, 1) , Element('monitoredObjectIdentifier', ObjectIdentifier, 2) , Element('timeRemaining', Unsigned, 3) , Element('listOfValues', SequenceOf(PropertyValue), 4) ] class ConfirmedCOVNotificationRequest(ConfirmedRequestSequence): serviceChoice = 1 sequenceElements = COVNotificationParameters.sequenceElements register_confirmed_request_type(ConfirmedCOVNotificationRequest) class UnconfirmedCOVNotificationRequest(UnconfirmedRequestSequence): serviceChoice = 2 sequenceElements = COVNotificationParameters.sequenceElements register_unconfirmed_request_type(UnconfirmedCOVNotificationRequest) #----- class UnconfirmedPrivateTransferRequest(UnconfirmedRequestSequence): serviceChoice = 4 sequenceElements = \ [ Element('vendorID', Unsigned, 0) , Element('serviceNumber', Unsigned, 1) , Element('serviceParameters', Any, 2, True) ] register_unconfirmed_request_type(UnconfirmedPrivateTransferRequest) #----- class UnconfirmedTextMessageRequestMessageClass(Choice): choiceElements = \ [ Element('numeric', Unsigned, 0) , Element('character', CharacterString, 1) ] class UnconfirmedTextMessageRequestMessagePriority(Enumerated): enumerations = \ { 'normal':0 , 'urgent':1 } class UnconfirmedTextMessageRequest(UnconfirmedRequestSequence): serviceChoice = 5 sequenceElements = \ [ Element('textMessageSourceDevice', ObjectIdentifier, 0) , Element('messageClass', UnconfirmedTextMessageRequestMessageClass, 1, True) , Element('messagePriority', UnconfirmedTextMessageRequestMessagePriority, 2) , Element('message', CharacterString, 3) ] register_unconfirmed_request_type(UnconfirmedTextMessageRequest) #----- class TimeSynchronizationRequest(UnconfirmedRequestSequence): serviceChoice = 6 sequenceElements = \ [ Element('time', DateTime) ] register_unconfirmed_request_type(TimeSynchronizationRequest) #----- class UTCTimeSynchronizationRequest(UnconfirmedRequestSequence): serviceChoice = 9 sequenceElements = \ [ Element('time', DateTime) ] register_unconfirmed_request_type(UTCTimeSynchronizationRequest) #----- class AcknowledgeAlarmRequest(ConfirmedRequestSequence): serviceChoice = 0 sequenceElements = \ [ Element('acknowledgingProcessIdentifier', Unsigned, 0) , Element('eventObjectIdentifier', ObjectIdentifier, 1) , Element('eventStateAcknowledged', EventState, 2) , Element('timeStamp', TimeStamp, 3) , Element('acknowledgmentSource', CharacterString, 4) , Element('timeOfAcknowledgment', TimeStamp, 5) ] register_confirmed_request_type(AcknowledgeAlarmRequest) #----- class GetAlarmSummaryRequest(ConfirmedRequestSequence): serviceChoice = 3 sequenceElements = \ [ ] register_confirmed_request_type(GetAlarmSummaryRequest) class GetAlarmSummaryAlarmSummary(Sequence): sequenceElements = \ [ Element('objectIdentifier', ObjectIdentifier,) , Element('alarmState', EventState,) , Element('acknowledgedTransitions', EventTransitionBits) ] class GetAlarmSummaryACK(ComplexAckSequence): serviceChoice = 3 sequenceElements = \ [ Element('listOfAlarmSummaries', SequenceOf(GetAlarmSummaryAlarmSummary)) ] register_complex_ack_type(GetAlarmSummaryACK) #----- class GetEnrollmentSummaryRequestAcknowledgmentFilterType(Enumerated): enumerations = \ { 'all':0 , 'acked':1 , 'notAcked':2 } class GetEnrollmentSummaryRequestEventStateFilterType(Enumerated): enumerations = \ { 'offnormal':0 , 'fault':1 , 'normal':2 , 'all':3 , 'active':4 } class GetEnrollmentSummaryRequestPriorityFilterType: sequenceElements = \ [ Element('minPriority', Unsigned, 0) , Element('maxPriority', Unsigned, 1) ] class GetEnrollmentSummaryRequest(ConfirmedRequestSequence): serviceChoice = 4 sequenceElements = \ [ Element('acknowledgmentFilter', GetEnrollmentSummaryRequestAcknowledgmentFilterType, 0) , Element('enrollmentFilter', RecipientProcess, 1, True) , Element('eventStateFilter', GetEnrollmentSummaryRequestEventStateFilterType, 2, True) , Element('eventTypeFilter', EventType, 3, True) , Element('priorityFilter', GetEnrollmentSummaryRequestPriorityFilterType, 4, True) , Element('notificationClassFilter', Unsigned, 5, True) ] register_confirmed_request_type(GetEnrollmentSummaryRequest) class GetEnrollmentSummaryEnrollmentSummary(Sequence): sequenceElements = \ [ Element('objectIdentifier', ObjectIdentifier) , Element('eventType', EventType) , Element('eventState', EventState) , Element('priority', Unsigned) , Element('notificationClass', Unsigned, optional=True) ] class GetEnrollmentSummaryACK(ComplexAckSequence): serviceChoice = 4 sequenceElements = \ [ Element('listOfEnrollmentSummaries', SequenceOf(GetEnrollmentSummaryEnrollmentSummary)) ] register_complex_ack_type(GetEnrollmentSummaryACK) #----- class GetEventInformationRequest(ConfirmedRequestSequence): serviceChoice = 29 sequenceElements = \ [ Element('lastReceivedObjectIdentifier', ObjectIdentifier, 0, True) ] register_confirmed_request_type(GetEventInformationRequest) class GetEventInformationEventSummary(Sequence): sequenceElements = \ [ Element('objectIdentifier', ObjectIdentifier, 0) , Element('eventState', EventState, 1) , Element('acknowledgedTransitions', EventTransitionBits, 2) , Element('eventTimeStamps', SequenceOf(TimeStamp), 3) , Element('notifyType', NotifyType, 4) , Element('eventEnable', EventTransitionBits, 5) , Element('eventPriorities', SequenceOf(Unsigned), 6) ] class GetEventInformationACK(ComplexAckSequence): serviceChoice = 29 sequenceElements = \ [ Element('listOfEventSummaries', SequenceOf(GetEventInformationEventSummary), 0) , Element('moreEvents', Boolean, 1) ] register_complex_ack_type(GetEventInformationACK) #----- class LifeSafetyOperationRequest(ConfirmedRequestSequence): serviceChoice = 27 sequenceElements = \ [ Element('requestingProcessIdentifier', Unsigned, 0) , Element('requestingSource', CharacterString, 1) , Element('request', LifeSafetyOperation, 2) , Element('objectIdentifier', ObjectIdentifier, 3) ] register_confirmed_request_type(LifeSafetyOperationRequest) #----- class SubscribeCOVRequest(ConfirmedRequestSequence): serviceChoice = 5 sequenceElements = \ [ Element('subscriberProcessIdentifier', Unsigned, 0) , Element('monitoredObjectIdentifier', ObjectIdentifier, 1) , Element('issueConfirmedNotifications', Boolean, 2, True) , Element('lifetime', Unsigned, 3, True) ] register_confirmed_request_type(SubscribeCOVRequest) #----- class SubscribeCOVPropertyRequest(ConfirmedRequestSequence): serviceChoice = 28 sequenceElements = \ [ Element('subscriberProcessIdentifier', Unsigned, 0) , Element('monitoredObjectIdentifier', ObjectIdentifier, 1) , Element('issueConfirmedNotifications', Boolean, 2) , Element('lifetime', Unsigned, 3) , Element('monitoredPropertyIdentifier', PropertyReference, 4) , Element('covIncrement', Real, 5) ] register_confirmed_request_type(SubscribeCOVPropertyRequest) #----- class AtomicReadFileRequestAccessMethodChoiceStreamAccess(Sequence): sequenceElements = \ [ Element('fileStartPosition', Integer) , Element('requestedOctetCount', Unsigned) ] class AtomicReadFileRequestAccessMethodChoiceRecordAccess(Sequence): sequenceElements = \ [ Element('fileStartRecord', Integer) , Element('requestedRecordCount', Unsigned) ] class AtomicReadFileRequestAccessMethodChoice(Choice): choiceElements = \ [ Element('streamAccess', AtomicReadFileRequestAccessMethodChoiceStreamAccess, 0) , Element('recordAccess', AtomicReadFileRequestAccessMethodChoiceRecordAccess, 1) ] class AtomicReadFileRequest(ConfirmedRequestSequence): serviceChoice = 6 sequenceElements = \ [ Element('fileIdentifier', ObjectIdentifier) , Element('accessMethod', AtomicReadFileRequestAccessMethodChoice) ] register_confirmed_request_type(AtomicReadFileRequest) class AtomicReadFileACKAccessMethodStreamAccess(Sequence): sequenceElements = \ [ Element('fileStartPosition', Integer) , Element('fileData', OctetString) ] class AtomicReadFileACKAccessMethodRecordAccess(Sequence): sequenceElements = \ [ Element('fileStartRecord', Integer) , Element('returnedRecordCount', Unsigned) , Element('fileRecordData', SequenceOf(OctetString)) ] class AtomicReadFileACKAccessMethodChoice(Choice): choiceElements = \ [ Element('streamAccess', AtomicReadFileACKAccessMethodStreamAccess, 0) , Element('recordAccess', AtomicReadFileACKAccessMethodRecordAccess, 1) ] class AtomicReadFileACK(ComplexAckSequence): serviceChoice = 6 sequenceElements = \ [ Element('endOfFile', Boolean) , Element('accessMethod', AtomicReadFileACKAccessMethodChoice) ] register_complex_ack_type(AtomicReadFileACK) #----- class AtomicWriteFileRequestAccessMethodChoiceStreamAccess(Sequence): sequenceElements = \ [ Element('fileStartPosition', Integer) , Element('fileData', OctetString) ] class AtomicWriteFileRequestAccessMethodChoiceRecordAccess(Sequence): sequenceElements = \ [ Element('fileStartRecord', Integer) , Element('recordCount', Unsigned) , Element('fileRecordData', SequenceOf(OctetString)) ] class AtomicWriteFileRequestAccessMethodChoice(Choice): choiceElements = \ [ Element('streamAccess', AtomicWriteFileRequestAccessMethodChoiceStreamAccess, 0) , Element('recordAccess', AtomicWriteFileRequestAccessMethodChoiceRecordAccess, 1) ] class AtomicWriteFileRequest(ConfirmedRequestSequence): serviceChoice = 7 sequenceElements = \ [ Element('fileIdentifier', ObjectIdentifier) , Element('accessMethod', AtomicWriteFileRequestAccessMethodChoice) ] register_confirmed_request_type(AtomicWriteFileRequest) class AtomicWriteFileACK(ComplexAckSequence): serviceChoice = 7 sequenceElements = \ [ Element('fileStartPosition', Integer, 0, True) , Element('fileStartRecord', Integer, 1, True) ] register_complex_ack_type(AtomicWriteFileACK) #----- class AddListElementRequest(ConfirmedRequestSequence): serviceChoice = 8 sequenceElements = \ [ Element('objectIdentifier', ObjectIdentifier, 0) , Element('propertyIdentifier', PropertyIdentifier, 1) , Element('propertyArrayIndex', Unsigned, 2, True) , Element('listOfElements', Any, 3) ] register_confirmed_request_type(AddListElementRequest) #----- class CreateObjectRequestObjectSpecifier(Choice): choiceElements = \ [ Element('objectType', ObjectType, 0) , Element('objectIdentifier',ObjectIdentifier, 1) ] class CreateObjectRequest(ConfirmedRequestSequence): serviceChoice = 10 sequenceElements = \ [ Element('objectSpecifier', CreateObjectRequestObjectSpecifier, 0) , Element('listOfInitialValues', SequenceOf(PropertyValue), 1, True) ] register_confirmed_request_type(CreateObjectRequest) class CreateObjectACK(ComplexAckSequence): serviceChoice = 10 sequenceElements = \ [ Element('objectIdentifier', ObjectIdentifier) ] register_complex_ack_type(CreateObjectACK) #----- class DeleteObjectRequest(ConfirmedRequestSequence): serviceChoice = 11 sequenceElements = \ [ Element('objectIdentifier', ObjectIdentifier) ] register_confirmed_request_type(DeleteObjectRequest) #----- class RemoveListElementRequest(ConfirmedRequestSequence): serviceChoice = 9 sequenceElements = \ [ Element('objectIdentifier', ObjectIdentifier, 0) , Element('propertyIdentifier', PropertyIdentifier, 1) , Element('propertyArrayIndex', Unsigned, 2) , Element('listOfElements', Any, 3) ] register_confirmed_request_type(RemoveListElementRequest) #----- class DeviceCommunicationControlRequestEnableDisable(Enumerated): enumerations = \ { 'enable':0 , 'disable':1 , 'defaultInitiation':2 } class DeviceCommunicationControlRequest(ConfirmedRequestSequence): serviceChoice = 17 sequenceElements = \ [ Element('timeDuration', Unsigned, 0, True) , Element('enableDisable', DeviceCommunicationControlRequestEnableDisable, 1, True) , Element('password', CharacterString, 2, True) ] register_confirmed_request_type(DeviceCommunicationControlRequest) class ConfirmedPrivateTransferRequest(ConfirmedRequestSequence): serviceChoice = 18 sequenceElements = \ [ Element('vendorID', Unsigned, 0) , Element('serviceNumber', Unsigned, 1) , Element('serviceParameters', Any, 2) ] register_confirmed_request_type(ConfirmedPrivateTransferRequest) class ConfirmedPrivateTransferACK(ComplexAckSequence): serviceChoice = 18 sequenceElements = \ [ Element('vendorID', Unsigned, 0) , Element('serviceNumber', Unsigned, 1) , Element('resultBlock', Any, 2) ] register_complex_ack_type(ConfirmedPrivateTransferACK) #----- class ConfirmedTextMessageRequestMessageClass(Choice): choiceElements = \ [ Element('numeric', Unsigned, 0) , Element('character', CharacterString, 1) ] class ConfirmedTextMessageRequestMessagePriority(Enumerated): enumerations = \ { 'normal':0 , 'urgent':1 } class ConfirmedTextMessageRequest(ConfirmedRequestSequence): serviceChoice = 19 sequenceElements = \ [ Element('textMessageSourceDevice', ObjectIdentifier, 0) , Element('messageClass', ConfirmedTextMessageRequestMessageClass, 1, True) , Element('messagePriority', ConfirmedTextMessageRequestMessagePriority, 2) , Element('message', CharacterString, 3) ] register_confirmed_request_type(ConfirmedTextMessageRequest) #----- class ReinitializeDeviceRequestReinitializedStateOfDevice(Enumerated): enumerations = \ { 'coldstart':0 , 'warmstart':1 , 'startbackup':2 , 'endbackup':3 , 'startrestore':4 , 'endrestore':5 , 'abortrestore':6 } class ReinitializeDeviceRequest(ConfirmedRequestSequence): serviceChoice = 20 sequenceElements = \ [ Element('reinitializedStateOfDevice', ReinitializeDeviceRequestReinitializedStateOfDevice, 0) , Element('password', CharacterString, 1, True) ] register_confirmed_request_type(ReinitializeDeviceRequest) #----- class VTOpenRequest(ConfirmedRequestSequence): serviceChoice = 21 sequenceElements = \ [ Element('vtClass', VTClass,) , Element('localVTSessionIdentifier', Unsigned) ] register_confirmed_request_type(VTOpenRequest) class VTOpenACK(ComplexAckSequence): serviceChoice = 21 sequenceElements = \ [ Element('remoteVTSessionIdentifier', Unsigned) ] register_complex_ack_type(VTOpenACK) class VTCloseRequest(ConfirmedRequestSequence): serviceChoice = 22 sequenceElements = \ [ Element('listOfRemoteVTSessionIdentifiers', SequenceOf(Unsigned)) ] register_confirmed_request_type(VTCloseRequest) class VTDataRequest(ConfirmedRequestSequence): serviceChoice = 23 sequenceElements = \ [ Element('vtSessionIdentifier', Unsigned,) , Element('vtNewData', OctetString) , Element('vtDataFlag', Unsigned) ] register_confirmed_request_type(VTDataRequest) class VTDataACK(ComplexAckSequence): serviceChoice = 23 sequenceElements = \ [ Element('allNewDataAccepted', Boolean, 0) , Element('acceptedOctetCount', Unsigned, 1) ] register_complex_ack_type(VTDataACK) #----- # removed in version 1, revision 11 class AuthenticateRequest(ConfirmedRequestSequence): serviceChoice = 24 sequenceElements = \ [ Element('pseudoRandomNumber', Unsigned, 0) , Element('expectedInvokeID', Unsigned, 1) , Element('operatorName', CharacterString, 2) , Element('operatorPassword', CharacterString, 3) , Element('startEncipheredSession', Boolean, 4) ] # removed in version 1, revision 11 class AuthenticateACK(ComplexAckSequence): serviceChoice = 24 sequenceElements = \ [ Element('modifiedRandomNumber', Unsigned) ] # removed in version 1, revision 11 class RequestKeyRequest(ConfirmedRequestSequence): serviceChoice = 25 sequenceElements = \ [ Element('requestingDeviceIdentifier', ObjectIdentifier) , Element('requestingDeviceAddress', DeviceAddress) , Element('remoteDeviceIdentifier', ObjectIdentifier) , Element('remoteDeviceAddress', DeviceAddress) ] #----------------------------------- class ConfirmedServiceChoice(Enumerated): enumerations = { # Alarm and Event Services 'acknowledgeAlarm':0, 'confirmedCOVNotification':1, 'confirmedEventNotification':2, 'getAlarmSummary':3, 'getEnrollmentSummary':4, 'getEventInformation':29, 'subscribeCOV':5, 'subscribeCOVProperty':28, 'lifeSafetyOperation':27, # File Access Services 'atomicReadFile':6, 'atomicWriteFile':7, # Object Access Services 'addListElement':8, 'removeListElement':9, 'createObject':10, 'deleteObject':11, 'readProperty':12, 'readPropertyMultiple':14, 'readRange':26, 'writeProperty':15, 'writePropertyMultiple':16, # Remote Device Management Services 'deviceCommunicationControl':17, 'confirmedPrivateTransfer':18, 'confirmedTextMessage':19, 'reinitializeDevice':20, # Virtual Terminal Services 'vtOpen':21, 'vtClose':22, 'vtData':23, } expand_enumerations(ConfirmedServiceChoice) class UnconfirmedServiceChoice(Enumerated): enumerations = { 'iAm':0, 'iHave':1, 'unconfirmedCOVNotification':2, 'unconfirmedEventNotification':3, 'unconfirmedPrivateTransfer':4, 'unconfirmedTextMessage':5, 'timeSynchronization':6, 'whoHas':7, 'whoIs':8, 'utcTimeSynchronization':9, 'writeGroup':10, } expand_enumerations(UnconfirmedServiceChoice) PK0GG IlPlPbacpypes/bvll.py#!/usr/bin/python """ BACnet Virtual Link Layer Module """ from .errors import EncodingError, DecodingError from .debugging import ModuleLogger, DebugContents, bacpypes_debugging from .pdu import Address, PCI, PDUData, unpack_ip_addr # some debugging _debug = 0 _log = ModuleLogger(globals()) # a dictionary of message type values and classes bvl_pdu_types = {} def register_bvlpdu_type(klass): bvl_pdu_types[klass.messageType] = klass # # BVLCI # @bacpypes_debugging class BVLCI(PCI, DebugContents): _debug_contents = ('bvlciType', 'bvlciFunction', 'bvlciLength') result = 0x00 writeBroadcastDistributionTable = 0x01 readBroadcastDistributionTable = 0x02 readBroadcastDistributionTableAck = 0x03 forwardedNPDU = 0x04 registerForeignDevice = 0x05 readForeignDeviceTable = 0x06 readForeignDeviceTableAck = 0x07 deleteForeignDeviceTableEntry = 0x08 distributeBroadcastToNetwork = 0x09 originalUnicastNPDU = 0x0A originalBroadcastNPDU = 0x0B def __init__(self, *args, **kwargs): if _debug: BVLCI._debug("__init__ %r %r", args, kwargs) super(BVLCI, self).__init__(*args, **kwargs) self.bvlciType = 0x81 self.bvlciFunction = None self.bvlciLength = None def update(self, bvlci): PCI.update(self, bvlci) self.bvlciType = bvlci.bvlciType self.bvlciFunction = bvlci.bvlciFunction self.bvlciLength = bvlci.bvlciLength def encode(self, pdu): """encode the contents of the BVLCI into the PDU.""" if _debug: BVLCI._debug("encode %s", str(pdu)) # copy the basics PCI.update(pdu, self) pdu.put( self.bvlciType ) # 0x81 pdu.put( self.bvlciFunction ) if (self.bvlciLength != len(self.pduData) + 4): raise EncodingError("invalid BVLCI length") pdu.put_short( self.bvlciLength ) def decode(self, pdu): """decode the contents of the PDU into the BVLCI.""" if _debug: BVLCI._debug("decode %s", str(pdu)) # copy the basics PCI.update(self, pdu) self.bvlciType = pdu.get() if self.bvlciType != 0x81: raise DecodingError("invalid BVLCI type") self.bvlciFunction = pdu.get() self.bvlciLength = pdu.get_short() if (self.bvlciLength != len(pdu.pduData) + 4): raise DecodingError("invalid BVLCI length") def bvlci_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" if _debug: BVLCI._debug("bvlci_contents use_dict=%r as_class=%r", use_dict, as_class) # make/extend the dictionary of content if use_dict is None: use_dict = as_class() # save the mapped value use_dict.__setitem__('type', self.bvlciType) use_dict.__setitem__('function', self.bvlciFunction) use_dict.__setitem__('length', self.bvlciLength) # return what we built/updated return use_dict # # BVLPDU # @bacpypes_debugging class BVLPDU(BVLCI, PDUData): def __init__(self, *args, **kwargs): if _debug: BVLPDU._debug("__init__ %r %r", args, kwargs) super(BVLPDU, self).__init__(*args, **kwargs) def encode(self, pdu): BVLCI.encode(self, pdu) pdu.put_data(self.pduData) def decode(self, pdu): BVLCI.decode(self, pdu) self.pduData = pdu.get_data(len(pdu.pduData)) def bvlpdu_contents(self, use_dict=None, as_class=dict): return PDUData.pdudata_contents(self, use_dict=use_dict, as_class=as_class) def dict_contents(self, use_dict=None, as_class=dict, key_values=()): """Return the contents of an object as a dict.""" if _debug: BVLPDU._debug("dict_contents use_dict=%r as_class=%r key_values=%r", use_dict, as_class, key_values) # make/extend the dictionary of content if use_dict is None: use_dict = as_class() # call the superclasses self.bvlci_contents(use_dict=use_dict, as_class=as_class) self.bvlpdu_contents(use_dict=use_dict, as_class=as_class) # return what we built/updated return use_dict # # key_value_contents # @bacpypes_debugging def key_value_contents(use_dict=None, as_class=dict, key_values=()): """Return the contents of an object as a dict.""" if _debug: key_value_contents._debug("key_value_contents use_dict=%r as_class=%r key_values=%r", use_dict, as_class, key_values) # make/extend the dictionary of content if use_dict is None: use_dict = as_class() # loop through the values and save them for k, v in key_values: if v is not None: if hasattr(v, 'dict_contents'): v = v.dict_contents(as_class=as_class) use_dict.__setitem__(k, v) # return what we built/updated return use_dict #------------------------------ # # Result # class Result(BVLPDU): _debug_contents = ('bvlciResultCode',) messageType = BVLCI.result def __init__(self, code=None, *args, **kwargs): super(Result, self).__init__(*args, **kwargs) self.bvlciFunction = BVLCI.result self.bvlciLength = 6 self.bvlciResultCode = code def encode(self, bvlpdu): BVLCI.update(bvlpdu, self) bvlpdu.put_short( self.bvlciResultCode ) def decode(self, bvlpdu): BVLCI.update(self, bvlpdu) self.bvlciResultCode = bvlpdu.get_short() def bvlpdu_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" return key_value_contents(use_dict=use_dict, as_class=as_class, key_values=( ('function', 'Result'), ('result_code', self.bvlciResultCode), )) register_bvlpdu_type(Result) # # WriteBroadcastDistributionTable # class WriteBroadcastDistributionTable(BVLPDU): _debug_contents = ('bvlciBDT',) messageType = BVLCI.writeBroadcastDistributionTable def __init__(self, bdt=[], *args, **kwargs): super(WriteBroadcastDistributionTable, self).__init__(*args, **kwargs) self.bvlciFunction = BVLCI.writeBroadcastDistributionTable self.bvlciLength = 4 + 10 * len(bdt) self.bvlciBDT = bdt def encode(self, bvlpdu): BVLCI.update(bvlpdu, self) for bdte in self.bvlciBDT: bvlpdu.put_data( bdte.addrAddr ) bvlpdu.put_data( bdte.addrMask ) def decode(self, bvlpdu): BVLCI.update(self, bvlpdu) self.bvlciBDT = [] while bvlpdu.pduData: bdte = Address(unpack_ip_addr(bvlpdu.get_data(6))) bdte.addrMask = bvlpdu.get_long() self.bvlciBDT.append(bdte) def bvlpdu_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" broadcast_distribution_table = [] for bdte in self.bvlciBDT: broadcast_distribution_table.append(str(bdte)) return key_value_contents(use_dict=use_dict, as_class=as_class, key_values=( ('function', 'WriteBroadcastDistributionTable'), ('bdt', broadcast_distribution_table), )) register_bvlpdu_type(WriteBroadcastDistributionTable) # # ReadBroadcastDistributionTable # class ReadBroadcastDistributionTable(BVLPDU): messageType = BVLCI.readBroadcastDistributionTable def __init__(self, *args, **kwargs): super(ReadBroadcastDistributionTable, self).__init__(*args, **kwargs) self.bvlciFunction = BVLCI.readBroadcastDistributionTable self.bvlciLength = 4 def encode(self, bvlpdu): BVLCI.update(bvlpdu, self) def decode(self, bvlpdu): BVLCI.update(self, bvlpdu) def bvlpdu_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" return key_value_contents(use_dict=use_dict, as_class=as_class, key_values=( ('function', 'ReadBroadcastDistributionTable'), )) register_bvlpdu_type(ReadBroadcastDistributionTable) # # ReadBroadcastDistributionTableAck # class ReadBroadcastDistributionTableAck(BVLPDU): _debug_contents = ('bvlciBDT',) messageType = BVLCI.readBroadcastDistributionTableAck def __init__(self, bdt=[], *args, **kwargs): super(ReadBroadcastDistributionTableAck, self).__init__(*args, **kwargs) self.bvlciFunction = BVLCI.readBroadcastDistributionTableAck self.bvlciLength = 4 + 10 * len(bdt) self.bvlciBDT = bdt def encode(self, bvlpdu): # make sure the length is correct self.bvlciLength = 4 + 10 * len(self.bvlciBDT) BVLCI.update(bvlpdu, self) # encode the table for bdte in self.bvlciBDT: bvlpdu.put_data( bdte.addrAddr ) bvlpdu.put_long( bdte.addrMask ) def decode(self, bvlpdu): BVLCI.update(self, bvlpdu) # decode the table self.bvlciBDT = [] while bvlpdu.pduData: bdte = Address(unpack_ip_addr(bvlpdu.get_data(6))) bdte.addrMask = bvlpdu.get_long() self.bvlciBDT.append(bdte) def bvlpdu_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" broadcast_distribution_table = [] for bdte in self.bvlciBDT: broadcast_distribution_table.append(str(bdte)) return key_value_contents(use_dict=use_dict, as_class=as_class, key_values=( ('function', 'ReadBroadcastDistributionTableAck'), ('bdt', broadcast_distribution_table), )) register_bvlpdu_type(ReadBroadcastDistributionTableAck) # # ForwardedNPDU # class ForwardedNPDU(BVLPDU): _debug_contents = ('bvlciAddress',) messageType = BVLCI.forwardedNPDU def __init__(self, addr=None, *args, **kwargs): super(ForwardedNPDU, self).__init__(*args, **kwargs) self.bvlciFunction = BVLCI.forwardedNPDU self.bvlciLength = 10 + len(self.pduData) self.bvlciAddress = addr def encode(self, bvlpdu): # make sure the length is correct self.bvlciLength = 10 + len(self.pduData) BVLCI.update(bvlpdu, self) # encode the address bvlpdu.put_data( self.bvlciAddress.addrAddr ) # encode the rest of the data bvlpdu.put_data( self.pduData ) def decode(self, bvlpdu): BVLCI.update(self, bvlpdu) # get the address self.bvlciAddress = Address(unpack_ip_addr(bvlpdu.get_data(6))) # get the rest of the data self.pduData = bvlpdu.get_data(len(bvlpdu.pduData)) def bvlpdu_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" # make/extend the dictionary of content if use_dict is None: use_dict = as_class() # call the normal procedure key_value_contents(use_dict=use_dict, as_class=as_class, key_values=( ('function', 'ForwardedNPDU'), ('address', str(self.bvlciAddress)), )) # this message has data PDUData.dict_contents(self, use_dict=use_dict, as_class=as_class) # return what we built/updated return use_dict register_bvlpdu_type(ForwardedNPDU) # # Foreign Device Table Entry # class FDTEntry(DebugContents): _debug_contents = ('fdAddress', 'fdTTL', 'fdRemain') def __init__(self): self.fdAddress = None self.fdTTL = None self.fdRemain = None def bvlpdu_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" # make/extend the dictionary of content if use_dict is None: use_dict = as_class() # save the content use_dict.__setitem__('address', str(self.fdAddress)) use_dict.__setitem__('ttl', self.fdTTL) use_dict.__setitem__('remaining', self.fdRemain) # return what we built/updated return use_dict # # RegisterForeignDevice # class RegisterForeignDevice(BVLPDU): _debug_contents = ('bvlciTimeToLive',) messageType = BVLCI.registerForeignDevice def __init__(self, ttl=None, *args, **kwargs): super(RegisterForeignDevice, self).__init__(*args, **kwargs) self.bvlciFunction = BVLCI.registerForeignDevice self.bvlciLength = 6 self.bvlciTimeToLive = ttl def encode(self, bvlpdu): BVLCI.update(bvlpdu, self) bvlpdu.put_short( self.bvlciTimeToLive ) def decode(self, bvlpdu): BVLCI.update(self, bvlpdu) self.bvlciTimeToLive = bvlpdu.get_short() def bvlpdu_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" return key_value_contents(use_dict=use_dict, as_class=as_class, key_values=( ('function', 'RegisterForeignDevice'), ('ttl', self.bvlciTimeToLive), )) register_bvlpdu_type(RegisterForeignDevice) # # ReadForeignDeviceTable # class ReadForeignDeviceTable(BVLPDU): messageType = BVLCI.readForeignDeviceTable def __init__(self, ttl=None, *args, **kwargs): super(ReadForeignDeviceTable, self).__init__(*args, **kwargs) self.bvlciFunction = BVLCI.readForeignDeviceTable self.bvlciLength = 4 def encode(self, bvlpdu): BVLCI.update(bvlpdu, self) def decode(self, bvlpdu): BVLCI.update(self, bvlpdu) def bvlpdu_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" return key_value_contents(use_dict=use_dict, as_class=as_class, key_values=( ('function', 'ReadForeignDeviceTable'), )) register_bvlpdu_type(ReadForeignDeviceTable) # # ReadForeignDeviceTableAck # class ReadForeignDeviceTableAck(BVLPDU): _debug_contents = ('bvlciFDT',) messageType = BVLCI.readForeignDeviceTableAck def __init__(self, fdt=[], *args, **kwargs): super(ReadForeignDeviceTableAck, self).__init__(*args, **kwargs) self.bvlciFunction = BVLCI.readForeignDeviceTableAck self.bvlciLength = 4 + 10 * len(fdt) self.bvlciFDT = fdt def encode(self, bvlpdu): BVLCI.update(bvlpdu, self) for fdte in self.bvlciFDT: bvlpdu.put_data( fdte.fdAddress.addrAddr ) bvlpdu.put_short( fdte.fdTTL ) bvlpdu.put_short( fdte.fdRemain ) def decode(self, bvlpdu): BVLCI.update(self, bvlpdu) self.bvlciFDT = [] while bvlpdu.pduData: fdte = FDTEntry() fdte.fdAddress = Address(unpack_ip_addr(bvlpdu.get_data(6))) fdte.fdTTL = bvlpdu.get_short() fdte.fdRemain = bvlpdu.get_short() self.bvlciFDT.append(fdte) def bvlpdu_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" foreign_device_table = [] for fdte in self.bvlciFDT: foreign_device_table.append(fdte.dict_contents(as_class=as_class)) return key_value_contents(use_dict=use_dict, as_class=as_class, key_values=( ('function', 'ReadForeignDeviceTableAck'), ('foreign_device_table', foreign_device_table), )) register_bvlpdu_type(ReadForeignDeviceTableAck) # # DeleteForeignDeviceTableEntry # class DeleteForeignDeviceTableEntry(BVLPDU): _debug_contents = ('bvlciAddress',) messageType = BVLCI.deleteForeignDeviceTableEntry def __init__(self, addr=None, *args, **kwargs): super(DeleteForeignDeviceTableEntry, self).__init__(*args, **kwargs) self.bvlciFunction = BVLCI.deleteForeignDeviceTableEntry self.bvlciLength = 10 self.bvlciAddress = addr def encode(self, bvlpdu): BVLCI.update(bvlpdu, self) bvlpdu.put_data( self.bvlciAddress.addrAddr ) def decode(self, bvlpdu): BVLCI.update(self, bvlpdu) self.bvlciAddress = Address(unpack_ip_addr(bvlpdu.get_data(6))) def bvlpdu_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" return key_value_contents(use_dict=use_dict, as_class=as_class, key_values=( ('function', 'DeleteForeignDeviceTableEntry'), ('address', str(self.bvlciAddress)), )) register_bvlpdu_type(DeleteForeignDeviceTableEntry) # # DistributeBroadcastToNetwork # class DistributeBroadcastToNetwork(BVLPDU): messageType = BVLCI.distributeBroadcastToNetwork def __init__(self, *args, **kwargs): super(DistributeBroadcastToNetwork, self).__init__(*args, **kwargs) self.bvlciFunction = BVLCI.distributeBroadcastToNetwork self.bvlciLength = 4 + len(self.pduData) def encode(self, bvlpdu): self.bvlciLength = 4 + len(self.pduData) BVLCI.update(bvlpdu, self) bvlpdu.put_data( self.pduData ) def decode(self, bvlpdu): BVLCI.update(self, bvlpdu) self.pduData = bvlpdu.get_data(len(bvlpdu.pduData)) def bvlpdu_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" # make/extend the dictionary of content if use_dict is None: use_dict = as_class() # call the normal procedure key_value_contents(use_dict=use_dict, as_class=as_class, key_values=( ('function', 'DistributeBroadcastToNetwork'), )) # this message has data PDUData.dict_contents(self, use_dict=use_dict, as_class=as_class) # return what we built/updated return use_dict register_bvlpdu_type(DistributeBroadcastToNetwork) # # OriginalUnicastNPDU # class OriginalUnicastNPDU(BVLPDU): messageType = BVLCI.originalUnicastNPDU def __init__(self, *args, **kwargs): super(OriginalUnicastNPDU, self).__init__(*args, **kwargs) self.bvlciFunction = BVLCI.originalUnicastNPDU self.bvlciLength = 4 + len(self.pduData) def encode(self, bvlpdu): self.bvlciLength = 4 + len(self.pduData) BVLCI.update(bvlpdu, self) bvlpdu.put_data( self.pduData ) def decode(self, bvlpdu): BVLCI.update(self, bvlpdu) self.pduData = bvlpdu.get_data(len(bvlpdu.pduData)) def bvlpdu_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" # make/extend the dictionary of content if use_dict is None: use_dict = as_class() # call the normal procedure key_value_contents(use_dict=use_dict, as_class=as_class, key_values=( ('function', 'OriginalUnicastNPDU'), )) # this message has data PDUData.dict_contents(self, use_dict=use_dict, as_class=as_class) # return what we built/updated return use_dict register_bvlpdu_type(OriginalUnicastNPDU) # # OriginalBroadcastNPDU # class OriginalBroadcastNPDU(BVLPDU): messageType = BVLCI.originalBroadcastNPDU def __init__(self, *args, **kwargs): super(OriginalBroadcastNPDU, self).__init__(*args, **kwargs) self.bvlciFunction = BVLCI.originalBroadcastNPDU self.bvlciLength = 4 + len(self.pduData) def encode(self, bvlpdu): self.bvlciLength = 4 + len(self.pduData) BVLCI.update(bvlpdu, self) bvlpdu.put_data( self.pduData ) def decode(self, bvlpdu): BVLCI.update(self, bvlpdu) self.pduData = bvlpdu.get_data(len(bvlpdu.pduData)) def bvlpdu_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" # make/extend the dictionary of content if use_dict is None: use_dict = as_class() # call the normal procedure key_value_contents(use_dict=use_dict, as_class=as_class, key_values=( ('function', 'OriginalBroadcastNPDU'), )) # this message has data PDUData.dict_contents(self, use_dict=use_dict, as_class=as_class) # return what we built/updated return use_dict register_bvlpdu_type(OriginalBroadcastNPDU) PKG5Hnqqbacpypes/singleton.py#!/usr/bin/python """ Singleton This module defines a "super singleton" class that verifies only once instance is actually created. It allows the class designating itself as a singleton to be inherited, still retains its singletoness, but makes sure that derived classes aren't created first. Test classes A -> B -> C where A is a singleton. B can be created before A, and calls for A will return the instance of B. But if B is created, C cannot be created, since a new C would imply a new instance of B. """ from . import debugging # # _SingletonMetaclass # class _SingletonMetaclass(type): def __init__(cls, *args): # no instance created yet cls._singleton_instance = None # save the current initializer old_cls_init = cls.__init__ # create an initialization trap def __init_trap__(self, *args, **kwargs): # see if one of these has been created already if cls._singleton_instance: raise RuntimeError("instance of " + cls.__name__ + " has already been created") # initialize as usual old_cls_init(self, *args, **kwargs) # save this as a created instance cls._singleton_instance = self # set the trap cls.__init__ = __init_trap__ # continue initializing the class super(_SingletonMetaclass, cls).__init__(*args) def __call__(cls, *args, **kwargs): if cls._singleton_instance is None: cls._singleton_instance = super(_SingletonMetaclass, cls).__call__(*args, **kwargs) return cls._singleton_instance # # Singleton # class Singleton(metaclass=_SingletonMetaclass): pass # # _SingletonLoggingMetaclass # class _SingletonLoggingMetaclass(_SingletonMetaclass, debugging._LoggingMetaclass): pass class SingletonLogging(metaclass=_SingletonLoggingMetaclass): pass PK*UHݾ \\bacpypes/tcp.py#!/usr/bin/python """ TCP Communications Module """ import asyncore import socket import pickle from time import time as _time, sleep as _sleep from io import StringIO from .debugging import ModuleLogger, DebugContents, bacpypes_debugging from .core import deferred from .task import FunctionTask, OneShotFunction from .comm import PDU, Client, Server from .comm import ServiceAccessPoint, ApplicationServiceElement # some debugging _debug = 0 _log = ModuleLogger(globals()) # globals REBIND_SLEEP_INTERVAL = 2.0 # # PickleActorMixIn # @bacpypes_debugging class PickleActorMixIn: def __init__(self, *args): if _debug: PickleActorMixIn._debug("__init__ %r", args) super(PickleActorMixIn, self).__init__(*args) # keep an upstream buffer self.pickleBuffer = '' def indication(self, pdu): if _debug: PickleActorMixIn._debug("indication %r", pdu) # pickle the data pdu.pduData = pickle.dumps(pdu.pduData) # continue as usual super(PickleActorMixIn, self).indication(pdu) def response(self, pdu): if _debug: PickleActorMixIn._debug("response %r", pdu) # add the data to our buffer self.pickleBuffer += pdu.pduData # build a file-like object around the buffer strm = StringIO(self.pickleBuffer) pos = 0 while (pos < strm.len): try: # try to load something msg = pickle.load(strm) except: break # got a message rpdu = PDU(msg) rpdu.update(pdu) super(PickleActorMixIn, self).response(rpdu) # see where we are pos = strm.tell() # save anything left over, if there is any if (pos < strm.len): self.pickleBuffer = self.pickleBuffer[pos:] else: self.pickleBuffer = '' # # TCPClient # # This class is a mapping between the client/server pattern and the # socket API. The ctor is given the address to connect as a TCP # client. Because objects of this class sit at the bottom of a # protocol stack they are accessed as servers. # @bacpypes_debugging class TCPClient(asyncore.dispatcher): def __init__(self, peer): if _debug: TCPClient._debug("__init__ %r", peer) asyncore.dispatcher.__init__(self) # ask the dispatcher for a socket self.create_socket(socket.AF_INET, socket.SOCK_STREAM) # save the peer self.peer = peer # create a request buffer self.request = '' # hold the socket error if there was one self.socketError = None # try to connect the socket if _debug: TCPClient._debug(" - try to connect") self.connect(peer) if _debug: TCPClient._debug(" - connected (maybe)") def handle_connect(self): if _debug: deferred(TCPClient._debug, "handle_connect") def handle_expt(self): pass def readable(self): return 1 def handle_read(self): if _debug: deferred(TCPClient._debug, "handle_read") try: msg = self.recv(65536) if _debug: deferred(TCPClient._debug, " - received %d octets", len(msg)) self.socketError = None # no socket means it was closed if not self.socket: if _debug: deferred(TCPClient._debug, " - socket was closed") else: # sent the data upstream deferred(self.response, PDU(msg)) except socket.error as err: if (err.args[0] == 111): deferred(TCPClient._error, "connection to %r refused", self.peer) else: deferred(TCPClient._error, "TCPClient.handle_read socket error: %r", err) self.socketError = err def writable(self): return (len(self.request) != 0) def handle_write(self): if _debug: deferred(TCPClient._debug, "handle_write") try: sent = self.send(self.request) if _debug: deferred(TCPClient._debug, " - sent %d octets, %d remaining", sent, len(self.request) - sent) self.socketError = None self.request = self.request[sent:] except socket.error as err: if (err.args[0] == 111): deferred(TCPClient._error, "connection to %r refused", self.peer) else: deferred(TCPClient._error, "handle_write socket error: %s", err) self.socketError = err def handle_close(self): if _debug: deferred(TCPClient._debug, "handle_close") # close the socket self.close() # make sure other routines know the socket is closed self.socket = None def indication(self, pdu): """Requests are queued for delivery.""" if _debug: TCPClient._debug("indication %r", pdu) self.request += pdu.pduData # # TCPClientActor # # Actors are helper objects for a director. There is one actor for # each connection. # @bacpypes_debugging class TCPClientActor(TCPClient): def __init__(self, director, peer): if _debug: TCPClientActor._debug("__init__ %r %r", director, peer) TCPClient.__init__(self, peer) # keep track of the director self.director = director # add a timer self.timeout = director.timeout if self.timeout > 0: self.timer = FunctionTask(self.idle_timeout) self.timer.install_task(_time() + self.timeout) else: self.timer = None # this may have a flush state self.flushTask = None # tell the director this is a new actor self.director.add_actor(self) def handle_close(self): if _debug: TCPClientActor._debug("handle_close") # if there's a flush task, cancel it if self.flushTask: self.flushTask.suspend_task() # cancel the timer if self.timer: self.timer.suspend_task() # tell the director this is gone self.director.remove_actor(self) # pass the function along TCPClient.handle_close(self) def idle_timeout(self): if _debug: TCPClientActor._debug("idle_timeout") # shut it down self.handle_close() def indication(self, pdu): if _debug: TCPClientActor._debug("indication %r", pdu) # additional downstream data is tossed while flushing if self.flushTask: if _debug: TCPServerActor._debug(" - flushing") return # reschedule the timer if self.timer: self.timer.install_task(_time() + self.timeout) # continue as usual TCPClient.indication(self, pdu) def response(self, pdu): if _debug: TCPClientActor._debug("response %r", pdu) # put the peer address in as the source pdu.pduSource = self.peer # reschedule the timer if self.timer: self.timer.install_task(_time() + self.timeout) # process this as a response from the director self.director.response(pdu) def flush(self): if _debug: TCPClientActor._debug("flush") # clear out the old task self.flushTask = None # if the outgoing buffer has data, re-schedule another attempt if self.request: self.flushTask = OneShotFunction(self.flush) return # close up shop, all done self.handle_close() # # TCPPickleClientActor # class TCPPickleClientActor(PickleActorMixIn, TCPClientActor): pass # # TCPClientDirector # # A client director presents a connection pool as one virtual # interface. If a request should be sent to an address and there # is no connection already established for it, it will create one # and maintain it. PDU's from TCP clients have no source address, # so one is provided by the client actor. # @bacpypes_debugging class TCPClientDirector(Server, ServiceAccessPoint, DebugContents): _debug_contents = ('timeout', 'actorClass', 'clients', 'reconnect') def __init__(self, timeout=0, actorClass=TCPClientActor, sid=None, sapID=None): if _debug: TCPClientDirector._debug("__init__ timeout=%r actorClass=%r sid=%r sapID=%r", timeout, actorClass, sid, sapID) Server.__init__(self, sid) ServiceAccessPoint.__init__(self, sapID) # check the actor class if not issubclass(actorClass, TCPClientActor): raise TypeError("actorClass must be a subclass of TCPClientActor") self.actorClass = actorClass # save the timeout for actors self.timeout = timeout # start with an empty client pool self.clients = {} # no clients automatically reconnecting self.reconnect = {} def add_actor(self, actor): """Add an actor when a new one is connected.""" if _debug: TCPClientDirector._debug("add_actor %r", actor) self.clients[actor.peer] = actor # tell the ASE there is a new client if self.serviceElement: self.sap_request(addPeer=actor.peer) def remove_actor(self, actor): """Remove an actor when the socket is closed.""" if _debug: TCPClientDirector._debug("remove_actor %r", actor) del self.clients[actor.peer] # tell the ASE the client has gone away if self.serviceElement: self.sap_request(delPeer=actor.peer) # see if it should be reconnected if actor.peer in self.reconnect: connect_task = FunctionTask(self.connect, actor.peer) connect_task.install_task(_time() + self.reconnect[actor.peer]) def get_actor(self, address): """ Get the actor associated with an address or None. """ return self.clients.get(address, None) def connect(self, address, reconnect=0): if _debug: TCPClientDirector._debug("connect %r reconnect=%r", address, reconnect) if address in self.clients: return # create an actor, which will eventually call add_actor client = self.actorClass(self, address) if _debug: TCPClientDirector._debug(" - client: %r", client) # if it should automatically reconnect, save the timer value if reconnect: self.reconnect[address] = reconnect def disconnect(self, address): if _debug: TCPClientDirector._debug("disconnect %r", address) if address not in self.clients: return # if it would normally reconnect, don't bother if address in self.reconnect: del self.reconnect[address] # close it self.clients[address].handle_close() def indication(self, pdu): """Direct this PDU to the appropriate server, create a connection if one hasn't already been created.""" if _debug: TCPClientDirector._debug("indication %r", pdu) # get the destination addr = pdu.pduDestination # get the client client = self.clients.get(addr, None) if not client: client = self.actorClass(self, addr) # send the message client.indication(pdu) # # TCPServer # @bacpypes_debugging class TCPServer(asyncore.dispatcher): def __init__(self, sock, peer): if _debug: TCPServer._debug("__init__ %r %r", sock, peer) asyncore.dispatcher.__init__(self, sock) # save the peer self.peer = peer # create a request buffer self.request = '' # hold the socket error if there was one self.socketError = None def handle_connect(self): if _debug: deferred(TCPServer._debug, "handle_connect") def readable(self): return 1 def handle_read(self): if _debug: deferred(TCPServer._debug, "handle_read") try: msg = self.recv(65536) if _debug: deferred(TCPServer._debug, " - received %d octets", len(msg)) self.socketError = None # no socket means it was closed if not self.socket: if _debug: deferred(TCPServer._debug, " - socket was closed") else: deferred(self.response, PDU(msg)) except socket.error as err: if (err.args[0] == 111): deferred(TCPServer._error, "connection to %r refused", self.peer) else: deferred(TCPServer._error, "handle_read socket error: %s", err) self.socketError = err def writable(self): return (len(self.request) != 0) def handle_write(self): if _debug: deferred(TCPServer._debug, "handle_write") try: sent = self.send(self.request) if _debug: deferred(TCPServer._debug, " - sent %d octets, %d remaining", sent, len(self.request) - sent) self.socketError = None self.request = self.request[sent:] except socket.error as why: if (why.args[0] == 111): deferred(TCPServer._error, "connection to %r refused", self.peer) else: deferred(TCPServer._error, "handle_write socket error: %s", why) self.socketError = why def handle_close(self): if _debug: deferred(TCPServer._debug, "handle_close") if not self: deferred(TCPServer._warning, "handle_close: self is None") return if not self.socket: deferred(TCPServer._warning, "handle_close: socket already closed") return self.close() self.socket = None def indication(self, pdu): """Requests are queued for delivery.""" if _debug: TCPServer._debug("indication %r", pdu) self.request += pdu.pduData # # TCPServerActor # @bacpypes_debugging class TCPServerActor(TCPServer): def __init__(self, director, sock, peer): if _debug: TCPServerActor._debug("__init__ %r %r %r", director, sock, peer) TCPServer.__init__(self, sock, peer) # keep track of the director self.director = director # add a timer self.timeout = director.timeout if self.timeout > 0: self.timer = FunctionTask(self.idle_timeout) self.timer.install_task(_time() + self.timeout) else: self.timer = None # this may have a flush state self.flushTask = None # tell the director this is a new actor self.director.add_actor(self) def handle_close(self): if _debug: TCPServerActor._debug("handle_close") # if there's a flush task, cancel it if self.flushTask: self.flushTask.suspend_task() # tell the director this is gone self.director.remove_actor(self) # pass it down TCPServer.handle_close(self) def idle_timeout(self): if _debug: TCPServerActor._debug("idle_timeout") # shut it down self.handle_close() def indication(self, pdu): if _debug: TCPServerActor._debug("indication %r", pdu) # additional downstream data is tossed while flushing if self.flushTask: if _debug: TCPServerActor._debug(" - flushing") return # reschedule the timer if self.timer: self.timer.install_task(_time() + self.timeout) # continue as usual TCPServer.indication(self, pdu) def response(self, pdu): if _debug: TCPServerActor._debug("response %r", pdu) # upstream data is tossed while flushing if self.flushTask: if _debug: TCPServerActor._debug(" - flushing") return # save the source pdu.pduSource = self.peer # reschedule the timer if self.timer: self.timer.install_task(_time() + self.timeout) # process this as a response from the director self.director.response(pdu) def flush(self): if _debug: TCPServerActor._debug("flush") # clear out the old task self.flushTask = None # if the outgoing buffer has data, re-schedule another attempt if self.request: self.flushTask = OneShotFunction(self.flush) return # close up shop, all done self.handle_close() # # TCPPickleServerActor # class TCPPickleServerActor(PickleActorMixIn, TCPServerActor): pass # # TCPServerDirector # @bacpypes_debugging class TCPServerDirector(asyncore.dispatcher, Server, ServiceAccessPoint, DebugContents): _debug_contents = ('port', 'timeout', 'actorClass', 'servers') def __init__(self, address, listeners=5, timeout=0, reuse=False, actorClass=TCPServerActor, cid=None, sapID=None): if _debug: TCPServerDirector._debug("__init__ %r listeners=%r timeout=%r reuse=%r actorClass=%r cid=%r sapID=%r" , address, listeners, timeout, reuse, actorClass, cid, sapID ) Server.__init__(self, cid) ServiceAccessPoint.__init__(self, sapID) # save the address and timeout self.port = address self.timeout = timeout # check the actor class if not issubclass(actorClass, TCPServerActor): raise TypeError("actorClass must be a subclass of TCPServerActor") self.actorClass = actorClass # start with an empty pool of servers self.servers = {} # continue with initialization asyncore.dispatcher.__init__(self) # create a listening port self.create_socket(socket.AF_INET, socket.SOCK_STREAM) if reuse: self.set_reuse_addr() # try to bind, keep trying for a while if its already in use hadBindErrors = False for i in range(30): try: self.bind(address) break except socket.error as err: hadBindErrors = True TCPServerDirector._warning('bind error %r, sleep and try again', err) _sleep(REBIND_SLEEP_INTERVAL) else: TCPServerDirector._error('unable to bind') raise RuntimeError("unable to bind") # if there were some bind errors, generate a meesage that all is OK now if hadBindErrors: TCPServerDirector._info('bind successful') self.listen(listeners) def handle_accept(self): if _debug: TCPServerDirector._debug("handle_accept") try: client, addr = self.accept() except socket.error: TCPServerDirector._warning('accept() threw an exception') return except TypeError: TCPServerDirector._warning('accept() threw EWOULDBLOCK') return if _debug: TCPServerDirector._debug(" - connection %r, %r", client, addr) # create a server server = self.actorClass(self, client, addr) # add it to our pool self.servers[addr] = server # return it to the dispatcher return server def handle_close(self): if _debug: TCPServerDirector._debug("handle_close") # close the socket self.close() def add_actor(self, actor): if _debug: TCPServerDirector._debug("add_actor %r", actor) self.servers[actor.peer] = actor # tell the ASE there is a new server if self.serviceElement: self.sap_request(addPeer=actor.peer) def remove_actor(self, actor): if _debug: TCPServerDirector._debug("remove_actor %r", actor) try: del self.servers[actor.peer] except KeyError: TCPServerDirector._warning("remove_actor: %r not an actor", actor) # tell the ASE the server has gone away if self.serviceElement: self.sap_request(delPeer=actor.peer) def get_actor(self, address): """ Get the actor associated with an address or None. """ return self.servers.get(address, None) def indication(self, pdu): """Direct this PDU to the appropriate server.""" if _debug: TCPServerDirector._debug("indication %r", pdu) # get the destination addr = pdu.pduDestination # get the server server = self.servers.get(addr, None) if not server: raise RuntimeError("not a connected server") # pass the indication to the actor server.indication(pdu) # # StreamToPacket # @bacpypes_debugging class StreamToPacket(Client, Server): def __init__(self, fn, cid=None, sid=None): if _debug: StreamToPacket._debug("__init__ %r cid=%r, sid=%r", fn, cid, sid) Client.__init__(self, cid) Server.__init__(self, sid) # save the packet function self.packetFn = fn # start with an empty set of buffers self.upstreamBuffer = {} self.downstreamBuffer = {} def packetize(self, pdu, streamBuffer): if _debug: StreamToPacket._debug("packetize %r ...", pdu) def chop(addr): if _debug: StreamToPacket._debug("chop %r", addr) # get the current downstream buffer buff = streamBuffer.get(addr, b'') + pdu.pduData if _debug: StreamToPacket._debug(" - buff: %r", buff) # look for a packet while 1: packet = self.packetFn(buff) if packet is None: break yield PDU(packet[0], source=pdu.pduSource, destination=pdu.pduDestination, user_data=pdu.pduUserData, ) buff = packet[1] # save what didn't get sent streamBuffer[addr] = buff # buffer related to the addresses if pdu.pduSource: for pdu in chop(pdu.pduSource): yield pdu if pdu.pduDestination: for pdu in chop(pdu.pduDestination): yield pdu def indication(self, pdu): """Message going downstream.""" if _debug: StreamToPacket._debug("indication %r", pdu) # hack it up into chunks for packet in self.packetize(pdu, self.downstreamBuffer): self.request(packet) def confirmation(self, pdu): """Message going upstream.""" if _debug: StreamToPacket._debug("StreamToPacket.confirmation %r", pdu) # hack it up into chunks for packet in self.packetize(pdu, self.upstreamBuffer): self.response(packet) # # StreamToPacketSAP # @bacpypes_debugging class StreamToPacketSAP(ApplicationServiceElement, ServiceAccessPoint): def __init__(self, stp, aseID=None, sapID=None): if _debug: StreamToPacketSAP._debug("__init__ %r aseID=%r, sapID=%r", stp, aseID, sapID) ApplicationServiceElement.__init__(self, aseID) ServiceAccessPoint.__init__(self, sapID) # save a reference to the StreamToPacket object self.stp = stp def indication(self, addPeer=None, delPeer=None): if _debug: StreamToPacketSAP._debug("indication addPeer=%r delPeer=%r", addPeer, delPeer) if addPeer: # create empty buffers associated with the peer self.stp.upstreamBuffer[addPeer] = b'' self.stp.downstreamBuffer[addPeer] = b'' if delPeer: # delete the buffer contents associated with the peer del self.stp.upstreamBuffer[delPeer] del self.stp.downstreamBuffer[delPeer] # chain this along if self.serviceElement: self.sap_request(addPeer=addPeer, delPeer=delPeer) PKH`"66bacpypes/primitivedata.py#!/usr/bin/python """ Primitive Data """ import sys import struct import time import re from .debugging import ModuleLogger, btox from .errors import DecodingError, InvalidTag from .pdu import PDUData # some debugging _debug = 0 _log = ModuleLogger(globals()) # # Tag # class Tag(object): applicationTagClass = 0 contextTagClass = 1 openingTagClass = 2 closingTagClass = 3 nullAppTag = 0 booleanAppTag = 1 unsignedAppTag = 2 integerAppTag = 3 realAppTag = 4 doubleAppTag = 5 octetStringAppTag = 6 characterStringAppTag = 7 bitStringAppTag = 8 enumeratedAppTag = 9 dateAppTag = 10 timeAppTag = 11 objectIdentifierAppTag = 12 reservedAppTag13 = 13 reservedAppTag14 = 14 reservedAppTag15 = 15 _app_tag_name = \ [ 'null', 'boolean', 'unsigned', 'integer' , 'real', 'double', 'octetString', 'characterString' , 'bitString', 'enumerated', 'date', 'time' , 'objectIdentifier', 'reserved13', 'reserved14', 'reserved15' ] _app_tag_class = [] # defined later def __init__(self, *args): self.tagClass = None self.tagNumber = None self.tagLVT = None self.tagData = None if args: if (len(args) == 1) and isinstance(args[0], PDUData): self.decode(args[0]) elif (len(args) >= 2): self.set(*args) else: raise ValueError("invalid Tag ctor arguments") def set(self, tclass, tnum, tlvt=0, tdata=b''): """set the values of the tag.""" if isinstance(tdata, bytearray): tdata = bytes(tdata) elif not isinstance(tdata, bytes): raise TypeError("tag data must be bytes or bytearray") self.tagClass = tclass self.tagNumber = tnum self.tagLVT = tlvt self.tagData = tdata def set_app_data(self, tnum, tdata): """set the values of the tag.""" if isinstance(tdata, bytearray): tdata = bytes(tdata) elif not isinstance(tdata, bytes): raise TypeError("tag data must be bytes or bytearray") self.tagClass = Tag.applicationTagClass self.tagNumber = tnum self.tagLVT = len(tdata) self.tagData = tdata def encode(self, pdu): # check for special encoding if (self.tagClass == Tag.contextTagClass): data = 0x08 elif (self.tagClass == Tag.openingTagClass): data = 0x0E elif (self.tagClass == Tag.closingTagClass): data = 0x0F else: data = 0x00 # encode the tag number part if (self.tagNumber < 15): data += (self.tagNumber << 4) else: data += 0xF0 # encode the length/value/type part if (self.tagLVT < 5): data += self.tagLVT else: data += 0x05 # save this and the extended tag value pdu.put( data ) if (self.tagNumber >= 15): pdu.put(self.tagNumber) # really short lengths are already done if (self.tagLVT >= 5): if (self.tagLVT <= 253): pdu.put( self.tagLVT ) elif (self.tagLVT <= 65535): pdu.put( 254 ) pdu.put_short( self.tagLVT ) else: pdu.put( 255 ) pdu.put_long( self.tagLVT ) # now put the data pdu.put_data(self.tagData) def decode(self, pdu): tag = pdu.get() # extract the type self.tagClass = (tag >> 3) & 0x01 # extract the tag number self.tagNumber = (tag >> 4) if (self.tagNumber == 0x0F): self.tagNumber = pdu.get() # extract the length self.tagLVT = tag & 0x07 if (self.tagLVT == 5): self.tagLVT = pdu.get() if (self.tagLVT == 254): self.tagLVT = pdu.get_short() elif (self.tagLVT == 255): self.tagLVT = pdu.get_long() elif (self.tagLVT == 6): self.tagClass = Tag.openingTagClass self.tagLVT = 0 elif (self.tagLVT == 7): self.tagClass = Tag.closingTagClass self.tagLVT = 0 # application tagged boolean has no more data if (self.tagClass == Tag.applicationTagClass) and (self.tagNumber == Tag.booleanAppTag): # tagLVT contains value self.tagData = b'' else: # tagLVT contains length self.tagData = pdu.get_data(self.tagLVT) def app_to_context(self, context): """Return a context encoded tag.""" if self.tagClass != Tag.applicationTagClass: raise ValueError("application tag required") # application tagged boolean now has data if (self.tagNumber == Tag.booleanAppTag): return ContextTag(context, bytearray([self.tagLVT])) else: return ContextTag(context, self.tagData) def context_to_app(self, dataType): """Return an application encoded tag.""" if self.tagClass != Tag.contextTagClass: raise ValueError("context tag required") # context booleans have value in data if (dataType == Tag.booleanAppTag): return Tag(Tag.applicationTagClass, Tag.booleanAppTag, struct.unpack('B', self.tagData)[0], b'') else: return ApplicationTag(dataType, self.tagData) def app_to_object(self): """Return the application object encoded by the tag.""" if self.tagClass != Tag.applicationTagClass: raise ValueError("application tag required") # get the class to build klass = self._app_tag_class[self.tagNumber] if not klass: return None # build an object, tell it to decode this tag, and return it return klass(self) def __repr__(self): sname = self.__module__ + '.' + self.__class__.__name__ try: if self.tagClass == Tag.openingTagClass: desc = "(open(%d))" % (self.tagNumber,) elif self.tagClass == Tag.closingTagClass: desc = "(close(%d))" % (self.tagNumber,) elif self.tagClass == Tag.contextTagClass: desc = "(context(%d))" % (self.tagNumber,) elif self.tagClass == Tag.applicationTagClass: desc = "(%s)" % (self._app_tag_name[self.tagNumber],) else: raise ValueError("invalid tag class") except: desc = "(?)" return '<' + sname + desc + ' instance at 0x%08x' % (id(self),) + '>' def __eq__(self, tag): return (self.tagClass == tag.tagClass) \ and (self.tagNumber == tag.tagNumber) \ and (self.tagLVT == tag.tagLVT) \ and (self.tagData == tag.tagData) def __ne__(self,arg): return not self.__eq__(arg) def debug_contents(self, indent=1, file=sys.stdout, _ids=None): # object reference first file.write("%s%r\n" % (" " * indent, self)) indent += 1 # tag class msg = "%stagClass = %s " % (" " * indent, self.tagClass) if self.tagClass == Tag.applicationTagClass: msg += 'application' elif self.tagClass == Tag.contextTagClass: msg += 'context' elif self.tagClass == Tag.openingTagClass: msg += 'opening' elif self.tagClass == Tag.closingTagClass: msg += 'closing' else: msg += "?" file.write(msg + "\n") # tag number msg = "%stagNumber = %d " % (" " * indent, self.tagNumber) if self.tagClass == Tag.applicationTagClass: try: msg += self._app_tag_name[self.tagNumber] except: msg += '?' file.write(msg + "\n") # length, value, type file.write("%stagLVT = %s\n" % (" " * indent, self.tagLVT)) # data file.write("%stagData = '%s'\n" % (" " * indent, btox(self.tagData,'.'))) # # ApplicationTag # class ApplicationTag(Tag): def __init__(self, *args): if len(args) == 1 and isinstance(args[0], PDUData): Tag.__init__(self, args[0]) if self.tagClass != Tag.applicationTagClass: raise DecodingError("application tag not decoded") elif len(args) == 2: tnum, tdata = args Tag.__init__(self, Tag.applicationTagClass, tnum, len(tdata), tdata) else: raise ValueError("ApplicationTag ctor requires a type and data or PDUData") # # ContextTag # class ContextTag(Tag): def __init__(self, *args): if len(args) == 1 and isinstance(args[0], PDUData): Tag.__init__(self, args[0]) if self.tagClass != Tag.contextTagClass: raise DecodingError("context tag not decoded") elif len(args) == 2: tnum, tdata = args Tag.__init__(self, Tag.contextTagClass, tnum, len(tdata), tdata) else: raise ValueError("ContextTag ctor requires a type and data or PDUData") # # OpeningTag # class OpeningTag(Tag): def __init__(self, context): if isinstance(context, PDUData): Tag.__init__(self, context) if self.tagClass != Tag.openingTagClass: raise DecodingError("opening tag not decoded") elif isinstance(context, int): Tag.__init__(self, Tag.openingTagClass, context) else: raise TypeError("OpeningTag ctor requires an integer or PDUData") # # ClosingTag # class ClosingTag(Tag): def __init__(self, context): if isinstance(context, PDUData): Tag.__init__(self, context) if self.tagClass != Tag.closingTagClass: raise DecodingError("closing tag not decoded") elif isinstance(context, int): Tag.__init__(self, Tag.closingTagClass, context) else: raise TypeError("ClosingTag ctor requires an integer or PDUData") # # TagList # class TagList(object): def __init__(self, arg=None): self.tagList = [] if isinstance(arg, list): self.tagList = arg elif isinstance(arg, TagList): self.tagList = arg.tagList[:] elif isinstance(arg, PDUData): self.decode(arg) def append(self, tag): self.tagList.append(tag) def extend(self, taglist): self.tagList.extend(taglist) def __getitem__(self, item): return self.tagList[item] def __len__(self): return len(self.tagList) def Peek(self): """Return the tag at the front of the list.""" if self.tagList: tag = self.tagList[0] else: tag = None return tag def push(self, tag): """Return a tag back to the front of the list.""" self.tagList = [tag] + self.tagList def Pop(self): """Remove the tag from the front of the list and return it.""" if self.tagList: tag = self.tagList[0] del self.tagList[0] else: tag = None return tag def get_context(self, context): """Return a tag or a list of tags context encoded.""" # forward pass i = 0 while i < len(self.tagList): tag = self.tagList[i] # skip application stuff if tag.tagClass == Tag.applicationTagClass: pass # check for context encoded atomic value elif tag.tagClass == Tag.contextTagClass: if tag.tagNumber == context: return tag # check for context encoded group elif tag.tagClass == Tag.openingTagClass: keeper = tag.tagNumber == context rslt = [] i += 1 lvl = 0 while i < len(self.tagList): tag = self.tagList[i] if tag.tagClass == Tag.openingTagClass: lvl += 1 elif tag.tagClass == Tag.closingTagClass: lvl -= 1 if lvl < 0: break rslt.append(tag) i += 1 # make sure everything balances if lvl >= 0: raise InvalidTag("mismatched open/close tags") # get everything we need? if keeper: return TagList(rslt) else: raise InvalidTag("unexpected tag") # try the next tag i += 1 # nothing found return None def encode(self, pdu): """encode the tag list into a PDU.""" for tag in self.tagList: tag.encode(pdu) def decode(self, pdu): """decode the tags from a PDU.""" while pdu.pduData: self.tagList.append( Tag(pdu) ) def debug_contents(self, indent=1, file=sys.stdout, _ids=None): for tag in self.tagList: tag.debug_contents(indent+1, file, _ids) # # Atomic # class Atomic(object): _app_tag = None def __cmp__(self, other): # sys.stderr.write("__cmp__ %r %r\n" % (self, other)) # hoop jump it if not isinstance(other, self.__class__): other = self.__class__(other) # now compare the values if (self.value < other.value): return -1 elif (self.value > other.value): return 1 else: return 0 def __lt__(self, other): # sys.stderr.write("__lt__ %r %r\n" % (self, other)) # hoop jump it if not isinstance(other, self.__class__): other = self.__class__(other) # now compare the values return (self.value < other.value) def __eq__(self, other): # sys.stderr.write("__eq__ %r %r\n" % (self, other)) # hoop jump it if not isinstance(other, self.__class__): other = self.__class__(other) # now compare the values return self.value == other.value # # Null # class Null(Atomic): _app_tag = Tag.nullAppTag def __init__(self, arg=None): self.value = () if arg is None: pass elif isinstance(arg, Tag): self.decode(arg) elif isinstance(arg, tuple): if len(arg) != 0: raise ValueError("empty tuple required") elif isinstance(arg, Null): pass else: raise TypeError("invalid constructor datatype") def encode(self, tag): tag.set_app_data(Tag.nullAppTag, b'') def decode(self, tag): if (tag.tagClass != Tag.applicationTagClass) or (tag.tagNumber != Tag.nullAppTag): raise InvalidTag("null application tag required") if len(tag.tagData) != 0: raise InvalidTag("invalid tag length") self.value = () def __str__(self): return "Null" # # Boolean # class Boolean(Atomic): _app_tag = Tag.booleanAppTag def __init__(self, arg=None): self.value = False if arg is None: pass elif isinstance(arg, Tag): self.decode(arg) elif isinstance(arg, bool): self.value = arg elif isinstance(arg, Boolean): self.value = arg.value elif str(arg) == 'True' or str(arg) == 'true': self.value = True elif str(arg) == 'False' or str(arg) == 'false': self.value = False else: raise TypeError("invalid constructor datatype") def encode(self, tag): tag.set(Tag.applicationTagClass, Tag.booleanAppTag, int(self.value), b'') def decode(self, tag): if (tag.tagClass != Tag.applicationTagClass) or (tag.tagNumber != Tag.booleanAppTag): raise InvalidTag("boolean application tag required") if (tag.tagLVT > 1): raise InvalidTag("invalid tag value") # get the data self.value = bool(tag.tagLVT) def __str__(self): return "Boolean(%s)" % (str(self.value), ) # # Unsigned # class Unsigned(Atomic): _app_tag = Tag.unsignedAppTag def __init__(self,arg = None): self.value = 0 if arg is None: pass elif isinstance(arg, Tag): self.decode(arg) elif isinstance(arg, int): if (arg < 0): raise ValueError("unsigned integer required") self.value = arg elif isinstance(arg, Unsigned): self.value = arg.value else: raise TypeError("invalid constructor datatype") def encode(self, tag): # rip apart the number data = bytearray(struct.pack('>L', self.value)) # reduce the value to the smallest number of octets while (len(data) > 1) and (data[0] == 0): del data[0] # encode the tag tag.set_app_data(Tag.unsignedAppTag, data) def decode(self, tag): if (tag.tagClass != Tag.applicationTagClass) or (tag.tagNumber != Tag.unsignedAppTag): raise InvalidTag("unsigned application tag required") if len(tag.tagData) == 0: raise InvalidTag("invalid tag length") # get the data rslt = 0 for c in tag.tagData: rslt = (rslt << 8) + c # save the result self.value = rslt def __str__(self): return "Unsigned(%s)" % (self.value, ) # # Integer # class Integer(Atomic): _app_tag = Tag.integerAppTag def __init__(self,arg = None): self.value = 0 if arg is None: pass elif isinstance(arg, Tag): self.decode(arg) elif isinstance(arg, int): self.value = arg elif isinstance(arg, Integer): self.value = arg.value else: raise TypeError("invalid constructor datatype") def encode(self, tag): # rip apart the number data = bytearray(struct.pack('>I', self.value & 0xFFFFFFFF)) # reduce the value to the smallest number of bytes, be # careful about sign extension if self.value < 0: while (len(data) > 1): if (data[0] != 255): break if (data[1] < 128): break del data[0] else: while (len(data) > 1): if (data[0] != 0): break if (data[1] >= 128): break del data[0] # encode the tag tag.set_app_data(Tag.integerAppTag, data) def decode(self, tag): if (tag.tagClass != Tag.applicationTagClass) or (tag.tagNumber != Tag.integerAppTag): raise InvalidTag("integer application tag required") if len(tag.tagData) == 0: raise InvalidTag("invalid tag length") # byte array easier to deal with tag_data = bytearray(tag.tagData) # get the data rslt = tag_data[0] if (rslt & 0x80) != 0: rslt = (-1 << 8) | rslt for c in tag_data[1:]: rslt = (rslt << 8) | c # save the result self.value = rslt def __str__(self): return "Integer(%s)" % (self.value, ) # # Real # class Real(Atomic): _app_tag = Tag.realAppTag def __init__(self, arg=None): self.value = 0.0 if arg is None: pass elif isinstance(arg, Tag): self.decode(arg) elif isinstance(arg, float): self.value = arg elif isinstance(arg, int): self.value = float(arg) elif isinstance(arg, Real): self.value = arg.value else: raise TypeError("invalid constructor datatype") def encode(self, tag): # encode the tag tag.set_app_data(Tag.realAppTag, struct.pack('>f',self.value)) def decode(self, tag): if (tag.tagClass != Tag.applicationTagClass) or (tag.tagNumber != Tag.realAppTag): raise InvalidTag("real application tag required") if len(tag.tagData) != 4: raise InvalidTag("invalid tag length") # extract the data self.value = struct.unpack('>f',tag.tagData)[0] def __str__(self): return "Real(%g)" % (self.value,) # # Double # class Double(Atomic): _app_tag = Tag.doubleAppTag def __init__(self,arg = None): self.value = 0.0 if arg is None: pass elif isinstance(arg, Tag): self.decode(arg) elif isinstance(arg, float): self.value = arg elif isinstance(arg, int): self.value = float(arg) elif isinstance(arg, Double): self.value = arg.value else: raise TypeError("invalid constructor datatype") def encode(self, tag): # encode the tag tag.set_app_data(Tag.doubleAppTag, struct.pack('>d',self.value)) def decode(self, tag): if (tag.tagClass != Tag.applicationTagClass) or (tag.tagNumber != Tag.doubleAppTag): raise InvalidTag("double application tag required") if len(tag.tagData) != 8: raise InvalidTag("invalid tag length") # extract the data self.value = struct.unpack('>d',tag.tagData)[0] def __str__(self): return "Double(%g)" % (self.value,) # # OctetString # class OctetString(Atomic): _app_tag = Tag.octetStringAppTag def __init__(self, arg=None): self.value = bytes() if arg is None: pass elif isinstance(arg, Tag): self.decode(arg) elif isinstance(arg, (bytes, bytearray)): self.value = bytes(arg) elif isinstance(arg, OctetString): self.value = arg.value else: raise TypeError("invalid constructor datatype") def encode(self, tag): # encode the tag tag.set_app_data(Tag.octetStringAppTag, self.value) def decode(self, tag): if (tag.tagClass != Tag.applicationTagClass) or (tag.tagNumber != Tag.octetStringAppTag): raise InvalidTag("octet string application tag required") self.value = tag.tagData def __str__(self): return "OctetString(X'" + btox(self.value) + "')" # # CharacterString # class CharacterString(Atomic): _app_tag = Tag.characterStringAppTag def __init__(self, arg=None): self.value = '' self.strEncoding = 0 self.strValue = b'' if arg is None: pass elif isinstance(arg, Tag): self.decode(arg) elif isinstance(arg, str): self.value = arg self.strValue = arg.encode('utf-8') elif isinstance(arg, CharacterString): self.value = arg.value self.strEncoding = arg.strEncoding self.strValue = arg.strValue else: raise TypeError("invalid constructor datatype") def encode(self, tag): # encode the tag tag.set_app_data(Tag.characterStringAppTag, bytes([self.strEncoding]) + self.strValue) def decode(self, tag): if (tag.tagClass != Tag.applicationTagClass) or (tag.tagNumber != Tag.characterStringAppTag): raise InvalidTag("character string application tag required") if len(tag.tagData) == 0: raise InvalidTag("invalid tag length") # byte array easier to deal with tag_data = bytearray(tag.tagData) # extract the data self.strEncoding = tag_data[0] self.strValue = tag_data[1:] # normalize the value if (self.strEncoding == 0): self.value = self.strValue.decode('utf-8') elif (self.strEncoding == 3): self.value = self.strValue.decode('utf_32be') elif (self.strEncoding == 4): self.value = self.strValue.decode('utf_16be') elif (self.strEncoding == 5): self.value = self.strValue.decode('latin_1') else: self.value = '### unknown encoding: %d ###' % (self.strEncoding,) def __str__(self): return "CharacterString(%d,X'%s')" % (self.strEncoding, btox(self.strValue)) # # BitString # class BitString(Atomic): _app_tag = Tag.bitStringAppTag bitNames = {} bitLen = 0 def __init__(self, arg = None): self.value = [0] * self.bitLen if arg is None: pass elif isinstance(arg, Tag): self.decode(arg) elif isinstance(arg, list): allInts = allStrings = True for elem in arg: allInts = allInts and ((elem == 0) or (elem == 1)) allStrings = allStrings and elem in self.bitNames if allInts: self.value = arg elif allStrings: for bit in arg: bit = self.bitNames[bit] if (bit < 0) or (bit > len(self.value)): raise IndexError("constructor element out of range") self.value[bit] = 1 else: raise TypeError("invalid constructor list element(s)") elif isinstance(arg,BitString): self.value = arg.value[:] else: raise TypeError("invalid constructor datatype") def encode(self, tag): # compute the unused bits to fill out the string _, used = divmod(len(self.value), 8) unused = used and (8 - used) or 0 # start with the number of unused bits data = bytearray([unused]) # build and append each packed octet bits = self.value + [0] * unused for i in range(0,len(bits),8): x = 0 for j in range(0,8): x |= bits[i + j] << (7 - j) data.append(x) # encode the tag tag.set_app_data(Tag.bitStringAppTag, data) def decode(self, tag): if (tag.tagClass != Tag.applicationTagClass) or (tag.tagNumber != Tag.bitStringAppTag): raise InvalidTag("bit string application tag required") if len(tag.tagData) == 0: raise InvalidTag("invalid tag length") tag_data = bytearray(tag.tagData) # extract the number of unused bits unused = tag_data[0] # extract the data data = [] for x in tag_data[1:]: for i in range(8): if (x & (1 << (7 - i))) != 0: data.append( 1 ) else: data.append( 0 ) # trim off the unused bits if unused: self.value = data[:-unused] else: self.value = data def __str__(self): # flip the bit names bitNames = {} for key, value in self.bitNames.items(): bitNames[value] = key # build a list of values and/or names valueList = [] for value, index in zip(self.value,range(len(self.value))): if index in bitNames: if value: valueList.append(bitNames[index]) else: valueList.append('!' + bitNames[index]) else: valueList.append(str(value)) # bundle it together return "BitString(" + ','.join(valueList) + ")" def __getitem__(self, bit): if isinstance(bit, int): pass elif isinstance(bit, str): if not bit in self.bitNames: raise IndexError("unknown bit name '%s'" % (bit,)) bit = self.bitNames[bit] else: raise TypeError("bit index must be an integer or bit name") if (bit < 0) or (bit > len(self.value)): raise IndexError("list index out of range") return self.value[bit] def __setitem__(self, bit, value): if isinstance(bit,int): pass elif isinstance(bit,str): if not bit in self.bitNames: raise IndexError("unknown bit name '%s'" % (bit,)) bit = self.bitNames[bit] else: raise TypeError("bit index must be an integer or bit name : %s is %s" % (bit,type(bit))) if (bit < 0) or (bit > len(self.value)): raise IndexError("list index out of range") # funny cast to a bit self.value[bit] = value and 1 or 0 # # Enumerated # class Enumerated(Atomic): _app_tag = Tag.enumeratedAppTag enumerations = {} _xlate_table = {} def __init__(self, arg=None): self.value = int(0) # see if the class has a translate table if '_xlate_table' not in self.__class__.__dict__: expand_enumerations(self.__class__) # initialize the object if arg is None: pass elif isinstance(arg, Tag): self.decode(arg) elif isinstance(arg, int): if (arg < 0): raise ValueError("unsigned integer required") # convert it to a string if you can self.value = self._xlate_table.get(arg, arg) elif isinstance(arg, str): if arg not in self._xlate_table: raise ValueError("undefined enumeration '%s'" % (arg,)) self.value = arg elif isinstance(arg, Enumerated): self.value = arg.value else: raise TypeError("invalid constructor datatype") def __getitem__(self, item): return self._xlate_table.get(item) def get_long(self): if isinstance(self.value, int): return self.value elif isinstance(self.value, str): return int(self._xlate_table[self.value]) else: raise TypeError("%s is an invalid enumeration value datatype" % (type(self.value),)) def keylist(self): """Return a list of names in order by value.""" items = self.enumerations.items() items.sort(lambda a, b: self.cmp(a[1], b[1])) # last item has highest value rslt = [None] * (items[-1][1] + 1) # map the values for key, value in items: rslt[value] = key # return the result return rslt def __cmp__(self, other): """Special function to make sure comparisons are done in enumeration order, not alphabetic order.""" # hoop jump it if not isinstance(other, self.__class__): other = self.__class__(other) # get the numeric version a = self.get_long() b = other.get_long() # now compare the values if (a < b): return -1 elif (a > b): return 1 else: return 0 def encode(self, tag): if isinstance(self.value, int): value = int(self.value) elif isinstance(self.value, str): value = self._xlate_table[self.value] else: raise TypeError("%s is an invalid enumeration value datatype" % (type(self.value),)) # rip apart the number data = bytearray(struct.pack('>L', value)) # reduce the value to the smallest number of octets while (len(data) > 1) and (data[0] == 0): del data[0] # encode the tag tag.set_app_data(Tag.enumeratedAppTag, data) def decode(self, tag): if (tag.tagClass != Tag.applicationTagClass) or (tag.tagNumber != Tag.enumeratedAppTag): raise InvalidTag("enumerated application tag required") if len(tag.tagData) == 0: raise InvalidTag("invalid tag length") # get the data rslt = 0 for c in tag.tagData: rslt = (rslt << 8) + c # translate to a string if possible rslt = self._xlate_table.get(rslt, rslt) # save the result self.value = rslt def __str__(self): return "%s(%s)" % (self.__class__.__name__, self.value) # # expand_enumerations # def expand_enumerations(klass): # build a value dictionary xlateTable = {} for c in klass.__mro__: enumerations = getattr(c, 'enumerations', {}) if enumerations: for name, value in enumerations.items(): # save the results xlateTable[name] = value xlateTable[value] = name # save the name in the class setattr(klass, name, value) # save the dictionary in the class setattr(klass, '_xlate_table', xlateTable) # # Date # _mm = r'(?P0?[1-9]|1[0-4]|odd|even|255|[*])' _dd = r'(?P[0-3]?\d|last|odd|even|255|[*])' _yy = r'(?P\d{2}|255|[*])' _yyyy = r'(?P\d{4}|255|[*])' _dow = r'(?P[1-7]|mon|tue|wed|thu|fri|sat|sun|255|[*])' _special_mon = {'*': 255, 'odd': 13, 'even': 14, None: 255} _special_mon_inv = {255: '*', 13: 'odd', 14: 'even'} _special_day = {'*': 255, 'last': 32, 'odd': 33, 'even': 34, None: 255} _special_day_inv = {255: '*', 32: 'last', 33: 'odd', 34: 'even'} _special_dow = {'*': 255, 'mon': 1, 'tue': 2, 'wed': 3, 'thu': 4, 'fri': 5, 'sat': 6, 'sun': 7} _special_dow_inv = {255: '*', 1: 'mon', 2: 'tue', 3: 'wed', 4: 'thu', 5: 'fri', 6: 'sat', 7: 'sun'} def _merge(*args): """Create a composite pattern and compile it.""" return re.compile(r'^' + r'[/-]'.join(args) + r'(?:\s+' + _dow + ')?$') # make a list of compiled patterns _date_patterns = [ _merge(_yyyy, _mm, _dd), _merge(_mm, _dd, _yyyy), _merge(_dd, _mm, _yyyy), _merge(_yy, _mm, _dd), _merge(_mm, _dd, _yy), _merge(_dd, _mm, _yy), ] class Date(Atomic): _app_tag = Tag.dateAppTag def __init__(self, arg=None, year=255, month=255, day=255, day_of_week=255): self.value = (year, month, day, day_of_week) if arg is None: pass elif isinstance(arg, Tag): self.decode(arg) elif isinstance(arg, tuple): self.value = arg elif isinstance(arg, str): # lower case everything arg = arg.lower() # make a list of the contents from matching patterns matches = [] for p in _date_patterns: m = p.match(arg) if m: matches.append(m.groupdict()) # try to find a good one match = None if not matches: raise ValueError("unmatched") # if there is only one, success if len(matches) == 1: match = matches[0] else: # check to see if they really are the same for a, b in zip(matches[:-1],matches[1:]): if a != b: raise ValueError("ambiguous") break else: match = matches[0] # extract the year and normalize year = match['year'] if (year == '*') or (not year): year = 255 else: year = int(year) if (year == 255): pass elif year < 35: year += 2000 elif year < 100: year += 1900 elif year < 1900: raise ValueError("invalid year") # extract the month and normalize month = match['month'] if month in _special_mon: month = _special_mon[month] else: month = int(month) if (month == 255): pass elif (month == 0) or (month > 14): raise ValueError("invalid month") # extract the day and normalize day = match['day'] if day in _special_day: day = _special_day[day] else: day = int(day) if (day == 255): pass elif (day == 0) or (day > 34): raise ValueError("invalid day") # extract the day-of-week and normalize day_of_week = match['dow'] if day_of_week in _special_dow: day_of_week = _special_dow[day_of_week] elif not day_of_week: pass else: day_of_week = int(day_of_week) if (day_of_week == 255): pass elif day_of_week > 7: raise ValueError("invalid day of week") # year becomes the correct octet if year != 255: year -= 1900 # save the value self.value = (year, month, day, day_of_week) # calculate the day of the week if not day_of_week: self.CalcDayOfWeek() elif isinstance(arg, Date): self.value = arg.value else: raise TypeError("invalid constructor datatype") def CalcDayOfWeek(self): """Calculate the correct day of the week.""" # rip apart the value year, month, day, day_of_week = self.value # assume the worst day_of_week = 255 # check for special values if year == 255: pass elif month in _special_mon_inv: pass elif day in _special_day_inv: pass else: try: today = time.mktime( (year + 1900, month, day, 0, 0, 0, 0, 0, -1) ) day_of_week = time.gmtime(today)[6] + 1 except OverflowError: pass # put it back together self.value = (year, month, day, day_of_week) def now(self): tup = time.localtime() self.value = (tup[0]-1900, tup[1], tup[2], tup[6] + 1) return self def encode(self, tag): # encode the tag tag.set_app_data(Tag.dateAppTag, bytearray(self.value)) def decode(self, tag): if (tag.tagClass != Tag.applicationTagClass) or (tag.tagNumber != Tag.dateAppTag): raise InvalidTag("date application tag required") if len(tag.tagData) != 4: raise InvalidTag("invalid tag length") # rip apart the data self.value = tuple(tag.tagData) def __str__(self): """String representation of the date.""" # rip it apart year, month, day, day_of_week = self.value if year == 255: year = "*" else: year = str(year + 1900) month = _special_mon_inv.get(month, str(month)) day = _special_day_inv.get(day, str(day)) day_of_week = _special_dow_inv.get(day_of_week, str(day_of_week)) return "%s(%s-%s-%s %s)" % (self.__class__.__name__, year, month, day, day_of_week) # # Time # class Time(Atomic): _app_tag = Tag.timeAppTag _time_regex = re.compile("^([*]|[0-9]+)[:]([*]|[0-9]+)(?:[:]([*]|[0-9]+)(?:[.]([*]|[0-9]+))?)?$") DONT_CARE = 255 def __init__(self, arg=None, hour=255, minute=255, second=255, hundredth=255): # put it together self.value = (hour, minute, second, hundredth) if arg is None: pass elif isinstance(arg, Tag): self.decode(arg) elif isinstance(arg, tuple): self.value = arg elif isinstance(arg, str): tup_match = Time._time_regex.match(arg) if not tup_match: raise ValueError("invalid time pattern") tup_list = [] tup_items = list(tup_match.groups()) for s in tup_items: if s == '*': tup_list.append(255) elif s is None: if '*' in tup_items: tup_list.append(255) else: tup_list.append(0) else: tup_list.append(int(s)) # fix the hundredths if necessary if (tup_list[3] > 0) and (tup_list[3] < 10): tup_list[3] = tup_list[3] * 10 self.value = tuple(tup_list) elif isinstance(arg, Time): self.value = arg.value else: raise TypeError("invalid constructor datatype") def now(self): now = time.time() tup = time.localtime(now) self.value = (tup[3], tup[4], tup[5], int((now - int(now)) * 100)) return self def encode(self, tag): # encode the tag tag.set_app_data(Tag.timeAppTag, bytearray(self.value)) def decode(self, tag): if (tag.tagClass != Tag.applicationTagClass) or (tag.tagNumber != Tag.timeAppTag): raise InvalidTag("time application tag required") if len(tag.tagData) != 4: raise InvalidTag("invalid tag length") # rip apart the data self.value = tuple(tag.tagData) def __str__(self): # rip it apart hour, minute, second, hundredth = self.value rslt = "Time(" if hour == 255: rslt += "*:" else: rslt += "%02d:" % (hour,) if minute == 255: rslt += "*:" else: rslt += "%02d:" % (minute,) if second == 255: rslt += "*." else: rslt += "%02d." % (second,) if hundredth == 255: rslt += "*)" else: rslt += "%02d)" % (hundredth,) return rslt # # ObjectType # class ObjectType(Enumerated): vendor_range = (128, 1023) enumerations = \ { 'accessDoor':30 , 'accessPoint':33 , 'accessRights':34 , 'accessUser':35 , 'accessZone':36 , 'accumulator':23 , 'analogInput':0 , 'analogOutput':1 , 'analogValue':2 , 'averaging':18 , 'binaryInput':3 , 'binaryOutput':4 , 'binaryValue':5 , 'bitstringValue':39 , 'calendar':6 , 'characterstringValue':40 , 'command':7 , 'credentialDataInput':37 , 'datePatternValue':41 , 'dateValue':42 , 'datetimePatternValue':43 , 'datetimeValue':44 , 'device':8 , 'eventEnrollment':9 , 'eventLog':25 , 'file':10 , 'globalGroup':26 , 'group':11 , 'integerValue':45 , 'largeAnalogValue':46 , 'lifeSafetyPoint':21 , 'lifeSafetyZone':22 , 'loadControl':28 , 'loop':12 , 'multiStateInput':13 , 'multiStateOutput':14 , 'multiStateValue':19 , 'networkSecurity':38 , 'notificationClass':15 , 'octetstringValue':47 , 'positiveIntegerValue':48 , 'program':16 , 'pulseConverter':24 , 'schedule':17 , 'structuredView':29 , 'timePatternValue':49 , 'timeValue':50 , 'trendLog':20 , 'trendLogMultiple':27 } expand_enumerations(ObjectType) # # ObjectIdentifier # class ObjectIdentifier(Atomic): _app_tag = Tag.objectIdentifierAppTag objectTypeClass = ObjectType maximum_instance_number = 0x003FFFFF def __init__(self, *args): self.value = ('analogInput', 0) if len(args) == 0: pass elif len(args) == 1: arg = args[0] if isinstance(arg, Tag): self.decode(arg) elif isinstance(arg, int): self.set_long(arg) elif isinstance(arg, tuple): self.set_tuple(*arg) elif isinstance(arg, ObjectIdentifier): self.value = arg.value else: raise TypeError("invalid constructor datatype") elif len(args) == 2: self.set_tuple(*args) else: raise ValueError("invalid constructor parameters") def set_tuple(self, objType, objInstance): # allow a type name as well as an integer if isinstance(objType, int): # try and make it pretty objType = self.objectTypeClass._xlate_table.get(objType, objType) elif isinstance(objType, str): # make sure the type is known if objType not in self.objectTypeClass._xlate_table: raise ValueError("unrecognized object type '%s'" % (objType,)) else: raise TypeError("invalid datatype for objType: %r, %r" % (type(objType), objType)) # check for valid instance number if (objInstance < 0) or (objInstance > ObjectIdentifier.maximum_instance_number): raise ValueError("instance number out of range") # pack the components together self.value = (objType, objInstance) def get_tuple(self): """Return the unsigned integer tuple of the identifier.""" objType, objInstance = self.value if isinstance(objType, int): pass elif isinstance(objType, str): # turn it back into an integer objType = self.objectTypeClass()[objType] else: raise TypeError("invalid datatype for objType") # pack the components together return (objType, objInstance) def set_long(self, value): # suck out the type objType = (value >> 22) & 0x03FF # try and make it pretty objType = self.objectTypeClass()[objType] or objType # suck out the instance objInstance = value & 0x003FFFFF # save the result self.value = (objType, objInstance) def get_long(self): """Return the unsigned integer representation of the identifier.""" objType, objInstance = self.get_tuple() # pack the components together return ((objType << 22) + objInstance) def encode(self, tag): # encode the tag tag.set_app_data(Tag.objectIdentifierAppTag, struct.pack('>L', self.get_long())) def decode(self, tag): if (tag.tagClass != Tag.applicationTagClass) or (tag.tagNumber != Tag.objectIdentifierAppTag): raise InvalidTag("object identifier application tag required") if len(tag.tagData) != 4: raise InvalidTag("invalid tag length") # extract the data self.set_long(struct.unpack('>L',tag.tagData)[0]) def __str__(self): # rip it apart objType, objInstance = self.value if isinstance(objType, str): typestr = objType elif objType < 0: typestr = "Bad %d" % (objType,) elif objType in self.objectTypeClass._xlate_table: typestr = self.objectTypeClass._xlate_table[objType] elif (objType < 128): typestr = "Reserved %d" % (objType,) else: typestr = "Vendor %d" % (objType,) return "ObjectIdentifier(%s,%d)" % (typestr, objInstance) def __hash__(self): return hash(self.value) def __lt__(self, other): """Special function to make sure comparisons are done in enumeration order, not alphabetic order.""" # hoop jump it if not isinstance(other, self.__class__): other = self.__class__(other) # get the numeric version a = self.get_long() b = other.get_long() # now compare the values return (a < b) # # Application Tag Classes # # This list is set in the Tag class so that the app_to_object # function can return one of the appliction datatypes. It # can't be provided in the Tag class definition because the # classes aren't defined yet. # Tag._app_tag_class = \ [ Null, Boolean, Unsigned, Integer , Real, Double, OctetString, CharacterString , BitString, Enumerated, Date, Time , ObjectIdentifier, None, None, None ] PK;dF$\\bacpypes/npdu.py#!/usr/bin/python """ NPDU """ from .errors import DecodingError from .debugging import ModuleLogger, DebugContents, bacpypes_debugging, btox from .pdu import Address, RemoteStation, RemoteBroadcast, GlobalBroadcast, \ PCI, PDUData # some debugging _debug = 0 _log = ModuleLogger(globals()) # a dictionary of message type values and classes npdu_types = {} def register_npdu_type(klass): npdu_types[klass.messageType] = klass # # NPCI # @bacpypes_debugging class NPCI(PCI, DebugContents): _debug_contents = ('npduVersion', 'npduControl', 'npduDADR', 'npduSADR' , 'npduHopCount', 'npduNetMessage', 'npduVendorID' ) whoIsRouterToNetwork = 0x00 iAmRouterToNetwork = 0x01 iCouldBeRouterToNetwork = 0x02 rejectMessageToNetwork = 0x03 routerBusyToNetwork = 0x04 routerAvailableToNetwork = 0x05 initializeRoutingTable = 0x06 initializeRoutingTableAck = 0x07 establishConnectionToNetwork = 0x08 disconnectConnectionToNetwork = 0x09 challengeRequest = 0x0A securityPayload = 0x0B securityResponse = 0x0C requestKeyUpdate = 0x0D updateKeySet = 0x0E updateDistributionKey = 0x0F requestMasterKey = 0x10 setMasterKey = 0x11 whatIsNetworkNumber = 0x12 networkNumberIs = 0x13 def __init__(self, *args, **kwargs): super(NPCI, self).__init__(*args, **kwargs) self.npduVersion = 1 self.npduControl = None self.npduDADR = None self.npduSADR = None self.npduHopCount = None self.npduNetMessage = None self.npduVendorID = None def update(self, npci): PCI.update(self, npci) self.npduVersion = npci.npduVersion self.npduControl = npci.npduControl self.npduDADR = npci.npduDADR self.npduSADR = npci.npduSADR self.npduHopCount = npci.npduHopCount self.npduNetMessage = npci.npduNetMessage self.npduVendorID = npci.npduVendorID def encode(self, pdu): """encode the contents of the NPCI into the PDU.""" if _debug: NPCI._debug("encode %s", repr(pdu)) PCI.update(pdu, self) # only version 1 messages supported pdu.put(self.npduVersion) # build the flags if self.npduNetMessage is not None: netLayerMessage = 0x80 else: netLayerMessage = 0x00 # map the destination address dnetPresent = 0x00 if self.npduDADR is not None: dnetPresent = 0x20 # map the source address snetPresent = 0x00 if self.npduSADR is not None: snetPresent = 0x08 # encode the control octet control = netLayerMessage | dnetPresent | snetPresent if self.pduExpectingReply: control |= 0x04 control |= (self.pduNetworkPriority & 0x03) self.npduControl = control pdu.put(control) # make sure expecting reply and priority get passed down pdu.pduExpectingReply = self.pduExpectingReply pdu.pduNetworkPriority = self.pduNetworkPriority # encode the destination address if dnetPresent: if self.npduDADR.addrType == Address.remoteStationAddr: pdu.put_short(self.npduDADR.addrNet) pdu.put(self.npduDADR.addrLen) pdu.put_data(self.npduDADR.addrAddr) elif self.npduDADR.addrType == Address.remoteBroadcastAddr: pdu.put_short(self.npduDADR.addrNet) pdu.put(0) elif self.npduDADR.addrType == Address.globalBroadcastAddr: pdu.put_short(0xFFFF) pdu.put(0) # encode the source address if snetPresent: pdu.put_short(self.npduSADR.addrNet) pdu.put(self.npduSADR.addrLen) pdu.put_data(self.npduSADR.addrAddr) # put the hop count if dnetPresent: pdu.put(self.npduHopCount) # put the network layer message type (if present) if netLayerMessage: pdu.put(self.npduNetMessage) # put the vendor ID if (self.npduNetMessage >= 0x80) and (self.npduNetMessage <= 0xFF): pdu.put_short(self.npduVendorID) def decode(self, pdu): """decode the contents of the PDU and put them into the NPDU.""" if _debug: NPCI._debug("decode %s", str(pdu)) PCI.update(self, pdu) # check the length if len(pdu.pduData) < 2: raise DecodingError("invalid length") # only version 1 messages supported self.npduVersion = pdu.get() if (self.npduVersion != 0x01): raise DecodingError("only version 1 messages supported") # decode the control octet self.npduControl = control = pdu.get() netLayerMessage = control & 0x80 dnetPresent = control & 0x20 snetPresent = control & 0x08 self.pduExpectingReply = (control & 0x04) != 0 self.pduNetworkPriority = control & 0x03 # extract the destination address if dnetPresent: dnet = pdu.get_short() dlen = pdu.get() dadr = pdu.get_data(dlen) if dnet == 0xFFFF: self.npduDADR = GlobalBroadcast() elif dlen == 0: self.npduDADR = RemoteBroadcast(dnet) else: self.npduDADR = RemoteStation(dnet, dadr) # extract the source address if snetPresent: snet = pdu.get_short() slen = pdu.get() sadr = pdu.get_data(slen) if snet == 0xFFFF: raise DecodingError("SADR can't be a global broadcast") elif slen == 0: raise DecodingError("SADR can't be a remote broadcast") self.npduSADR = RemoteStation(snet, sadr) # extract the hop count if dnetPresent: self.npduHopCount = pdu.get() # extract the network layer message type (if present) if netLayerMessage: self.npduNetMessage = pdu.get() if (self.npduNetMessage >= 0x80) and (self.npduNetMessage <= 0xFF): # extract the vendor ID self.npduVendorID = pdu.get_short() else: # application layer message self.npduNetMessage = None def npci_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" if _debug: NPCI._debug("npci_contents use_dict=%r as_class=%r", use_dict, as_class) # make/extend the dictionary of content if use_dict is None: if _debug: NPCI._debug(" - new use_dict") use_dict = as_class() # version and control are simple use_dict.__setitem__('version', self.npduVersion) use_dict.__setitem__('control', self.npduControl) # dnet/dlen/dadr if self.npduDADR is not None: if self.npduDADR.addrType == Address.remoteStationAddr: use_dict.__setitem__('dnet', self.npduDADR.addrNet) use_dict.__setitem__('dlen', self.npduDADR.addrLen) use_dict.__setitem__('dadr', btox(self.npduDADR.addrAddr or b'')) elif self.npduDADR.addrType == Address.remoteBroadcastAddr: use_dict.__setitem__('dnet', self.npduDADR.addrNet) use_dict.__setitem__('dlen', 0) use_dict.__setitem__('dadr', '') elif self.npduDADR.addrType == Address.globalBroadcastAddr: use_dict.__setitem__('dnet', 0xFFFF) use_dict.__setitem__('dlen', 0) use_dict.__setitem__('dadr', '') # snet/slen/sadr if self.npduSADR is not None: use_dict.__setitem__('snet', self.npduSADR.addrNet) use_dict.__setitem__('slen', self.npduSADR.addrLen) use_dict.__setitem__('sadr', btox(self.npduSADR.addrAddr or b'')) # hop count if self.npduHopCount is not None: use_dict.__setitem__('hop_count', self.npduHopCount) # network layer message name decoded if self.npduNetMessage is not None: use_dict.__setitem__('net_message', self.npduNetMessage) if self.npduVendorID is not None: use_dict.__setitem__('vendor_id', self.npduVendorID) # return what we built/updated return use_dict # # NPDU # @bacpypes_debugging class NPDU(NPCI, PDUData): def __init__(self, *args, **kwargs): super(NPDU, self).__init__(*args, **kwargs) def encode(self, pdu): NPCI.encode(self, pdu) pdu.put_data(self.pduData) def decode(self, pdu): NPCI.decode(self, pdu) self.pduData = pdu.get_data(len(pdu.pduData)) def npdu_contents(self, use_dict=None, as_class=dict): return PDUData.pdudata_contents(self, use_dict=use_dict, as_class=as_class) def dict_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" if _debug: NPDU._debug("dict_contents use_dict=%r as_class=%r", use_dict, as_class) # make/extend the dictionary of content if use_dict is None: use_dict = as_class() # call the parent classes self.npci_contents(use_dict=use_dict, as_class=as_class) self.npdu_contents(use_dict=use_dict, as_class=as_class) # return what we built/updated return use_dict # # key_value_contents # @bacpypes_debugging def key_value_contents(use_dict=None, as_class=dict, key_values=()): """Return the contents of an object as a dict.""" if _debug: key_value_contents._debug("key_value_contents use_dict=%r as_class=%r key_values=%r", use_dict, as_class, key_values) # make/extend the dictionary of content if use_dict is None: use_dict = as_class() # loop through the values and save them for k, v in key_values: if v is not None: if hasattr(v, 'dict_contents'): v = v.dict_contents(as_class=as_class) use_dict.__setitem__(k, v) # return what we built/updated return use_dict #------------------------------ # # WhoIsRouterToNetwork # class WhoIsRouterToNetwork(NPDU): _debug_contents = ('wirtnNetwork',) messageType = 0x00 def __init__(self, net=None, *args, **kwargs): super(WhoIsRouterToNetwork, self).__init__(*args, **kwargs) self.npduNetMessage = WhoIsRouterToNetwork.messageType self.wirtnNetwork = net def encode(self, npdu): NPCI.update(npdu, self) if self.wirtnNetwork is not None: npdu.put_short( self.wirtnNetwork ) def decode(self, npdu): NPCI.update(self, npdu) if npdu.pduData: self.wirtnNetwork = npdu.get_short() else: self.wirtnNetwork = None def npdu_contents(self, use_dict=None, as_class=dict): return key_value_contents(use_dict=use_dict, as_class=as_class, key_values=( ('function', 'WhoIsRouterToNetwork'), ('network', self.wirtnNetwork), )) register_npdu_type(WhoIsRouterToNetwork) # # IAmRouterToNetwork # class IAmRouterToNetwork(NPDU): _debug_contents = ('iartnNetworkList',) messageType = 0x01 def __init__(self, netList=[], *args, **kwargs): super(IAmRouterToNetwork, self).__init__(*args, **kwargs) self.npduNetMessage = IAmRouterToNetwork.messageType self.iartnNetworkList = netList def encode(self, npdu): NPCI.update(npdu, self) for net in self.iartnNetworkList: npdu.put_short(net) def decode(self, npdu): NPCI.update(self, npdu) self.iartnNetworkList = [] while npdu.pduData: self.iartnNetworkList.append(npdu.get_short()) def npdu_contents(self, use_dict=None, as_class=dict): return key_value_contents(use_dict=use_dict, as_class=as_class, key_values=( ('function', 'IAmRouterToNetwork'), ('network_list', self.iartnNetworkList), )) register_npdu_type(IAmRouterToNetwork) # # ICouldBeRouterToNetwork # class ICouldBeRouterToNetwork(NPDU): _debug_contents = ('icbrtnNetwork','icbrtnPerformanceIndex') messageType = 0x02 def __init__(self, net=None, perf=None, *args, **kwargs): super(ICouldBeRouterToNetwork, self).__init__(*args, **kwargs) self.npduNetMessage = ICouldBeRouterToNetwork.messageType self.icbrtnNetwork = net self.icbrtnPerformanceIndex = perf def encode(self, npdu): NPCI.update(npdu, self) npdu.put_short( self.icbrtnNetwork ) npdu.put( self.icbrtnPerformanceIndex ) def decode(self, npdu): NPCI.update(self, npdu) self.icbrtnNetwork = npdu.get_short() self.icbrtnPerformanceIndex = npdu.get() def npdu_contents(self, use_dict=None, as_class=dict): return key_value_contents(use_dict=use_dict, as_class=as_class, key_values=( ('function', 'ICouldBeRouterToNetwork'), ('network', self.icbrtnNetwork), ('performance_index', self.icbrtnPerformanceIndex), )) register_npdu_type(ICouldBeRouterToNetwork) # # RejectMessageToNetwork # class RejectMessageToNetwork(NPDU): _debug_contents = ('rmtnRejectReason','rmtnDNET') messageType = 0x03 def __init__(self, reason=None, dnet=None, *args, **kwargs): super(RejectMessageToNetwork, self).__init__(*args, **kwargs) self.npduNetMessage = RejectMessageToNetwork.messageType self.rmtnRejectionReason = reason self.rmtnDNET = dnet def encode(self, npdu): NPCI.update(npdu, self) npdu.put( self.rmtnRejectionReason ) npdu.put_short( self.rmtnDNET ) def decode(self, npdu): NPCI.update(self, npdu) self.rmtnRejectionReason = npdu.get() self.rmtnDNET = npdu.get_short() def npdu_contents(self, use_dict=None, as_class=dict): return key_value_contents(use_dict=use_dict, as_class=as_class, key_values=( ('function', 'RejectMessageToNetwork'), ('reject_reason', self.rmtnRejectionReason), ('dnet', self.rmtnDNET), )) register_npdu_type(RejectMessageToNetwork) # # RouterBusyToNetwork # class RouterBusyToNetwork(NPDU): _debug_contents = ('rbtnNetworkList',) messageType = 0x04 def __init__(self, netList=[], *args, **kwargs): super(RouterBusyToNetwork, self).__init__(*args, **kwargs) self.npduNetMessage = RouterBusyToNetwork.messageType self.rbtnNetworkList = netList def encode(self, npdu): NPCI.update(npdu, self) for net in self.ratnNetworkList: npdu.put_short(net) def decode(self, npdu): NPCI.update(self, npdu) self.rbtnNetworkList = [] while npdu.pduData: self.rbtnNetworkList.append(npdu.get_short()) def npdu_contents(self, use_dict=None, as_class=dict): return key_value_contents(use_dict=use_dict, as_class=as_class, key_values=( ('function', 'RouterBusyToNetwork'), ('network_list', self.rbtnNetworkList), )) register_npdu_type(RouterBusyToNetwork) # # RouterAvailableToNetwork # class RouterAvailableToNetwork(NPDU): _debug_contents = ('ratnNetworkList',) messageType = 0x05 def __init__(self, netList=[], *args, **kwargs): super(RouterAvailableToNetwork, self).__init__(*args, **kwargs) self.npduNetMessage = RouterAvailableToNetwork.messageType self.ratnNetworkList = netList def encode(self, npdu): NPCI.update(npdu, self) for net in self.ratnNetworkList: npdu.put_short(net) def decode(self, npdu): NPCI.update(self, npdu) self.ratnNetworkList = [] while npdu.pduData: self.ratnNetworkList.append(npdu.get_short()) def npdu_contents(self, use_dict=None, as_class=dict): return key_value_contents(use_dict=use_dict, as_class=as_class, key_values=( ('function', 'RouterAvailableToNetwork'), ('network_list', self.ratnNetworkList), )) register_npdu_type(RouterAvailableToNetwork) # # Routing Table Entry # class RoutingTableEntry(DebugContents): _debug_contents = ('rtDNET', 'rtPortID', 'rtPortInfo') def __init__(self, dnet=None, portID=None, portInfo=None): self.rtDNET = dnet self.rtPortID = portID self.rtPortInfo = portInfo def dict_contents(self, use_dict=None, as_class=dict): """Return the contents of an object as a dict.""" # make/extend the dictionary of content if use_dict is None: use_dict = as_class() # save the content use_dict.__setitem__('dnet', self.rtDNET) use_dict.__setitem__('port_id', self.rtPortID) use_dict.__setitem__('port_info', self.rtPortInfo) # return what we built/updated return use_dict # # InitializeRoutingTable # class InitializeRoutingTable(NPDU): messageType = 0x06 _debug_contents = ('irtTable++',) def __init__(self, routingTable=[], *args, **kwargs): super(InitializeRoutingTable, self).__init__(*args, **kwargs) self.npduNetMessage = InitializeRoutingTable.messageType self.irtTable = routingTable def encode(self, npdu): NPCI.update(npdu, self) npdu.put(len(self.irtTable)) for rte in self.irtTable: npdu.put_short(rte.rtDNET) npdu.put(rte.rtPortID) npdu.put(len(rte.rtPortInfo)) npdu.put_data(rte.rtPortInfo) def decode(self, npdu): NPCI.update(self, npdu) self.irtTable = [] rtLength = npdu.get() for i in range(rtLength): dnet = npdu.get_short() portID = npdu.get() portInfoLen = npdu.get() portInfo = npdu.get_data(portInfoLen) rte = RoutingTableEntry(dnet, portID, portInfo) self.irtTable.append(rte) def npdu_contents(self, use_dict=None, as_class=dict): routing_table = [] for rte in self.irtTable: routing_table.append(rte.dict_contents(as_class=as_class)) return key_value_contents(use_dict=use_dict, as_class=as_class, key_values=( ('function', 'InitializeRoutingTable'), ('routing_table', routing_table), )) register_npdu_type(InitializeRoutingTable) # # InitializeRoutingTableAck # class InitializeRoutingTableAck(NPDU): messageType = 0x07 _debug_contents = ('irtaTable++',) def __init__(self, routingTable=[], *args, **kwargs): super(InitializeRoutingTableAck, self).__init__(*args, **kwargs) self.npduNetMessage = InitializeRoutingTableAck.messageType self.irtaTable = routingTable def encode(self, npdu): NPCI.update(npdu, self) npdu.put(len(self.irtaTable)) for rte in self.irtaTable: npdu.put_short(rte.rtDNET) npdu.put(rte.rtPortID) npdu.put(len(rte.rtPortInfo)) npdu.put_data(rte.rtPortInfo) def decode(self, npdu): NPCI.update(self, npdu) self.irtaTable = [] rtLength = npdu.get() for i in range(rtLength): dnet = npdu.get_short() portID = npdu.get() portInfoLen = npdu.get() portInfo = npdu.get_data(portInfoLen) rte = RoutingTableEntry(dnet, portID, portInfo) self.irtaTable.append(rte) def npdu_contents(self, use_dict=None, as_class=dict): routing_table = [] for rte in self.irtaTable: routing_table.append(rte.dict_contents(as_class=as_class)) return key_value_contents(use_dict=use_dict, as_class=as_class, key_values=( ('function', 'InitializeRoutingTableAck'), ('routing_table', routing_table), )) register_npdu_type(InitializeRoutingTableAck) # # EstablishConnectionToNetwork # class EstablishConnectionToNetwork(NPDU): _debug_contents = ('ectnDNET', 'ectnTerminationTime') messageType = 0x08 def __init__(self, dnet=None, terminationTime=None, *args, **kwargs): super(EstablishConnectionToNetwork, self).__init__(*args, **kwargs) self.npduNetMessage = EstablishConnectionToNetwork.messageType self.ectnDNET = dnet self.ectnTerminationTime = terminationTime def encode(self, npdu): NPCI.update(npdu, self) npdu.put_short( self.ectnDNET ) npdu.put( self.ectnTerminationTime ) def decode(self, npdu): NPCI.update(self, npdu) self.ectnDNET = npdu.get_short() self.ectnTerminationTime = npdu.get() def npdu_contents(self, use_dict=None, as_class=dict): return key_value_contents(use_dict=use_dict, as_class=as_class, key_values=( ('function', 'EstablishConnectionToNetwork'), ('dnet', self.ectnDNET), ('termination_time', self.ectnTerminationTime), )) register_npdu_type(EstablishConnectionToNetwork) # # DisconnectConnectionToNetwork # class DisconnectConnectionToNetwork(NPDU): _debug_contents = ('dctnDNET',) messageType = 0x09 def __init__(self, dnet=None, *args, **kwargs): super(DisconnectConnectionToNetwork, self).__init__(*args, **kwargs) self.npduNetMessage = DisconnectConnectionToNetwork.messageType self.dctnDNET = dnet def encode(self, npdu): NPCI.update(npdu, self) npdu.put_short( self.dctnDNET ) def decode(self, npdu): NPCI.update(self, npdu) self.dctnDNET = npdu.get_short() def npdu_contents(self, use_dict=None, as_class=dict): return key_value_contents(use_dict=use_dict, as_class=as_class, key_values=( ('function', 'DisconnectConnectionToNetwork'), ('dnet', self.dctnDNET), )) register_npdu_type(DisconnectConnectionToNetwork) # # WhatIsNetworkNumber # class WhatIsNetworkNumber(NPDU): _debug_contents = () messageType = 0x12 def encode(self, npdu): NPCI.update(npdu, self) def decode(self, npdu): NPCI.update(self, npdu) def npdu_contents(self, use_dict=None, as_class=dict): return key_value_contents(use_dict=use_dict, as_class=as_class, key_values=( ('function', 'WhatIsNetworkNumber'), )) register_npdu_type(WhatIsNetworkNumber) # # NetworkNumberIs # class NetworkNumberIs(NPDU): _debug_contents = ('nniNET', 'nniFlag',) messageType = 0x13 def encode(self, npdu): NPCI.update(npdu, self) npdu.put_short( self.nniNET ) npdu.put( self.nniFlag ) def decode(self, npdu): NPCI.update(self, npdu) self.nniNET = npdu.get_short() self.nniFlag = npdu.get() def npdu_contents(self, use_dict=None, as_class=dict): return key_value_contents(use_dict=use_dict, as_class=as_class, key_values=( ('function', 'NetorkNumberIs'), ('net', self.nniNET), ('flag', self.nniFlag), )) register_npdu_type(NetworkNumberIs) PK*UH<bacpypes/core.py#!/usr/bin/python """ Core """ import sys import asyncore import signal import time import traceback from .task import TaskManager from .debugging import bacpypes_debugging, ModuleLogger # some debugging _debug = 0 _log = ModuleLogger(globals()) # globals running = False taskManager = None deferredFns = [] sleeptime = 0.0 # # run # SPIN = 1.0 @bacpypes_debugging def run(spin=SPIN): if _debug: run._debug("run spin=%r", spin) global running, taskManager, deferredFns, sleeptime # reference the task manager (a singleton) taskManager = TaskManager() # count how many times we are going through the loop loopCount = 0 running = True while running: # if _debug: run._debug(" - time: %r", time.time()) loopCount += 1 # get the next task task, delta = taskManager.get_next_task() try: # if there is a task to process, do it if task: # if _debug: run._debug(" - task: %r", task) taskManager.process_task(task) # if delta is None, there are no tasks, default to spinning if delta is None: delta = spin # there may be threads around, sleep for a bit if sleeptime and (delta > sleeptime): time.sleep(sleeptime) delta -= sleeptime # if there are deferred functions, use a small delta if deferredFns: delta = min(delta, 0.001) # if _debug: run._debug(" - delta: %r", delta) # loop for socket activity asyncore.loop(timeout=delta, count=1) # check for deferred functions while deferredFns: # get a reference to the list fnlist = deferredFns deferredFns = [] # call the functions for fn, args, kwargs in fnlist: # if _debug: run._debug(" - call: %r %r %r", fn, args, kwargs) fn( *args, **kwargs) # done with this list del fnlist except KeyboardInterrupt: if _debug: run._info("keyboard interrupt") running = False except Exception as err: if _debug: run._exception("an error has occurred: %s", err) running = False # # run_once # @bacpypes_debugging def run_once(): """ Make a pass through the scheduled tasks and deferred functions just like the run() function but without the asyncore call (so there is no socket IO actviity) and the timers. """ if _debug: run_once._debug("run_once") global taskManager, deferredFns # reference the task manager (a singleton) taskManager = TaskManager() try: delta = 0.0 while delta == 0.0: # get the next task task, delta = taskManager.get_next_task() if _debug: run_once._debug(" - task, delta: %r, %r", task, delta) # if there is a task to process, do it if task: taskManager.process_task(task) # check for deferred functions while deferredFns: # get a reference to the list fnlist = deferredFns deferredFns = [] # call the functions for fn, args, kwargs in fnlist: if _debug: run_once._debug(" - call: %r %r %r", fn, args, kwargs) fn( *args, **kwargs) # done with this list del fnlist except KeyboardInterrupt: if _debug: run_once._info("keyboard interrupt") except Exception as err: if _debug: run_once._exception("an error has occurred: %s", err) # # stop # @bacpypes_debugging def stop(*args): """Call to stop running, may be called with a signum and frame parameter if called as a signal handler.""" if _debug: stop._debug("stop") global running, taskManager if args: sys.stderr.write("===== TERM Signal, %s\n" % time.strftime("%d-%b-%Y %H:%M:%S")) sys.stderr.flush() running = False # trigger the task manager event if taskManager and taskManager.trigger: if _debug: stop._debug(" - trigger") taskManager.trigger.set() # set a TERM signal handler if hasattr(signal, 'SIGTERM'): signal.signal(signal.SIGTERM, stop) # # print_stack # @bacpypes_debugging def print_stack(sig, frame): """Signal handler to print a stack trace and some interesting values.""" if _debug: print_stack._debug("print_stack, %r, %r", sig, frame) global running, deferredFns, sleeptime sys.stderr.write("==== USR1 Signal, %s\n" % time.strftime("%d-%b-%Y %H:%M:%S")) sys.stderr.write("---------- globals\n") sys.stderr.write(" running: %r\n" % (running,)) sys.stderr.write(" deferredFns: %r\n" % (deferredFns,)) sys.stderr.write(" sleeptime: %r\n" % (sleeptime,)) sys.stderr.write("---------- stack\n") traceback.print_stack(frame) # make a list of interesting frames flist = [] f = frame while f.f_back: flist.append(f) f = f.f_back # reverse the list so it is in the same order as print_stack flist.reverse() for f in flist: sys.stderr.write("---------- frame: %s\n" % (f,)) for k, v in f.f_locals.items(): sys.stderr.write(" %s: %r\n" % (k, v)) sys.stderr.flush() # set a USR1 signal handler to print a stack trace if hasattr(signal, 'SIGUSR1'): signal.signal(signal.SIGUSR1, print_stack) # # deferred # def deferred(fn, *args, **kwargs): # _log.debug("deferred %r %r %r", fn, args, kwargs) global deferredFns # append it to the list deferredFns.append((fn, args, kwargs)) # # enable_sleeping # @bacpypes_debugging def enable_sleeping(stime=0.001): if _debug: enable_sleeping._debug("enable_sleeping %r", stime) global sleeptime # set the sleep time sleeptime = stime PKH I&I&bacpypes/task.py#!/usr/bin/python """ Task """ import sys from time import time as _time from heapq import heapify, heappush, heappop from .singleton import SingletonLogging from .debugging import DebugContents, Logging, ModuleLogger, bacpypes_debugging # some debugging _debug = 0 _log = ModuleLogger(globals()) # globals _task_manager = None _unscheduled_tasks = [] # only defined for linux platforms if sys.platform in ('linux', 'darwin'): from .event import WaitableEvent # # _Trigger # # An instance of this class is used in the task manager to break # the asyncore.loop() call. In this case, handle_read will # immediately "clear" the event. # class _Trigger(WaitableEvent, Logging): def handle_read(self): if _debug: _Trigger._debug("handle_read") # read in the character, highlander data = self.recv(1) if _debug: _Trigger._debug(" - data: %r", data) else: _Trigger = None # # _Task # class _Task(DebugContents, Logging): _debug_contents = ('taskTime', 'isScheduled') def __init__(self): self.taskTime = None self.isScheduled = False def install_task(self, when=None, delta=None): global _task_manager, _unscheduled_tasks # check for delta from now if (when is None) and (delta is not None): if not _task_manager: raise RuntimeError("no task manager") when = _task_manager.get_time() + delta # fallback to the inited value if when is None: when = self.taskTime if when is None: raise RuntimeError("schedule missing, use zero for 'now'") self.taskTime = when # pass along to the task manager if not _task_manager: _unscheduled_tasks.append(self) else: _task_manager.install_task(self) def process_task(self): raise RuntimeError("process_task must be overridden") def suspend_task(self): global _task_manager # pass along to the task manager if not _task_manager: _unscheduled_tasks.remove(self) else: _task_manager.suspend_task(self) def resume_task(self): global _task_manager _task_manager.resume_task(self) def __lt__(self, other): return id(self) < id(other) # # OneShotTask # class OneShotTask(_Task): def __init__(self, when=None): _Task.__init__(self) self.taskTime = when # # OneShotDeleteTask # class OneShotDeleteTask(_Task): def __init__(self, when=None): _Task.__init__(self) self.taskTime = when # # OneShotFunction # @bacpypes_debugging def OneShotFunction(fn, *args, **kwargs): class OneShotFunctionTask(OneShotDeleteTask): def process_task(self): OneShotFunction._debug("process_task %r %s %s", fn, repr(args), repr(kwargs)) fn(*args, **kwargs) task = OneShotFunctionTask() # if there is no task manager, postpone the install if not _task_manager: _unscheduled_tasks.append(task) else: task.install_task(_task_manager.get_time()) return task # # FunctionTask # def FunctionTask(fn, *args, **kwargs): _log.debug("FunctionTask %r %r %r", fn, args, kwargs) class _FunctionTask(OneShotDeleteTask): def process_task(self): _log.debug("process_task (%r %r %r)", fn, args, kwargs) fn(*args, **kwargs) task = _FunctionTask() _log.debug(" - task: %r", task) return task # # RecurringTask # @bacpypes_debugging class RecurringTask(_Task): _debug_contents = ('taskInterval',) def __init__(self, interval=None): if _debug: RecurringTask._debug("__init__ interval=%r", interval) _Task.__init__(self) # save the interval, but do not automatically install self.taskInterval = interval def install_task(self, interval=None): if _debug: RecurringTask._debug("install_task interval=%r", interval) global _task_manager, _unscheduled_tasks # set the interval if it hasn't already been set if interval is not None: self.taskInterval = interval if self.taskInterval is None: raise RuntimeError("interval unset, use ctor or install_task parameter") if self.taskInterval <= 0.0: raise RuntimeError("interval must be greater than zero") # if there is no task manager, postpone the install if not _task_manager: if _debug: RecurringTask._debug(" - no task manager") _unscheduled_tasks.append(self) else: # get ready for the next interval (aligned) now = _task_manager.get_time() interval = self.taskInterval / 1000.0 self.taskTime = now + interval - (now % interval) if _debug: RecurringTask._debug(" - task time: %r", self.taskTime) # install it _task_manager.install_task(self) # # RecurringFunctionTask # @bacpypes_debugging def RecurringFunctionTask(interval, fn, *args, **kwargs): if _debug: RecurringFunctionTask._debug("RecurringFunctionTask %r %r %r", fn, args, kwargs) class _RecurringFunctionTask(RecurringTask): def __init__(self, interval): RecurringTask.__init__(self, interval) def process_task(self): if _debug: RecurringFunctionTask._debug("process_task %r %r %r", fn, args, kwargs) fn(*args, **kwargs) task = _RecurringFunctionTask(interval) if _debug: RecurringFunctionTask._debug(" - task: %r", task) return task # # recurring_function # @bacpypes_debugging def recurring_function(interval): def recurring_function_decorator(fn): class _RecurringFunctionTask(RecurringTask): def process_task(self): if _debug: recurring_function._debug("process_task %r", fn) fn() def __call__(self, *args, **kwargs): fn(*args, **kwargs) task = _RecurringFunctionTask(interval) task.install_task() return task return recurring_function_decorator # # TaskManager # # @bacpypes_debugging - implicit via metaclass class TaskManager(SingletonLogging): def __init__(self): if _debug: TaskManager._debug("__init__") global _task_manager, _unscheduled_tasks # initialize self.tasks = [] if _Trigger: self.trigger = _Trigger() else: self.trigger = None # task manager is this instance _task_manager = self # there may be tasks created that couldn't be scheduled # because a task manager wasn't created yet. if _unscheduled_tasks: for task in _unscheduled_tasks: task.install_task() def get_time(self): if _debug: TaskManager._debug("get_time") # return the real time return _time() def install_task(self, task): if _debug: TaskManager._debug("install_task %r @ %r", task, task.taskTime) # if the taskTime is None is hasn't been computed correctly if task.taskTime is None: raise RuntimeError("task time is None") # if this is already installed, suspend it if task.isScheduled: self.suspend_task(task) # save this in the task list heappush( self.tasks, (task.taskTime, task) ) if _debug: TaskManager._debug(" - tasks: %r", self.tasks) task.isScheduled = True # trigger the event if self.trigger: self.trigger.set() def suspend_task(self, task): if _debug: TaskManager._debug("suspend_task %r", task) # remove this guy for i, (when, curtask) in enumerate(self.tasks): if task is curtask: if _debug: TaskManager._debug(" - task found") del self.tasks[i] task.isScheduled = False heapify(self.tasks) break else: if _debug: TaskManager._debug(" - task not found") # trigger the event if self.trigger: self.trigger.set() def resume_task(self, task): if _debug: TaskManager._debug("resume_task %r", task) # just re-install it self.install_task(task) def get_next_task(self): """get the next task if there's one that should be processed, and return how long it will be until the next one should be processed.""" if _debug: TaskManager._debug("get_next_task") # get the time now = _time() task = None delta = None if self.tasks: # look at the first task when, nxttask = self.tasks[0] if when <= now: # pull it off the list and mark that it's no longer scheduled heappop(self.tasks) task = nxttask task.isScheduled = False if self.tasks: when, nxttask = self.tasks[0] # peek at the next task, return how long to wait delta = max(when - now, 0.0) else: delta = when - now # return the task to run and how long to wait for the next one return (task, delta) def process_task(self, task): if _debug: TaskManager._debug("process_task %r", task) # process the task task.process_task() # see if it should be rescheduled if isinstance(task, RecurringTask): task.install_task() elif isinstance(task, OneShotDeleteTask): del task PKF[bacpypes/vlan.py#!/usr/bin/python """ Virtual Local Area Network """ import random from copy import deepcopy from .errors import ConfigurationError from .debugging import ModuleLogger, bacpypes_debugging from .core import deferred from .pdu import Address from .comm import Server # some debugging _debug = 0 _log = ModuleLogger(globals()) # # Network # @bacpypes_debugging class Network: def __init__(self, dropPercent=0.0): if _debug: Network._debug("__init__ dropPercent=%r", dropPercent) self.nodes = [] self.dropPercent = dropPercent def add_node(self, node): """ Add a node to this network, let the node know which network it's on. """ if _debug: Network._debug("add_node %r", node) self.nodes.append(node) node.lan = self def remove_node(self, node): """ Remove a node from this network. """ if _debug: Network._debug("remove_node %r", node) self.nodes.remove(node) node.lan = None def process_pdu(self, pdu): """ Process a PDU by sending a copy to each node as dictated by the addressing and if a node is promiscuous. """ if _debug: Network._debug("process_pdu %r", pdu) if self.dropPercent != 0.0: if (random.random() * 100.0) < self.dropPercent: if _debug: Network._debug(" - packet dropped") return if not pdu.pduDestination or not isinstance(pdu.pduDestination, Address): raise RuntimeError("invalid destination address") elif pdu.pduDestination.addrType == Address.localBroadcastAddr: for n in self.nodes: if (pdu.pduSource != n.address): n.response(deepcopy(pdu)) elif pdu.pduDestination.addrType == Address.localStationAddr: for n in self.nodes: if n.promiscuous or (pdu.pduDestination == n.address): n.response(deepcopy(pdu)) else: raise RuntimeError("invalid destination address type") def __len__(self): """ Simple way to determine the number of nodes in the network. """ if _debug: Network._debug("__len__") return len(self.nodes) # # Node # @bacpypes_debugging class Node(Server): def __init__(self, addr, lan=None, promiscuous=False, spoofing=False, sid=None): if _debug: Node._debug("__init__ %r lan=%r promiscuous=%r spoofing=%r sid=%r", addr, lan, promiscuous, spoofing, sid ) Server.__init__(self, sid) if not isinstance(addr, Address): raise TypeError("addr must be an address") self.lan = None self.address = addr # bind to a lan if it was provided if lan: self.bind(lan) # might receive all packets and might spoof self.promiscuous = promiscuous self.spoofing = spoofing def bind(self, lan): """bind to a LAN.""" if _debug: Node._debug("bind %r", lan) lan.add_node(self) def indication(self, pdu): """Send a message.""" if _debug: Node._debug("indication %r", pdu) # make sure we're connected if not self.lan: raise ConfigurationError("unbound node") # if the pduSource is unset, fill in our address, otherwise # leave it alone to allow for simulated spoofing if pdu.pduSource is None: pdu.pduSource = self.address elif (not self.spoofing) and (pdu.pduSource != self.address): raise RuntimeError("spoofing address conflict") # actual network delivery is deferred deferred(self.lan.process_pdu, pdu) PKkFh}99bacpypes/bsllservice.py#!/usr/bin/python """ BACnet Streaming Link Layer Service """ import random from .debugging import ModuleLogger, DebugContents, bacpypes_debugging from .comm import Client, bind, ApplicationServiceElement from .tcp import TCPClientDirector, TCPServerDirector, StreamToPacket from .pdu import Address, LocalBroadcast, PDU, unpack_ip_addr from .npdu import NPDU from .netservice import NetworkAdapter from .bsll import AUTHENTICATION_FAILURE, AUTHENTICATION_HASH, \ AUTHENTICATION_NO_SERVICE, AUTHENTICATION_REQUIRED, AccessChallenge, \ AccessRequest, AccessResponse, BSLCI, BSLPDU, \ CLIENT_SERVER_SERVICE_ID, DEVICE_TO_DEVICE_SERVICE_ID, DeviceToDeviceAPDU, \ LANE_SERVICE_ID, NO_DEVICE_TO_DEVICE_SERVICE, \ NO_LANE_SERVICE, NO_PROXY_SERVICE, NO_ROUTER_TO_ROUTER_SERVICE, \ PROXY_SERVICE_ID, ProxyToServerBroadcastNPDU, ProxyToServerUnicastNPDU, \ ROUTER_TO_ROUTER_SERVICE_ID, Result, RouterToRouterNPDU, SUCCESS, \ ServerToProxyBroadcastNPDU, ServerToProxyUnicastNPDU, ServiceRequest, \ UNRECOGNIZED_SERVICE, bsl_pdu_types, hash_functions # some debugging _debug = 0 _log = ModuleLogger(globals()) # # _Packetize # @bacpypes_debugging def _Packetize(data): if _debug: _Packetize._debug("_Packetize %r", data) # look for the type field start_ind = data.find('\x83') if start_ind == -1: return None # chop off everything up to the start, it's garbage if start_ind > 0: if _debug: _Packetize._debug(" - garbage: %r", data[:start_ind]) data = data[start_ind:] # make sure we have at least a complete header if len(data) < 4: return None # get the length, make sure we have the whole packet total_len = (ord(data[2]) << 8) + ord(data[3]) if len(data) < total_len: return None packet_slice = (data[:total_len], data[total_len:]) if _debug: _Packetize._debug(" - packet_slice: %r", packet_slice) return packet_slice # # _StreamToPacket # @bacpypes_debugging class _StreamToPacket(StreamToPacket): def __init__(self): if _debug: _StreamToPacket._debug("__init__") StreamToPacket.__init__(self, _Packetize) def indication(self, pdu): if _debug: _StreamToPacket._debug("indication %r", pdu) self.request(pdu) # # UserInformation # @bacpypes_debugging class UserInformation(DebugContents): _debug_contents = ('username', 'password*', 'service', 'proxyNetwork') def __init__(self, **kwargs): if _debug: UserInformation._debug("__init__ %r", kwargs) # init from kwargs self.username = kwargs.get('username', None) self.password = kwargs.get('password', None) # init what services are available self.service = {} allServices = kwargs.get('allServices', False) self.service[DEVICE_TO_DEVICE_SERVICE_ID] = kwargs.get('deviceToDeviceService', allServices) self.service[ROUTER_TO_ROUTER_SERVICE_ID] = kwargs.get('routerToRouterService', allServices) self.service[PROXY_SERVICE_ID] = kwargs.get('proxyService', allServices) self.service[LANE_SERVICE_ID] = kwargs.get('laneService', allServices) self.service[CLIENT_SERVER_SERVICE_ID] = kwargs.get('clientServerService', allServices) # proxy service can map to a network self.proxyNetwork = kwargs.get('proxyNetwork', None) # # ConnectionState # @bacpypes_debugging class ConnectionState(DebugContents): NOT_AUTHENTICATED = 0 # no authentication attempted REQUESTED = 1 # access request sent to the server (client only) CHALLENGED = 2 # access challenge sent to the client (server only) AUTHENTICATED = 3 # authentication successful _debug_contents = ('address', 'service', 'connected', 'accessState', 'challenge', 'userinfo', 'proxyAdapter') def __init__(self, addr): if _debug: ConnectionState._debug("__init__ %r", addr) # save the address self.address = addr # this is not associated with a specific service self.service = None # start out disconnected until the service request is acked self.connected = False # access information self.accessState = ConnectionState.NOT_AUTHENTICATED self.challenge = None self.userinfo = None # reference to adapter used by proxy server service self.proxyAdapter = None # # ServiceAdapter # @bacpypes_debugging class ServiceAdapter: _authentication_required = False def __init__(self, mux): if _debug: ServiceAdapter._debug("__init__ %r", mux) # keep a reference to the multiplexer self.multiplexer = mux # each multiplex adapter keeps a dict of its connections self.connections = {} # update the multiplexer to reference this adapter if (self.serviceID == DEVICE_TO_DEVICE_SERVICE_ID): mux.deviceToDeviceService = self elif (self.serviceID == ROUTER_TO_ROUTER_SERVICE_ID): mux.routerToRouterService = self elif (self.serviceID == PROXY_SERVICE_ID): mux.proxyService = self elif (self.serviceID == LANE_SERVICE_ID): mux.laneService = self else: raise RuntimeError("invalid service ID: {0}".format(self.serviceID)) def authentication_required(self, addr): """Return True iff authentication is required for connection requests from the address.""" if _debug: ServiceAdapter._debug("authentication_required %r", addr) return self._authentication_required def get_default_user_info(self, addr): """Return a UserInformation object for trusted address->user authentication.""" if _debug: ServiceAdapter._debug("get_default_user_info %r", addr) # no users return None def get_user_info(self, username): """Return a UserInformation object or None.""" if _debug: ServiceAdapter._debug("get_user_info %r", username) # no users return None def add_connection(self, conn): if _debug: ServiceAdapter._debug("add_connection %r", conn) # keep track of this connection self.connections[conn.address] = conn # assume it is happily connected conn.service = self conn.connected = True def remove_connection(self, conn): if _debug: ServiceAdapter._debug("remove_connection %r", conn) try: del self.connections[conn.address] except KeyError: ServiceAdapter._warning("remove_connection: %r not a connection", conn) # clear out the connection attributes conn.service = None conn.connected = False def service_request(self, pdu): if _debug: ServiceAdapter._debug("service_request %r", pdu) # direct requests to the multiplexer self.multiplexer.indication(self, pdu) def service_confirmation(self, conn, pdu): raise NotImplementedError("service_confirmation must be overridden") # # NetworkServiceAdapter # @bacpypes_debugging class NetworkServiceAdapter(ServiceAdapter, NetworkAdapter): def __init__(self, mux, sap, net, cid=None): if _debug: NetworkServiceAdapter._debug("__init__ %r %r %r status=%r cid=%r", mux, sap, net, cid) ServiceAdapter.__init__(self, mux) NetworkAdapter.__init__(self, sap, net, cid) # # TCPServerMultiplexer # @bacpypes_debugging class TCPServerMultiplexer(Client): def __init__(self, addr=None): if _debug: TCPServerMultiplexer._debug("__init__ %r", addr) Client.__init__(self) # check for some options if addr is None: self.address = Address() self.addrTuple = ('', 47808) else: # allow the address to be cast if isinstance(addr, Address): self.address = addr else: self.address = Address(addr) # extract the tuple for binding self.addrTuple = self.address.addrTuple if _debug: TCPServerMultiplexer._debug(" - address: %r", self.address) TCPServerMultiplexer._debug(" - addrTuple: %r", self.addrTuple) # create and bind self.director = TCPServerDirector(self.addrTuple) bind(self, _StreamToPacket(), self.director) # create an application service element and bind self.ase = TCPMultiplexerASE(self) bind(self.ase, self.director) # keep a dictionary of connections self.connections = {} # no services available until they are created, they are # instances of ServiceAdapter self.deviceToDeviceService = None self.routerToRouterService = None self.proxyService = None self.laneService = None def request(self, pdu): if _debug: TCPServerMultiplexer._debug("request %r", pdu) # encode it as a BSLPDU xpdu = BSLPDU() pdu.encode(xpdu) if _debug: TCPServerMultiplexer._debug(" - xpdu: %r", xpdu) # encode it as a raw PDU ypdu = PDU() xpdu.encode(ypdu) ypdu.pduDestination = unpack_ip_addr(pdu.pduDestination.addrAddr) if _debug: TCPServerMultiplexer._debug(" - ypdu: %r", ypdu) # continue along Client.request(self, ypdu) def indication(self, server, pdu): if _debug: TCPServerMultiplexer._debug("indication %r %r", server, pdu) # pass through, it will be encoded self.request(pdu) def confirmation(self, pdu): if _debug: TCPServerMultiplexer._debug("confirmation %r", pdu) # recast from a comm.PDU to a BACpypes PDU pdu = PDU(pdu, source=Address(pdu.pduSource)) if _debug: TCPServerMultiplexer._debug(" - pdu: %r", pdu) # interpret as a BSLL PDU bslpdu = BSLPDU() bslpdu.decode(pdu) if _debug: TCPServerMultiplexer._debug(" - bslpdu: %r", bslpdu) # get the connection conn = self.connections.get(pdu.pduSource, None) if not conn: TCPServerMultiplexer._warning("no connection: %r", pdu.pduSource) return # extract the function for easy access fn = bslpdu.bslciFunction # get the class related to the function rpdu = bsl_pdu_types[fn]() rpdu.decode(bslpdu) if _debug: TCPServerMultiplexer._debug(" - rpdu: %r", rpdu) # redirect if (fn == BSLCI.result): TCPServerMultiplexer._warning("unexpected Result") # client is asking for a particular service elif (fn == BSLCI.serviceRequest): # if it is already connected, disconnect it if conn.service and conn.connected: conn.service.remove_connection(conn) newSAP = None resultCode = SUCCESS if rpdu.bslciServiceID == DEVICE_TO_DEVICE_SERVICE_ID: if self.deviceToDeviceService: newSAP = self.deviceToDeviceService else: resultCode = NO_DEVICE_TO_DEVICE_SERVICE elif rpdu.bslciServiceID == ROUTER_TO_ROUTER_SERVICE_ID: if self.routerToRouterService: newSAP = self.routerToRouterService else: resultCode = NO_ROUTER_TO_ROUTER_SERVICE elif rpdu.bslciServiceID == PROXY_SERVICE_ID: if self.proxyService: newSAP = self.proxyService else: resultCode = NO_PROXY_SERVICE elif rpdu.bslciServiceID == LANE_SERVICE_ID: if self.laneService: newSAP = self.laneService else: resultCode = NO_LANE_SERVICE else: resultCode = UNRECOGNIZED_SERVICE # success means the service requested is supported if resultCode: response = Result(resultCode) response.pduDestination = rpdu.pduSource self.request(response) return # check to see if authentication is required if not newSAP.authentication_required(conn.address): newSAP.add_connection(conn) else: # if there is no userinfo, try to get default userinfo if not conn.userinfo: conn.userinfo = newSAP.get_default_user_info(conn.address) if conn.userinfo: conn.accessState = ConnectionState.AUTHENTICATED if _debug: TCPServerMultiplexer._debug(" - authenticated to default user info: %r", conn.userinfo) else: if _debug: TCPServerMultiplexer._debug(" - no default user info") # check if authentication has occurred if not conn.accessState == ConnectionState.AUTHENTICATED: resultCode = AUTHENTICATION_REQUIRED # save a reference to the service to use when authenticated conn.service = newSAP # make sure the user can use the service elif not conn.userinfo.service[newSAP.serviceID]: resultCode = AUTHENTICATION_NO_SERVICE # all's well else: newSAP.add_connection(conn) response = Result(resultCode) response.pduDestination = rpdu.pduSource self.request(response) elif (fn == BSLCI.deviceToDeviceAPDU) and self.deviceToDeviceService: if conn.service is not self.deviceToDeviceService: TCPServerMultiplexer._warning("not connected to appropriate service") return self.deviceToDeviceService.service_confirmation(conn, rpdu) elif (fn == BSLCI.routerToRouterNPDU) and self.routerToRouterService: if conn.service is not self.routerToRouterService: TCPServerMultiplexer._warning("not connected to appropriate service") return self.routerToRouterService.service_confirmation(conn, rpdu) elif (fn == BSLCI.proxyToServerUnicastNPDU) and self.proxyService: if conn.service is not self.proxyService: TCPServerMultiplexer._warning("not connected to appropriate service") return self.proxyService.service_confirmation(conn, rpdu) elif (fn == BSLCI.proxyToServerBroadcastNPDU) and self.proxyService: if conn.service is not self.proxyService: TCPServerMultiplexer._warning("not connected to appropriate service") return self.proxyService.service_confirmation(conn, rpdu) elif (fn == BSLCI.serverToProxyUnicastNPDU) and self.proxyService: TCPServerMultiplexer._warning("unexpected Server-To-Proxy-Unicast-NPDU") elif (fn == BSLCI.serverToProxyBroadcastNPDU) and self.proxyService: TCPServerMultiplexer._warning("unexpected Server-To-Proxy-Broadcast-NPDU") elif (fn == BSLCI.clientToLESUnicastNPDU) and self.laneService: if conn.service is not self.laneService: TCPServerMultiplexer._warning("not connected to appropriate service") return self.laneService.service_confirmation(conn, rpdu) elif (fn == BSLCI.clientToLESBroadcastNPDU) and self.laneService: if conn.service is not self.laneService: TCPServerMultiplexer._warning("not connected to appropriate service") return self.laneService.service_confirmation(conn, rpdu) elif (fn == BSLCI.lesToClientUnicastNPDU) and self.laneService: TCPServerMultiplexer._warning("unexpected LES-to-Client-Unicast-NPDU") elif (fn == BSLCI.lesToClientBroadcastNPDU) and self.laneService: TCPServerMultiplexer._warning("unexpected LES-to-Client-Broadcast-NPDU") elif (fn == BSLCI.accessRequest): self.do_AccessRequest(conn, rpdu) elif (fn == BSLCI.accessChallenge): TCPServerMultiplexer._warning("unexpected Access-Challenge") elif (fn == BSLCI.accessResponse): self.do_AccessResponse(conn, rpdu) else: TCPServerMultiplexer._warning("unsupported message") def do_AccessRequest(self, conn, bslpdu): if _debug: TCPServerMultiplexer._debug("do_AccessRequest %r %r", conn, bslpdu) # make sure this connection has requested a service first if not conn.service: if _debug: TCPServerMultiplexer._debug(" - request a service first") response = Result(AUTHENTICATION_NO_SERVICE) response.pduDestination = bslpdu.pduSource self.request(response) return # make sure this process isn't being repeated more than once for the connection if conn.accessState != ConnectionState.NOT_AUTHENTICATED: if _debug: TCPServerMultiplexer._debug(" - connection in the wrong state: %r", conn.accessState) response = Result(AUTHENTICATION_FAILURE) response.pduDestination = bslpdu.pduSource self.request(response) return # get the hash function try: hashFn = hash_functions[bslpdu.bslciHashFn] except: if _debug: TCPServerMultiplexer._debug(" - no hash function: %r", bslpdu.bslciHashFn) response = Result(AUTHENTICATION_HASH) response.pduDestination = bslpdu.pduSource self.request(response) return # get the userinfo from the service conn.userinfo = conn.service.get_user_info(bslpdu.bslciUsername) if not conn.userinfo: if _debug: TCPServerMultiplexer._debug(" - no user info: %r", bslpdu.bslciUsername) response = Result(AUTHENTICATION_FAILURE) response.pduDestination = bslpdu.pduSource self.request(response) return # build a challenge string, save it in the connection challenge = hashFn(''.join(chr(random.randrange(256)) for i in range(128))) conn.challenge = challenge # save that we have issued a challenge conn.accessState = ConnectionState.CHALLENGED # conn.userinfo is authentication information, build a challenge response and send it back response = AccessChallenge(bslpdu.bslciHashFn, challenge) response.pduDestination = conn.address self.request(response) def do_AccessResponse(self, conn, bslpdu): if _debug: TCPServerMultiplexer._debug("do_AccessResponse %r %r", conn, bslpdu) # start out happy resultCode = SUCCESS # if there's no user, fail if not conn.userinfo: if _debug: TCPServerMultiplexer._debug(" - connection has no user info") resultCode = AUTHENTICATION_FAILURE # make sure a challenge has been issued elif conn.accessState != ConnectionState.CHALLENGED: if _debug: TCPServerMultiplexer._debug(" - connection in the wrong state: %r", conn.accessState) resultCode = AUTHENTICATION_FAILURE else: # get the hash function try: hashFn = hash_functions[bslpdu.bslciHashFn] except: if _debug: TCPServerMultiplexer._debug(" - no hash function: %r", bslpdu.bslciHashFn) response = Result(AUTHENTICATION_HASH) response.pduDestination = bslpdu.pduSource self.request(response) return # take the password, the challenge, and hash them challengeResponse = hashFn(conn.userinfo.password + conn.challenge) # see if the response matches what we think it should be if challengeResponse == bslpdu.bslciResponse: if _debug: TCPServerMultiplexer._debug(" - success") # connection is now authenticated conn.accessState = ConnectionState.AUTHENTICATED # we may have gone through authentication without requesting a service if not conn.service: if _debug: TCPServerMultiplexer._debug(" - no service") # make sure the user can use the service elif not conn.userinfo.service[conn.service.serviceID]: # break the reference to the service resultCode = AUTHENTICATION_NO_SERVICE conn.service = None else: # all's well conn.service.add_connection(conn) else: if _debug: TCPServerMultiplexer._debug(" - challenge/response mismatch") resultCode = AUTHENTICATION_FAILURE response = Result(resultCode) response.pduDestination = bslpdu.pduSource self.request(response) # # TCPClientMultiplexer # @bacpypes_debugging class TCPClientMultiplexer(Client): def __init__(self): if _debug: TCPClientMultiplexer._debug("__init__") Client.__init__(self) # create and bind self.director = TCPClientDirector() bind(self, _StreamToPacket(), self.director) # create an application service element and bind self.ase = TCPMultiplexerASE(self) bind(self.ase, self.director) # keep a dictionary of connections self.connections = {} # no services available until they are created, they are # instances of ServiceAdapter self.deviceToDeviceService = None self.routerToRouterService = None self.proxyService = None self.laneService = None def request(self, pdu): if _debug: TCPClientMultiplexer._debug("request %r", pdu) # encode it as a BSLPDU xpdu = BSLPDU() pdu.encode(xpdu) if _debug: TCPClientMultiplexer._debug(" - xpdu: %r", xpdu) # encode it as a raw PDU ypdu = PDU() xpdu.encode(ypdu) ypdu.pduDestination = unpack_ip_addr(pdu.pduDestination.addrAddr) if _debug: TCPClientMultiplexer._debug(" - ypdu: %r", ypdu) # continue along Client.request(self, ypdu) def indication(self, server, pdu): if _debug: TCPClientMultiplexer._debug("indication %r %r", server, pdu) # pass through, it will be encoded self.request(pdu) def confirmation(self, pdu): if _debug: TCPClientMultiplexer._debug("confirmation %r", pdu) # recast from a comm.PDU to a BACpypes PDU pdu = PDU(pdu, source=Address(pdu.pduSource)) # interpret as a BSLL PDU bslpdu = BSLPDU() bslpdu.decode(pdu) if _debug: TCPClientMultiplexer._debug(" - bslpdu: %r", bslpdu) # get the connection conn = self.connections.get(pdu.pduSource, None) if not conn: TCPClientMultiplexer._warning("no connection: %r", pdu.pduSource) return # extract the function for easy access fn = bslpdu.bslciFunction # get the class related to the function rpdu = bsl_pdu_types[fn]() rpdu.decode(bslpdu) if _debug: TCPClientMultiplexer._debug(" - rpdu: %r", rpdu) # redirect if (fn == BSLCI.result): # if the connection is not associated with a service, toss it if not conn.service: TCPClientMultiplexer._warning("unexpected result") return # if it is already connected, stop if conn.connected: TCPClientMultiplexer._warning("unexpected result, already connected") return # if this is successful, add it to the service if rpdu.bslciResultCode == SUCCESS: # if authentication was required, change to authenticate when this ack comes back if conn.accessState == ConnectionState.REQUESTED: if _debug: TCPClientMultiplexer._debug(" - authentication successful") conn.accessState = ConnectionState.AUTHENTICATED # add the connection to the service conn.service.add_connection(conn) # let the service process the ack conn.service.connect_ack(conn, rpdu) # if authentication is required, start the process elif rpdu.bslciResultCode == AUTHENTICATION_REQUIRED: # make sure this process isn't being repeated more than once for the connection if conn.accessState != ConnectionState.NOT_AUTHENTICATED: TCPClientMultiplexer._warning("unexpected authentication required") return conn.userinfo = conn.service.get_default_user_info(conn.address) if not conn.userinfo: TCPClientMultiplexer._warning("authentication required, no user information") return # set the connection state conn.accessState = ConnectionState.REQUESTED # send the username response = AccessRequest(0, conn.userinfo.username) response.pduDestination = rpdu.pduSource self.request(response) else: TCPClientMultiplexer._warning("result code: %r", rpdu.bslciResultCode) elif (fn == BSLCI.serviceRequest): TCPClientMultiplexer._warning("unexpected service request") elif (fn == BSLCI.deviceToDeviceAPDU) and self.deviceToDeviceService: if conn.service is not self.deviceToDeviceService: TCPClientMultiplexer._warning("not connected to appropriate service") return self.deviceToDeviceService.service_confirmation(conn, rpdu) elif (fn == BSLCI.routerToRouterNPDU) and self.routerToRouterService: if conn.service is not self.routerToRouterService: TCPClientMultiplexer._warning("not connected to appropriate service") return self.routerToRouterService.service_confirmation(conn, rpdu) elif (fn == BSLCI.proxyToServerUnicastNPDU) and self.proxyService: TCPClientMultiplexer._warning("unexpected Proxy-To-Server-Unicast-NPDU") elif (fn == BSLCI.proxyToServerBroadcastNPDU) and self.proxyService: TCPClientMultiplexer._warning("unexpected Proxy-To-Broadcast-Unicast-NPDU") elif (fn == BSLCI.serverToProxyUnicastNPDU) and self.proxyService: if conn.service is not self.proxyService: TCPClientMultiplexer._warning("not connected to appropriate service") return self.proxyService.service_confirmation(conn, rpdu) elif (fn == BSLCI.serverToProxyBroadcastNPDU) and self.proxyService: if conn.service is not self.proxyService: TCPClientMultiplexer._warning("not connected to appropriate service") return self.proxyService.service_confirmation(conn, rpdu) elif (fn == BSLCI.clientToLESUnicastNPDU) and self.laneService: TCPClientMultiplexer._warning("unexpected Client-to-LES-Unicast-NPDU") elif (fn == BSLCI.clientToLESBroadcastNPDU) and self.laneService: TCPClientMultiplexer._warning("unexpected Client-to-LES-Broadcast-NPDU") elif (fn == BSLCI.lesToClientUnicastNPDU) and self.laneService: if conn.service is not self.laneService: TCPClientMultiplexer._warning("not connected to appropriate service") return self.laneService.service_confirmation(conn, rpdu) elif (fn == BSLCI.lesToClientBroadcastNPDU) and self.laneService: if conn.service is not self.laneService: TCPClientMultiplexer._warning("not connected to appropriate service") return self.laneService.service_confirmation(conn, rpdu) elif (fn == BSLCI.accessRequest): TCPClientMultiplexer._warning("unexpected Access-request") elif (fn == BSLCI.accessChallenge): self.do_AccessChallenge(conn, rpdu) elif (fn == BSLCI.accessResponse): TCPClientMultiplexer._warning("unexpected Access-response") else: TCPClientMultiplexer._warning("unsupported message: %s", rpdu.__class__.__name__) def do_AccessChallenge(self, conn, bslpdu): if _debug: TCPClientMultiplexer._debug("do_AccessRequest %r %r", conn, bslpdu) # make sure this process isn't being repeated more than once for the connection if conn.accessState != ConnectionState.REQUESTED: TCPClientMultiplexer._warning("unexpected access challenge") return # get the hash function try: hashFn = hash_functions[bslpdu.bslciHashFn] except: TCPClientMultiplexer._warning("no hash function: %r", bslpdu.bslciHashFn) return # take the password, the challenge, and hash them challengeResponse = hashFn(conn.userinfo.password + bslpdu.bslciChallenge) # conn.userinfo is authentication information, build a challenge response and send it back response = AccessResponse(bslpdu.bslciHashFn, challengeResponse) response.pduDestination = conn.address if _debug: TCPClientMultiplexer._debug(" - response: %r", response) self.request(response) # # TCPMultiplexerASE # @bacpypes_debugging class TCPMultiplexerASE(ApplicationServiceElement): def __init__(self, mux): if _debug: TCPMultiplexerASE._debug("__init__ %r", mux) # keep track of the multiplexer self.multiplexer = mux def indication(self, *args, **kwargs): if _debug: TCPMultiplexerASE._debug('TCPMultiplexerASE %r %r', args, kwargs) if 'addPeer' in kwargs: addr = Address(kwargs['addPeer']) if _debug: TCPMultiplexerASE._debug(" - add peer: %r", addr) if addr in self.multiplexer.connections: if _debug: TCPMultiplexerASE._debug(" - already a connection") return conn = ConnectionState(addr) if _debug: TCPMultiplexerASE._debug(" - conn: %r", conn) # add it to the multiplexer connections self.multiplexer.connections[addr] = conn if 'delPeer' in kwargs: addr = Address(kwargs['delPeer']) if _debug: TCPMultiplexerASE._info(" - delete peer: %r", addr) if addr not in self.multiplexer.connections: if _debug: TCPMultiplexerASE._debug(" - not a connection") return # get the connection conn = self.multiplexer.connections.get(addr) if _debug: TCPMultiplexerASE._debug(" - conn: %r", conn) # if it is associated and connected, disconnect it if conn.service and conn.connected: conn.service.remove_connection(conn) # remove it from the multiplexer del self.multiplexer.connections[addr] # # DeviceToDeviceServerService # @bacpypes_debugging class DeviceToDeviceServerService(NetworkServiceAdapter): serviceID = DEVICE_TO_DEVICE_SERVICE_ID def process_npdu(self, npdu): """encode NPDUs from the service access point and send them downstream.""" if _debug: DeviceToDeviceServerService._debug("process_npdu %r", npdu) # broadcast messages go to peers if npdu.pduDestination.addrType == Address.localBroadcastAddr: destList = self.connections.keys() else: if npdu.pduDestination not in self.connections: if _debug: DeviceToDeviceServerService._debug(" - not a connected client") return destList = [npdu.pduDestination] if _debug: DeviceToDeviceServerService._debug(" - destList: %r", destList) for dest in destList: # make device-to-device APDU xpdu = DeviceToDeviceAPDU(npdu) xpdu.pduDestination = dest # send it down to the multiplexer self.service_request(xpdu) def service_confirmation(self, conn, pdu): if _debug: DeviceToDeviceServerService._debug("service_confirmation %r %r", conn, pdu) # build an NPDU npdu = NPDU(pdu.pduData) npdu.pduSource = pdu.pduSource if _debug: DeviceToDeviceServerService._debug(" - npdu: %r", npdu) # send it to the service access point for processing self.adapterSAP.process_npdu(self, npdu) # # DeviceToDeviceClientService # @bacpypes_debugging class DeviceToDeviceClientService(NetworkServiceAdapter): serviceID = DEVICE_TO_DEVICE_SERVICE_ID def process_npdu(self, npdu): """encode NPDUs from the service access point and send them downstream.""" if _debug: DeviceToDeviceClientService._debug("process_npdu %r", npdu) # broadcast messages go to everyone if npdu.pduDestination.addrType == Address.localBroadcastAddr: destList = self.connections.keys() else: conn = self.connections.get(npdu.pduDestination, None) if not conn: if _debug: DeviceToDeviceClientService._debug(" - not a connected client") # start a connection attempt conn = self.connect(npdu.pduDestination) if not conn.connected: # keep a reference to this npdu to send after the ack comes back conn.pendingNPDU.append(npdu) return destList = [npdu.pduDestination] if _debug: DeviceToDeviceClientService._debug(" - destList: %r", destList) for dest in destList: # make device-to-device APDU xpdu = DeviceToDeviceAPDU(npdu) xpdu.pduDestination = dest # send it down to the multiplexer self.service_request(xpdu) def connect(self, addr): """Initiate a connection request to the device.""" if _debug: DeviceToDeviceClientService._debug("connect %r", addr) # make a connection conn = ConnectionState(addr) self.multiplexer.connections[addr] = conn # associate with this service, but it is not connected until the ack comes back conn.service = self # keep a list of pending NPDU objects until the ack comes back conn.pendingNPDU = [] # build a service request request = ServiceRequest(DEVICE_TO_DEVICE_SERVICE_ID) request.pduDestination = addr # send it self.service_request(request) # return the connection object return conn def connect_ack(self, conn, pdu): if _debug: DeviceToDeviceClientService._debug("connect_ack %r %r", conn, pdu) # if the response is good, consider it connected if pdu.bslciResultCode == 0: # send the pending NPDU if there is one if conn.pendingNPDU: for npdu in conn.pendingNPDU: # make device-to-device APDU xpdu = DeviceToDeviceAPDU(npdu) xpdu.pduDestination = npdu.pduDestination # send it down to the multiplexer self.service_request(xpdu) conn.pendingNPDU = [] else: pass def service_confirmation(self, conn, pdu): if _debug: DeviceToDeviceClientService._debug("service_confirmation %r %r", conn, pdu) # build an NPDU npdu = NPDU(pdu.pduData) npdu.pduSource = pdu.pduSource if _debug: DeviceToDeviceClientService._debug(" - npdu: %r", npdu) # send it to the service access point for processing self.adapterSAP.process_npdu(self, npdu) # # RouterToRouterService # @bacpypes_debugging class RouterToRouterService(NetworkServiceAdapter): serviceID = ROUTER_TO_ROUTER_SERVICE_ID def process_npdu(self, npdu): """encode NPDUs from the service access point and send them downstream.""" if _debug: RouterToRouterService._debug("process_npdu %r", npdu) # encode the npdu as if it was about to be delivered to the network pdu = PDU() npdu.encode(pdu) if _debug: RouterToRouterService._debug(" - pdu: %r", pdu) # broadcast messages go to everyone if pdu.pduDestination.addrType == Address.localBroadcastAddr: destList = self.connections.keys() else: conn = self.connections.get(pdu.pduDestination, None) if not conn: if _debug: RouterToRouterService._debug(" - not a connected client") # start a connection attempt conn = self.connect(pdu.pduDestination) if not conn.connected: # keep a reference to this pdu to send after the ack comes back conn.pendingNPDU.append(pdu) return destList = [pdu.pduDestination] if _debug: RouterToRouterService._debug(" - destList: %r", destList) for dest in destList: # make a router-to-router NPDU xpdu = RouterToRouterNPDU(pdu) xpdu.pduDestination = dest # send it to the multiplexer self.service_request(xpdu) def connect(self, addr): """Initiate a connection request to the peer router.""" if _debug: RouterToRouterService._debug("connect %r", addr) # make a connection conn = ConnectionState(addr) self.multiplexer.connections[addr] = conn # associate with this service, but it is not connected until the ack comes back conn.service = self # keep a list of pending NPDU objects until the ack comes back conn.pendingNPDU = [] # build a service request request = ServiceRequest(ROUTER_TO_ROUTER_SERVICE_ID) request.pduDestination = addr # send it self.service_request(request) # return the connection object return conn def connect_ack(self, conn, pdu): if _debug: RouterToRouterService._debug("connect_ack %r %r", conn, pdu) # if the response is good, consider it connected if pdu.bslciResultCode == 0: # send the pending NPDU if there is one if conn.pendingNPDU: for npdu in conn.pendingNPDU: # make router-to-router NPDU xpdu = RouterToRouterNPDU(npdu) xpdu.pduDestination = npdu.pduDestination # send it down to the multiplexer self.service_request(xpdu) conn.pendingNPDU = [] else: pass def add_connection(self, conn): if _debug: RouterToRouterService._debug("add_connection %r", conn) # first do the usual things NetworkServiceAdapter.add_connection(self, conn) # generate a Who-Is-Router-To-Network, all networks # send it to the client def remove_connection(self, conn): if _debug: RouterToRouterService._debug("remove_connection %r", conn) # first to the usual thing NetworkServiceAdapter.remove_connection(self, conn) # the NSAP needs routing table information related to this connection flushed self.adapterSAP.remove_router_references(self, conn.address) def service_confirmation(self, conn, pdu): if _debug: RouterToRouterService._debug("service_confirmation %r %r", conn, pdu) # decode it, the nework layer needs NPDUs npdu = NPDU() npdu.decode(pdu) npdu.pduSource = pdu.pduSource if _debug: ProxyServiceNetworkAdapter._debug(" - npdu: %r", npdu) # send it to the service access point for processing self.adapterSAP.process_npdu(self, npdu) # # ProxyServiceNetworkAdapter # @bacpypes_debugging class ProxyServiceNetworkAdapter(NetworkAdapter): def __init__(self, conn, sap, net, cid=None): if _debug: ProxyServiceNetworkAdapter._debug("__init__ %r %r %r status=0 cid=%r", conn, sap, net, cid) NetworkAdapter.__init__(self, sap, net, cid) # save the connection self.conn = conn def process_npdu(self, npdu): """encode NPDUs from the network service access point and send them to the proxy.""" if _debug: ProxyServiceNetworkAdapter._debug("process_npdu %r", npdu) # encode the npdu as if it was about to be delivered to the network pdu = PDU() npdu.encode(pdu) if _debug: ProxyServiceNetworkAdapter._debug(" - pdu: %r", pdu) # broadcast messages go to peers if pdu.pduDestination.addrType == Address.localBroadcastAddr: xpdu = ServerToProxyBroadcastNPDU(pdu) else: xpdu = ServerToProxyUnicastNPDU(pdu.pduDestination, pdu) # the connection has the correct address xpdu.pduDestination = self.conn.address # send it down to the multiplexer self.conn.service.service_request(xpdu) def service_confirmation(self, bslpdu): """Receive packets forwarded by the proxy and send them upstream to the network service access point.""" if _debug: ProxyServiceNetworkAdapter._debug("service_confirmation %r", bslpdu) # build a PDU pdu = NPDU(bslpdu.pduData) # the source is from the original source, not the proxy itself pdu.pduSource = bslpdu.bslciAddress # if the proxy received a broadcast, send it upstream as a broadcast if isinstance(bslpdu, ProxyToServerBroadcastNPDU): pdu.pduDestination = LocalBroadcast() if _debug: ProxyServiceNetworkAdapter._debug(" - pdu: %r", pdu) # decode it, the nework layer needs NPDUs npdu = NPDU() npdu.decode(pdu) if _debug: ProxyServiceNetworkAdapter._debug(" - npdu: %r", npdu) # send it to the service access point for processing self.adapterSAP.process_npdu(self, npdu) # # ProxyServerService # @bacpypes_debugging class ProxyServerService(ServiceAdapter): serviceID = PROXY_SERVICE_ID def __init__(self, mux, nsap): if _debug: ProxyServerService._debug("__init__ %r %r", mux, nsap) ServiceAdapter.__init__(self, mux) # save a reference to the network service access point self.nsap = nsap def add_connection(self, conn): if _debug: ProxyServerService._debug("add_connection %r", conn) # add as usual ServiceAdapter.add_connection(self, conn) # create a proxy adapter conn.proxyAdapter = ProxyServiceNetworkAdapter(conn, self.nsap, conn.userinfo.proxyNetwork) if _debug: ProxyServerService._debug(" - proxyAdapter: %r", conn.proxyAdapter) def remove_connection(self, conn): if _debug: ProxyServerService._debug("remove_connection %r", conn) # remove as usual ServiceAdapter.remove_connection(self, conn) # remove the adapter from the list of adapters for the nsap self.nsap.adapters.remove(conn.proxyAdapter) def service_confirmation(self, conn, bslpdu): """Receive packets forwarded by the proxy and redirect them to the proxy network adapter.""" if _debug: ProxyServerService._debug("service_confirmation %r %r", conn, bslpdu) # make sure there is an adapter for it - or something went wrong if not getattr(conn, 'proxyAdapter', None): raise RuntimeError("service confirmation received but no adapter for it") # forward along conn.proxyAdapter.service_confirmation(bslpdu) # # ProxyClientService # @bacpypes_debugging class ProxyClientService(ServiceAdapter, Client): serviceID = PROXY_SERVICE_ID def __init__(self, mux, addr=None, userinfo=None, cid=None): if _debug: ProxyClientService._debug("__init__ %r %r userinfo=%r cid=%r", mux, addr, userinfo, cid) ServiceAdapter.__init__(self, mux) Client.__init__(self, cid) # save the address of the server and the userinfo self.address = addr self.userinfo = userinfo def get_default_user_info(self, addr): """get the user information to authenticate.""" if _debug: ProxyClientService._debug("get_default_user_info %r", addr) return self.userinfo def connect(self, addr=None, userinfo=None): """Initiate a connection request to the device.""" if _debug: ProxyClientService._debug("connect addr=%r", addr) # if the address was provided, use it if addr: self.address = addr else: addr = self.address # if the user was provided, save it if userinfo: self.userinfo = userinfo # make a connection conn = ConnectionState(addr) self.multiplexer.connections[addr] = conn if _debug: ProxyClientService._debug(" - conn: %r", conn) # associate with this service, but it is not connected until the ack comes back conn.service = self # keep a list of pending BSLPDU objects until the ack comes back conn.pendingBSLPDU = [] # build a service request request = ServiceRequest(PROXY_SERVICE_ID) request.pduDestination = addr # send it self.service_request(request) # return the connection object return conn def connect_ack(self, conn, bslpdu): if _debug: ProxyClientService._debug("connect_ack %r %r", conn, bslpdu) # if the response is good, consider it connected if bslpdu.bslciResultCode == 0: # send the pending NPDU if there is one if conn.pendingBSLPDU: for pdu in conn.pendingBSLPDU: # send it down to the multiplexer self.service_request(pdu) conn.pendingBSLPDU = [] else: ProxyClientService._warning("connection nack: %r", bslpdu.bslciResultCode) def service_confirmation(self, conn, bslpdu): if _debug: ProxyClientService._debug("service_confirmation %r %r", conn, bslpdu) # build a PDU pdu = PDU(bslpdu) if isinstance(bslpdu, ServerToProxyUnicastNPDU): pdu.pduDestination = bslpdu.bslciAddress elif isinstance(bslpdu, ServerToProxyBroadcastNPDU): pdu.pduDestination = LocalBroadcast() if _debug: ProxyClientService._debug(" - pdu: %r", pdu) # send it downstream self.request(pdu) def confirmation(self, pdu): if _debug: ProxyClientService._debug("confirmation %r ", pdu) # we should at least have an address if not self.address: raise RuntimeError("no connection address") # build a bslpdu if pdu.pduDestination.addrType == Address.localBroadcastAddr: request = ProxyToServerBroadcastNPDU(pdu.pduSource, pdu) else: request = ProxyToServerUnicastNPDU(pdu.pduSource, pdu) request.pduDestination = self.address # make sure there is a connection conn = self.connections.get(self.address, None) if not conn: if _debug: ProxyClientService._debug(" - not a connected client") # start a connection attempt conn = self.connect() # if the connection is not connected, queue it, othersize send it if not conn.connected: # keep a reference to this npdu to send after the ack comes back conn.pendingBSLPDU.append(request) else: # send it self.service_request(request) PKHMae)bacpypes-0.13.8.dist-info/DESCRIPTION.rstBACpypes provides a BACnet application layer and network layer written in Python for daemons, scripting, and graphical interfaces. PKH x99'bacpypes-0.13.8.dist-info/metadata.json{"classifiers": ["Development Status :: 2 - Pre-Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Natural Language :: English", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.5", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4"], "extensions": {"python.details": {"contacts": [{"email": "joel@carrickbender.com", "name": "Joel Bender", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/JoelBender/bacpypes"}}}, "generator": "bdist_wheel (0.26.0)", "license": "MIT", "metadata_version": "2.0", "name": "bacpypes", "summary": "BACnet Communications Library", "test_requires": [{"requires": ["nose"]}], "version": "0.13.8"}PKHҮ 'bacpypes-0.13.8.dist-info/top_level.txtbacpypes PKH}\\bacpypes-0.13.8.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.26.0) Root-Is-Purelib: true Tag: py3-none-any PKH`K\"bacpypes-0.13.8.dist-info/METADATAMetadata-Version: 2.0 Name: bacpypes Version: 0.13.8 Summary: BACnet Communications Library Home-page: https://github.com/JoelBender/bacpypes Author: Joel Bender Author-email: joel@carrickbender.com License: MIT Platform: UNKNOWN Classifier: Development Status :: 2 - Pre-Alpha Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Natural Language :: English Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.5 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 BACpypes provides a BACnet application layer and network layer written in Python for daemons, scripting, and graphical interfaces. PKH   bacpypes-0.13.8.dist-info/RECORDbacpypes/__init__.py,sha256=E2fMFk4Qg9ahMbFhY96BX0AOa0KFNqM8_STIinJ8vlk,1008 bacpypes/analysis.py,sha256=YXR6FjXRdOGH1xPW3lHAs_jVOAVnf13XurH24ONfgrw,12585 bacpypes/apdu.py,sha256=sBFCje73-t5Lgrd12MQEY1AYYzmobF8ADH_jUrWRBgo,52927 bacpypes/app.py,sha256=OntaqOSoP9n24_YYrY9LHyPwpK4d6Ztd4Q4-007-l9E,28079 bacpypes/appservice.py,sha256=OGoSvdgW50cdekx6U7gNcKPrgHyYx3xxjYbXyMQEE8c,52810 bacpypes/basetypes.py,sha256=VKHS5xM-SRpoWnKUGsZIuQRLlCvtAXYGdtsd__T5pLw,72397 bacpypes/bsll.py,sha256=CBQodJu7-K9TB2VopEKyMxbt7XgrQxDoVESq4pchrbM,24122 bacpypes/bsllservice.py,sha256=0xWwV6IEEwiPvg6hj58P04A17DpA3-BSLqDq9eMJXhU,48185 bacpypes/bvll.py,sha256=Xjf3NgW0Fjq6X_IxFjoKnCsSpMWF4jsIqMdDFt8fNI8,20588 bacpypes/bvllservice.py,sha256=3dRjk8Esn1mLpD7vZNh2qBjbSDmhrHE2E0UwWR2e8Q4,30976 bacpypes/comm.py,sha256=R6cMZjmPx4FJI9aueNdl6Rfe0HQ-D-8d1EUnOu5NeFM,18805 bacpypes/commandlogging.py,sha256=9CXMp2ipFtNhB_ELNN3S4IoLbrz21Xo889tuUlrroNY,5087 bacpypes/console.py,sha256=0unITTREnOkJ-4KCMw_2sykEkYtkCVtD4zTLBK7fsrY,2589 bacpypes/consolecmd.py,sha256=cJp4z8eE47o7tGUvMmGovQ41XL5aFUP-FB_eJXiYkp8,9779 bacpypes/consolelogging.py,sha256=8MIzrH-f4p_pvK-1j3poVo1hY0byUZvhxgSbeVw38Pc,5589 bacpypes/constructeddata.py,sha256=7olIPixZTck8TBG1bfE0kt-auA1i58oxtpT1a9yQI5o,43307 bacpypes/core.py,sha256=eKztVQQbBoLlvG6_v0Qun-hBpiWRiOw9yJNQFT1aZRM,6053 bacpypes/debugging.py,sha256=irVar6BmH6AQvCQL7D3kUH4HVAc9bmh9TE8hiOmbYqI,9449 bacpypes/errors.py,sha256=DjJU9dSjURdcRPjxrP-mAW1E4SikKIrsecCDh2NvU9Y,8200 bacpypes/event.py,sha256=cGz9Pwb5MBUBLspZfo61Bf0UbXuIl4PyDDdL0WWeTQU,1794 bacpypes/netservice.py,sha256=JyzYIh_x1ELpcc1iRvarHoCixziCRie02SD6MOzLRno,28129 bacpypes/npdu.py,sha256=SU8n9sa7N8P7t938_4nW-4Ricav7DgKW2JtCh7ILUlI,23797 bacpypes/object.py,sha256=xVQ417OLkCtiyDdq11L2oEv1Z9b_ESRwOnW6UuDeobY,94032 bacpypes/pdu.py,sha256=kGtHxgmhcsQvDc8HLwtATW5IhQbkZ9Cof91u_kNiG-s,17700 bacpypes/primitivedata.py,sha256=uQJ6yV2HCXyUIhEAnzRbt74p2layAOWFXWly2stqJj4,48182 bacpypes/singleton.py,sha256=yUhBaT8S3Zn1TY9gmNbe3uDRrd8J9L99ttkz2RKHtTU,1905 bacpypes/task.py,sha256=zk_yVjDYH7PmSwLFAnLx2ABTsQMPcQcm9L2zS8B2l5Q,9801 bacpypes/tcp.py,sha256=GJNWW02yESXEH9BA-UQnG67iqoOU7GRkVLwk_QqK6kk,23800 bacpypes/udp.py,sha256=Cz0nCt2lZ1CNZh0NDo0t6lbGv0Ve3XkP5rsAJLOeRTw,7231 bacpypes/vlan.py,sha256=wP22wb5RosRvnWoWjzYn13wUs7jjfJg9vslJLZ6tKm4,3723 bacpypes-0.13.8.dist-info/DESCRIPTION.rst,sha256=3Ii2J2Cj_lDYlki3pr6u_3A_kYCqRutkW3iU633cBm8,133 bacpypes-0.13.8.dist-info/METADATA,sha256=vB5VkFIsGfSwNNnxqS4K8YvyFVTcuTZNkd3B3eHT_HY,793 bacpypes-0.13.8.dist-info/RECORD,, bacpypes-0.13.8.dist-info/WHEEL,sha256=zX7PHtH_7K-lEzyK75et0UBa3Bj8egCBMXe1M4gc6SU,92 bacpypes-0.13.8.dist-info/metadata.json,sha256=c22f7RUxMz0wd7u87lPCwA2GdFMKR-opG_NEtnfGYak,825 bacpypes-0.13.8.dist-info/top_level.txt,sha256=32Nqh_NTOMEkfoD-8Gs4rn8dNNlBIiif88YNbAVjxlk,9 PKF`j)1)1bacpypes/analysis.pyPK1HOJuPoPo[1bacpypes/object.pyPKHx3&3&۠bacpypes/consolecmd.pyPK1H=bacpypes/apdu.pyPK0GG IlPlP bacpypes/bvll.pyPKG5Hnqq]bacpypes/singleton.pyPK*UHݾ \\Vebacpypes/tcp.pyPKH`"66{bacpypes/primitivedata.pyPK;dF$\\~ bacpypes/npdu.pyPK*UH< bacpypes/core.pyPKH I&I& bacpypes/task.pyPKF[U bacpypes/vlan.pyPKkFh}99) bacpypes/bsllservice.pyPKHMae)| bacpypes-0.13.8.dist-info/DESCRIPTION.rstPKH x99'H bacpypes-0.13.8.dist-info/metadata.jsonPKHҮ ' bacpypes-0.13.8.dist-info/top_level.txtPKH}\\ bacpypes-0.13.8.dist-info/WHEELPKH`K\" bacpypes-0.13.8.dist-info/METADATAPKH    bacpypes-0.13.8.dist-info/RECORDPK$$ Z