class BackgroundFrame(Validatable): SCHEMA = [{ "subnet": Types.BYTE(), "control": Types.OBJECT(BackgroundFrameControl), "payload": Types.BITS(16), "crc16": Types.BITS( 16 ) # TODO does not work, look into this later {'validator': validate_crc } }] def __init__(self, subnet, control, payload, crc16): self.subnet = subnet self.control = control self.payload = payload self.crc16 = crc16 # TODO validate CRC super(BackgroundFrame, self).__init__() # def validate_crc(self, value, error): # raw_data = [] # raw_data.append(self.length) # raw_data.append(self.subnet) # raw_data.append(self.control) # raw_data.append(self.target_address) # raw_data.append(self.payload) # crc = CRCCCITT().calculate(raw_data) @staticmethod def parse(s): subnet = s.read("int:8") control = BackgroundFrameControl.parse(s) payload = s.read("uint:16") crc = s.read("uint:16") return BackgroundFrame(subnet=subnet, control=control, payload=payload, crc16=crc) def __iter__(self): yield self.subnet for byte in self.control: yield byte for byte in self.payload: yield byte yield self.crc16 def __str__(self): return pprint.PrettyPrinter().pformat(self.as_dict())
class Action(Validatable): SCHEMA = [{ "op": Types.BITS(6), "operation": Types.OBJECT(Operation), "operand": Types.OBJECT(nullable=True) # there is no Operand base-class }] def __init__(self, operation=NoOperation()): self.operation = operation super(Action, self).__init__() @property def op(self): return self.operation.op @property def operand(self): return self.operation.operand def __str__(self): if isinstance(self.operation, ReturnFileData): # when reading a known system files we output the parsed data if self.operation.systemfile_type != None and self.operation.file_data_parsed != None: return "Received {} content: {}".format( self.operation.systemfile_type.__class__.__name__, self.operation.file_data_parsed) return str(self.operation)
class Action(Validatable): SCHEMA = [{ "op": Types.BITS(6), "operation": Types.OBJECT(Operation), "operand": Types.OBJECT(nullable=True) # there is no Operand base-class }] def __init__(self, operation=NoOperation()): self.operation = operation super(Action, self).__init__() @property def op(self): return self.operation.op @property def operand(self): return self.operation.operand def __str__(self): output = "op={}, operand={}({})".format( type(self.operation).__name__, type(self.operand).__name__, self.operand) return output
class IndirectForwardAction(Action): SCHEMA = [{ "overload": Types.BOOLEAN(), "resp": Types.BOOLEAN(), "op": Types.BITS(6), "operation": Types.OBJECT(IndirectForward), "operand": Types.OBJECT(IndirectInterfaceOperand ) # TODO for now only D7 interface is supported }] def __init__(self, overload=False, resp=False, operation=NoOperation()): self.overload = overload self.resp = resp super(IndirectForwardAction, self).__init__(operation) def __iter__(self): byte = 0 if self.overload: byte |= 1 << 7 if self.resp: byte |= 1 << 6 byte += self.op yield byte for byte in self.operation: yield byte
class Length(Validatable): SCHEMA = [{ "length": Types.BITS(2), "value": Types.INTEGER(min=0, max=0x3FFFFFFF) }] def __init__(self, value=0): self.value = value super(Length, self).__init__() @staticmethod def parse(s): size = s.read("uint:2") # + 1 = already read value = s.read("uint:" + str(6 + (size * 8))) return Length(value=value) def __iter__(self): byte = 0 size = 1 if self.value > 0x3F: size = 2 if self.value > 0x3FFF: size = 3 if self.value > 0x3FFFFF: size = 4 byte += (size - 1) << 6 if size == 1: byte += self.value yield byte else: length_bytes = bytearray(struct.pack(">I", self.value)) if size == 2: length_bytes = length_bytes[2:] elif size == 3: length_bytes = length_bytes[1:] byte += length_bytes[0] yield byte for byte in length_bytes[1:]: yield byte def __str__(self): return str(self.value) def __eq__(self, other): if isinstance(other, self.__class__): return self.value == other.value elif isinstance(other, int): return self.value == other else: return False def __ne__(self, other): return not self.__eq__(other)
class Frame(Validatable): SCHEMA = [{ "length": Types.BYTE(), "subnet": Types.BYTE(), "control": Types.OBJECT(Control), "target_address": Types.BYTES(), # TODO max size? "d7anp_frame": Types.OBJECT(D7anpFrame), # TODO assuming foreground frames for now "crc16": Types.BITS( 16 ) # TODO does not work, look into this later {'validator': validate_crc } }] def __init__(self, length, subnet, control, target_address, d7anp_frame, crc16): self.length = length self.subnet = subnet self.control = control self.target_address = target_address self.d7anp_frame = d7anp_frame self.crc16 = crc16 # TODO validate CRC super(Frame, self).__init__() # def validate_crc(self, value, error): # raw_data = [] # raw_data.append(self.length) # raw_data.append(self.subnet) # raw_data.append(self.control) # raw_data.append(self.target_address) # raw_data.append(self.payload) # crc = CRCCCITT().calculate(raw_data) def __iter__(self): yield self.length yield self.subnet for byte in self.control: yield byte for byte in self.target_address: yield byte for byte in self.d7anp_frame: yield byte yield self.crc16
class ForwardAction(Action): SCHEMA = [{ "resp" : Types.BOOLEAN(), "op" : Types.BITS(6), "operation": Types.OBJECT(Operation), "operand" : Types.OBJECT(InterfaceConfiguration) # TODO for now only D7 interface is supported }] def __init__(self, resp=False, operation=NoOperation()): self.resp = resp super(ForwardAction, self).__init__(operation) def __iter__(self): byte = 0 if self.group: byte |= 1 << 7 if self.resp: byte |= 1 << 6 byte += self.op yield byte for byte in self.operation: yield byte
class TagRequestAction(Action): SCHEMA = [{ "respond_when_completed": Types.BOOLEAN(), "op": Types.BITS(6), "operation": Types.OBJECT(Operation), "operand": Types.OBJECT(nullable=True) # there is no Operand base-class }] def __init__(self, respond_when_completed=True, operation=NoOperation()): self.respond_when_completed = respond_when_completed super(TagRequestAction, self).__init__(operation) def __iter__(self): byte = 0 if self.respond_when_completed: byte |= 1 << 7 byte += self.op yield byte for byte in self.operation: yield byte
class SecurityKeyFile(File, Validatable): SCHEMA = [{ "key": Types.BITS(length=128) }] def __init__(self, key=0): self.key = key Validatable.__init__(self) File.__init__(self, SystemFileIds.NWL_SECURITY_KEY.value, 16) @staticmethod def parse(s): key = s.read("bytes:16") return SecurityKeyFile(key) def __iter__(self): for byte in [(self.key & (0xff << pos * 8)) >> pos * 8 for pos in range(16)]: yield byte def __str__(self): return "key={}".format(self.key)
class RegularAction(Action): SCHEMA = [{ "group": Types.BOOLEAN(), "resp": Types.BOOLEAN(), "op": Types.BITS(6), "operation": Types.OBJECT(Operation), "operand": Types.OBJECT(nullable=True) # there is no Operand base-class }] def __init__(self, group=False, resp=False, operation=NoOperation()): self.group = group self.resp = resp super(RegularAction, self).__init__(operation) def __iter__(self): byte = 0 if self.group: byte |= 1 << 7 if self.resp: byte |= 1 << 6 byte += self.op yield byte for byte in self.operation: yield byte
class TagResponseAction(Action): SCHEMA = [{ "eop": Types.BOOLEAN(), "error": Types.BOOLEAN(), "op": Types.BITS(6), "operation": Types.OBJECT(Operation), "operand": Types.OBJECT(nullable=True) # there is no Operand base-class }] def __init__(self, eop, error, operation=NoOperation()): self.eop = eop self.error = error super(TagResponseAction, self).__init__(operation) def __iter__(self): byte = 0 if self.eop: byte |= 1 << 7 if self.error: byte |= 1 << 6 byte += self.op yield byte for byte in self.operation: yield byte
class Frame(Validatable): SCHEMA = [{ "length": Types.BYTE(), "subnet": Types.BYTE(), "control": Types.OBJECT(Control), "target_address": Types.BYTES(), # TODO max size? "d7anp_frame": Types.OBJECT(D7anpFrame), # TODO assuming foreground frames for now "crc16": Types.BITS( 16 ) # TODO does not work, look into this later {'validator': validate_crc } }] def __init__(self, length, subnet, control, target_address, d7anp_frame, crc16): self.length = length self.subnet = subnet self.control = control self.target_address = target_address self.d7anp_frame = d7anp_frame self.crc16 = crc16 # TODO validate CRC super(Frame, self).__init__() # def validate_crc(self, value, error): # raw_data = [] # raw_data.append(self.length) # raw_data.append(self.subnet) # raw_data.append(self.control) # raw_data.append(self.target_address) # raw_data.append(self.payload) # crc = CRCCCITT().calculate(raw_data) @staticmethod def parse(s): length = s.read("int:8") subnet = s.read("int:8") control = Control.parse(s) payload_length = length - 4 # substract subnet, control, crc if control.id_type == IdType.VID: target_address = map(ord, s.read("bytes:2")) payload_length = payload_length - 2 elif control.id_type == IdType.UID: target_address = map(ord, s.read("bytes:8")) payload_length = payload_length - 8 else: target_address = [] return Frame(length=length, subnet=subnet, control=control, target_address=target_address, d7anp_frame=D7anpFrame.parse(s, payload_length), crc16=s.read("uint:16")) def __iter__(self): yield self.length yield self.subnet for byte in self.control: yield byte for byte in self.target_address: yield byte for byte in self.d7anp_frame: yield byte yield self.crc16
class Addressee(Validatable): # addressee ID length ID_LENGTH_BROADCAST = 0 ID_LENGTH_VID = 2 ID_LENGTH_UID = 8 SCHEMA = [ { # broadcast "id_type": Types.INTEGER([IdType.BCAST]), "access_class": Types.BITS(4), "id_length": Types.INTEGER([0]), "id": Types.INTEGER([None]) }, { # virtual "id_type": Types.INTEGER([IdType.VID]), "access_class": Types.BITS(4), "id_length": Types.INTEGER([2]), "id": Types.INTEGER(min=0, max=0xFFFF) }, { # unicast "id_type": Types.INTEGER([IdType.UID]), "access_class": Types.BITS(4), "id_length": Types.INTEGER([8]), "id": Types.INTEGER(min=0, max=0xFFFFFFFFFFFFFFFF) } ] # SCHEMA = [ # { # "id_type" : Types.INTEGER(IdType.ALL), # "cl" : Types.BITS(4), # "id_length" : Types.INTEGER([0, 2, 8]), # "id" : Types.INTEGER(min=0, max=0xFFFFFFFFFFFFFFFF) # } # ] def __init__(self, access_class=0, id_type=IdType.BCAST, id=None): self.id_type = id_type self.access_class = access_class self.id = id super(Addressee, self).__init__() @property def id_length(self): return Addressee.length_for(id_type=self.id_type) @classmethod def length_for(self, id_type): if id_type == IdType.BCAST: return Addressee.ID_LENGTH_BROADCAST if id_type == IdType.VID: return Addressee.ID_LENGTH_VID if id_type == IdType.UID: return Addressee.ID_LENGTH_UID @staticmethod def parse(s): _ = s.read("pad:2") id_type = s.read("uint:2") cl = s.read("uint:4") l = Addressee.length_for(id_type) id = s.read("uint:" + str(l * 8)) if l > 0 else None return Addressee(id_type=id_type, access_class=cl, id=id) def __iter__(self): byte = 0 # pad 2 << 7 << 6 byte |= self.id_type << 4 byte += self.access_class yield byte if self.id_length > 0: id = bytearray(struct.pack(">Q", self.id))[8 - self.id_length:] for byte in id: yield byte def __str__(self): return "ac={}, id_type={}, id={}".format(self.access_class, self.id_type, hex(self.id))