def from_bytes(cls, source_bytes: bytes): data = bytearray(source_bytes) tag = data.pop(0) if tag != cls.TAG: raise ValueError( f"Tag for GET request is not correct. Got {tag}, should be {cls.TAG}" ) type_choice = enums.GetRequestType(data.pop(0)) if type_choice is not enums.GetRequestType.WITH_LIST: raise ValueError( "The data for the GetRequest is not for a GetRequestWithList") invoke_id_and_priority = InvokeIdAndPriority.from_bytes( data.pop(0).to_bytes(1, "big")) number_of_items = data.pop(0) cosem_atts = list() for i in range(0, number_of_items): # Not really happy with the format of this but it works fine. c = cosem.CosemAttributeWithSelection.from_bytes(data) cosem_atts.append(c) data = data[len(c.to_bytes()):] return cls( cosem_attributes_with_selection=cosem_atts, invoke_id_and_priority=invoke_id_and_priority, )
def from_bytes(cls, source_bytes: bytes): data = bytearray(source_bytes) tag = data.pop(0) if tag != cls.TAG: raise ValueError( f"Tag for SetRequest is not correct. Got {tag}, should be {cls.TAG}" ) type_choice = enums.SetRequestType(data.pop(0)) if type_choice is not enums.SetRequestType.NORMAL: raise ValueError("The type of the SetRequest is not for a SetRequestNormal") invoke_id_and_priority = InvokeIdAndPriority.from_bytes( data.pop(0).to_bytes(1, "big") ) cosem_attribute = cosem.CosemAttribute.from_bytes(data[:9]) data = data[9:] has_access_selection = bool(data.pop(0)) if has_access_selection: raise NotImplementedError("Selective access on SET is not implemented") else: access_selection = None return cls( cosem_attribute=cosem_attribute, data=bytes(data), access_selection=access_selection, invoke_id_and_priority=invoke_id_and_priority, )
def from_bytes(cls, source_bytes: bytes): data = bytearray(source_bytes) tag = data.pop(0) if tag != cls.TAG: raise ValueError( f"Tag {tag} is not correct for ActionResponse. Should be {cls.TAG}" ) action_type = enumerations.ActionType(data.pop(0)) if action_type != enumerations.ActionType.NORMAL: raise ValueError( f"Bytes are not representing a ActionResponseNormal. Action type " f"is {action_type}") invoke_id_and_priority = InvokeIdAndPriority.from_bytes( data.pop(0).to_bytes(1, "big")) status = enumerations.ActionResultStatus(data.pop(0)) has_data = bool(data.pop(0)) if has_data: raise ValueError(f"ActionResponse has data and should not be a " f"ActionResponseNormal") return cls(invoke_id_and_priority=invoke_id_and_priority, status=status)
def from_bytes(cls, source_bytes: bytes): data = bytearray(source_bytes) tag = data.pop(0) if tag != cls.TAG: raise ValueError( f"Tag for GET request is not correct. Got {tag}, should be {cls.TAG}" ) type_choice = enums.GetRequestType(data.pop(0)) if type_choice is not enums.GetRequestType.NORMAL: raise ValueError( "The data for the GetRequest is not for a GetRequestNormal") invoke_id_and_priority = InvokeIdAndPriority.from_bytes( data.pop(0).to_bytes(1, "big")) cosem_attribute_data = data[:9] cosem_attribute = cosem.CosemAttribute.from_bytes(cosem_attribute_data) data = data[9:] has_access_selection = bool(data.pop(0)) if has_access_selection: access_selection = selective_access.AccessDescriptorFactory.from_bytes( data) else: access_selection = None return cls( cosem_attribute=cosem_attribute, invoke_id_and_priority=invoke_id_and_priority, access_selection=access_selection, )
def from_bytes(cls, source_bytes: bytes): data = bytearray(source_bytes) tag = data.pop(0) if tag != cls.TAG: raise ValueError( f"Tag is not correct. Should be {cls.TAG} but is {tag}") response_type = enums.GetResponseType(data.pop(0)) if response_type != cls.RESPONSE_TYPE: raise ValueError( f"The response type byte: {response_type} is not for a GetResponseNormal" ) invoke_id_and_priority = InvokeIdAndPriority.from_bytes( data.pop(0).to_bytes(1, "big")) last_block = bool(data.pop(0)) if not last_block: raise ValueError( f"Last block is not set to true in a GetResponseLastBlock.") block_number = int.from_bytes(data[:4], "big") data = data[4:] choice = data.pop(0) if choice != 0: raise ValueError( f"The data choice is not 0 to indicate data but: {choice}") data_length, data = dlms_cosem.dlms_data.decode_variable_integer(data) if data_length != len(data): raise ValueError( "The octet string in block data is not of the correct length") return cls(bytes(data), block_number, invoke_id_and_priority)
def from_bytes(cls, source_bytes: bytes): data = bytearray(source_bytes) tag = data.pop(0) if tag != cls.TAG: raise ValueError( f"Tag is not correct. Should be {cls.TAG} but is {tag}") response_type = enums.GetResponseType(data.pop(0)) if response_type != cls.RESPONSE_TYPE: raise ValueError( f"The response type byte: {response_type} is not for a GetResponseNormal" ) invoke_id_and_priority = InvokeIdAndPriority.from_bytes( data.pop(0).to_bytes(1, "big")) last_block = bool(data.pop(0)) if not last_block: raise ValueError( f"Last block is not set to true in a GetResponseLastBlock.") block_number = int.from_bytes(data[:4], "big") data = data[4:] choice = data.pop(0) if choice != 1: raise ValueError( f"The data choice is not 1 to indicate error but: {choice}") assert len(data) == 1 error = enums.DataAccessResult(data.pop(0)) return cls(error, block_number, invoke_id_and_priority)
def from_bytes(cls, source_bytes: bytes): data = bytearray(source_bytes) tag = data.pop(0) if tag != cls.TAG: raise ValueError( f"Tag {tag} is not the correct tag for an ActionRequest, should " f"be {cls.TAG}") request_type = enumerations.ActionType(data.pop(0)) if request_type != enumerations.ActionType.NORMAL: raise ValueError( f"Bytes are not representing a ActionRequestNormal. Action type " f"is {request_type}") invoke_id_and_priority = InvokeIdAndPriority.from_bytes( data.pop(0).to_bytes(1, "big")) cosem_method = cosem.CosemMethod.from_bytes(data[:9]) has_data = bool(data[9]) if has_data: request_data = data[10:] else: request_data = None return cls( cosem_method=cosem_method, data=request_data, invoke_id_and_priority=invoke_id_and_priority, )
def from_bytes(cls, source_bytes: bytes): data = bytearray(source_bytes) tag = data.pop(0) if tag != cls.TAG: raise ValueError( f"Tag {tag} is not correct for ActionResponse. Should be {cls.TAG}" ) action_type = enumerations.ActionType(data.pop(0)) if action_type != enumerations.ActionType.NORMAL: raise ValueError( f"Bytes are not representing a ActionResponseNormal. Action type " f"is {action_type}") invoke_id_and_priority = InvokeIdAndPriority.from_bytes( data.pop(0).to_bytes(1, "big")) status = enumerations.ActionResultStatus(data.pop(0)) has_data = bool(data.pop(0)) if has_data: data_is_error = data.pop(0) == 1 if not data_is_error: raise ValueError( "Data is not a ActionResponseNormalWithError, maybe a " "ActionResponseNormal") assert len(data) == 1 error = enumerations.DataAccessResult(data.pop(0)) else: raise ValueError("No error data in ActionResponseWithError") return cls(invoke_id_and_priority=invoke_id_and_priority, status=status, error=error)
def from_bytes(source_bytes: bytes): data = bytearray(source_bytes) tag = data.pop(0) if tag != GetResponseFactory.TAG: raise ValueError( f"Tag is not correct. Should be {GetResponseFactory.TAG} but is {tag}" ) response_type = enums.GetResponseType(data.pop(0)) invoke_id_and_priority = InvokeIdAndPriority.from_bytes( data.pop(0).to_bytes(1, "big")) if response_type == enums.GetResponseType.NORMAL: # check if it is an error or data response by assesing the choice. choice = data.pop(0) if choice == 0: return GetResponseNormal( invoke_id_and_priority=invoke_id_and_priority, data=bytes(data)) elif choice == 1: assert len(data) == 1 # should only be one byte left. error = enums.DataAccessResult(data.pop(0)) return GetResponseNormalWithError( invoke_id_and_priority=invoke_id_and_priority, error=error) elif response_type == enums.GetResponseType.WITH_BLOCK: last_block = bool(data.pop(0)) block_number = int.from_bytes(data[:4], "big") data = data[4:] choice = data.pop(0) if choice == 0: data_length, data = dlms_cosem.dlms_data.decode_variable_integer( data) if data_length != len(data): raise ValueError( "The octet string in block data is not of the correct length" ) if last_block: return GetResponseLastBlock(bytes(data), block_number, invoke_id_and_priority) else: return GetResponseWithBlock(bytes(data), block_number, invoke_id_and_priority) elif choice == 1: assert len(data) == 1 # should only be one byte left. error = enums.DataAccessResult(data.pop(0)) if last_block: return GetResponseLastBlockWithError( error, block_number, invoke_id_and_priority) else: raise ValueError( "It is not possible to send an error on a " "GetResponseWithBlock. When an error occurs it " "should always be sent in a GetResponseLastBlockWithError" ) elif response_type == enums.GetResponseType.WITH_LIST: return GetResponseWithList.from_bytes(bytes(source_bytes)) else: raise ValueError("Response type is not a valid GetResponse type")
class ActionResponseNormalWithError(AbstractXDlmsApdu): TAG: ClassVar[int] = 199 ACTION_TYPE: ClassVar[ enumerations.ActionType] = enumerations.ActionType.NORMAL status: enumerations.ActionResultStatus error: enumerations.DataAccessResult invoke_id_and_priority: InvokeIdAndPriority = attr.ib( default=InvokeIdAndPriority(0, True, True)) def to_bytes(self): out = bytearray() out.append(self.TAG) out.append(self.ACTION_TYPE.value) out.extend(self.invoke_id_and_priority.to_bytes()) out.append(self.status.value) out.extend(b"\x01") out.extend(b"\x01") # data result data (error) choice out.append(self.error.value) return bytes(out) @classmethod def from_bytes(cls, source_bytes: bytes): data = bytearray(source_bytes) tag = data.pop(0) if tag != cls.TAG: raise ValueError( f"Tag {tag} is not correct for ActionResponse. Should be {cls.TAG}" ) action_type = enumerations.ActionType(data.pop(0)) if action_type != enumerations.ActionType.NORMAL: raise ValueError( f"Bytes are not representing a ActionResponseNormal. Action type " f"is {action_type}") invoke_id_and_priority = InvokeIdAndPriority.from_bytes( data.pop(0).to_bytes(1, "big")) status = enumerations.ActionResultStatus(data.pop(0)) has_data = bool(data.pop(0)) if has_data: data_is_error = data.pop(0) == 1 if not data_is_error: raise ValueError( "Data is not a ActionResponseNormalWithError, maybe a " "ActionResponseNormal") assert len(data) == 1 error = enumerations.DataAccessResult(data.pop(0)) else: raise ValueError("No error data in ActionResponseWithError") return cls(invoke_id_and_priority=invoke_id_and_priority, status=status, error=error)
class ActionResponseNormalWithData(AbstractXDlmsApdu): TAG: ClassVar[int] = 199 ACTION_TYPE: ClassVar[ enumerations.ActionType] = enumerations.ActionType.NORMAL status: enumerations.ActionResultStatus data: bytes = attr.ib(default=None) invoke_id_and_priority: InvokeIdAndPriority = attr.ib( default=InvokeIdAndPriority(0, True, True)) def to_bytes(self): out = bytearray() out.append(self.TAG) out.append(self.ACTION_TYPE.value) out.extend(self.invoke_id_and_priority.to_bytes()) out.append(self.status.value) out.extend(b"\x01") # has data out.extend(b"\x00") # data result choice out.extend(self.data) return bytes(out) @classmethod def from_bytes(cls, source_bytes: bytes): data = bytearray(source_bytes) tag = data.pop(0) if tag != cls.TAG: raise ValueError( f"Tag {tag} is not correct for ActionResponse. Should be {cls.TAG}" ) action_type = enumerations.ActionType(data.pop(0)) if action_type != enumerations.ActionType.NORMAL: raise ValueError( f"Bytes are not representing a ActionResponseNormal. Action type " f"is {action_type}") invoke_id_and_priority = InvokeIdAndPriority.from_bytes( data.pop(0).to_bytes(1, "big")) status = enumerations.ActionResultStatus(data.pop(0)) has_data = bool(data.pop(0)) if has_data: data_is_result = data.pop(0) == 0 if not data_is_result: raise ValueError( "Data is not a ActionResponseNormalWithData, maybe a " "ActionResponseNormalWithError") response_data = data else: raise ValueError( f"ActionResponseNormalWithData does not contain any data. " f"Should probably be an ActionResponseNormal") return cls( invoke_id_and_priority=invoke_id_and_priority, status=status, data=response_data, )
class ActionRequestNormal(AbstractXDlmsApdu): TAG: ClassVar[int] = 195 ACTION_TYPE: ClassVar[ enumerations.ActionType] = enumerations.ActionType.NORMAL cosem_method: cosem.CosemMethod = attr.ib( validator=attr.validators.instance_of(cosem.CosemMethod)) data: Optional[bytes] = attr.ib(default=None) invoke_id_and_priority: InvokeIdAndPriority = attr.ib( default=InvokeIdAndPriority(0, True, True), validator=attr.validators.instance_of(InvokeIdAndPriority), ) def to_bytes(self): out = bytearray() out.append(self.TAG) out.append(self.ACTION_TYPE.value) out.extend(self.invoke_id_and_priority.to_bytes()) out.extend(self.cosem_method.to_bytes()) if self.data: out.append(0x01) out.extend(self.data) else: out.append(0x00) return bytes(out) @classmethod def from_bytes(cls, source_bytes: bytes): data = bytearray(source_bytes) tag = data.pop(0) if tag != cls.TAG: raise ValueError( f"Tag {tag} is not the correct tag for an ActionRequest, should " f"be {cls.TAG}") request_type = enumerations.ActionType(data.pop(0)) if request_type != enumerations.ActionType.NORMAL: raise ValueError( f"Bytes are not representing a ActionRequestNormal. Action type " f"is {request_type}") invoke_id_and_priority = InvokeIdAndPriority.from_bytes( data.pop(0).to_bytes(1, "big")) cosem_method = cosem.CosemMethod.from_bytes(data[:9]) has_data = bool(data[9]) if has_data: request_data = data[10:] else: request_data = None return cls( cosem_method=cosem_method, data=request_data, invoke_id_and_priority=invoke_id_and_priority, )
def from_bytes(cls, source_bytes: bytes): data = bytearray(source_bytes) tag = data.pop(0) if tag != cls.TAG: raise ValueError( f"Tag for GET request is not correct. Got {tag}, should be {cls.TAG}" ) type_choice = enums.GetRequestType(data.pop(0)) if type_choice is not enums.GetRequestType.NEXT: raise ValueError( "The data for the GetRequest is not for a GetRequestNext") invoke_id_and_priority = InvokeIdAndPriority.from_bytes( data.pop(0).to_bytes(1, "big")) assert len(data) == 4 # should only be block number left. block_number = int.from_bytes(data, "big") return cls(block_number, invoke_id_and_priority)
class ActionResponseNormal(AbstractXDlmsApdu): TAG: ClassVar[int] = 199 ACTION_TYPE: ClassVar[ enumerations.ActionType] = enumerations.ActionType.NORMAL status: enumerations.ActionResultStatus invoke_id_and_priority: InvokeIdAndPriority = attr.ib( default=InvokeIdAndPriority(0, True, True)) def to_bytes(self): out = bytearray() out.append(self.TAG) out.append(self.ACTION_TYPE.value) out.extend(self.invoke_id_and_priority.to_bytes()) out.append(self.status.value) out.extend(b"\x00") return bytes(out) @classmethod def from_bytes(cls, source_bytes: bytes): data = bytearray(source_bytes) tag = data.pop(0) if tag != cls.TAG: raise ValueError( f"Tag {tag} is not correct for ActionResponse. Should be {cls.TAG}" ) action_type = enumerations.ActionType(data.pop(0)) if action_type != enumerations.ActionType.NORMAL: raise ValueError( f"Bytes are not representing a ActionResponseNormal. Action type " f"is {action_type}") invoke_id_and_priority = InvokeIdAndPriority.from_bytes( data.pop(0).to_bytes(1, "big")) status = enumerations.ActionResultStatus(data.pop(0)) has_data = bool(data.pop(0)) if has_data: raise ValueError(f"ActionResponse has data and should not be a " f"ActionResponseNormal") return cls(invoke_id_and_priority=invoke_id_and_priority, status=status)
def from_bytes(cls, source_bytes: bytes): data = bytearray(source_bytes) tag = data.pop(0) if tag != cls.TAG: raise ValueError("Not a GetResponse APDU") response_type = data.pop(0) if response_type != cls.RESPONSE_TYPE: raise ValueError("Not a GetResponseWithList Apdu") invoke_id_and_priority = InvokeIdAndPriority.from_bytes( data.pop(0).to_bytes(1, "big")) # List of Get-Data-Response. list_length = data.pop(0) dlms_data = cls.parse_list_response(data, list_length) return cls(invoke_id_and_priority=invoke_id_and_priority, response_data=dlms_data)
def from_bytes(cls, source_bytes: bytes): data = bytearray(source_bytes) tag = data.pop(0) if tag != cls.TAG: raise ValueError( f"Tag is not correct. Should be {cls.TAG} but is {tag}") response_type = enums.GetResponseType(data.pop(0)) if response_type != cls.RESPONSE_TYPE: raise ValueError( f"The response type byte: {response_type} is not for a GetResponseNormal" ) invoke_id_and_priority = InvokeIdAndPriority.from_bytes( data.pop(0).to_bytes(1, "big")) choice = data.pop(0) if choice != 0: raise ValueError( f"The data choice is not 0 to indicate data but: {choice}") return cls(bytes(data), invoke_id_and_priority)
def from_bytes(cls, source_bytes: bytes): data = bytearray(source_bytes) tag = data.pop(0) if tag != cls.TAG: raise ValueError( f"Tag for SetResponse is not correct. Got {tag}, should be {cls.TAG}" ) type_choice = enums.SetResponseType(data.pop(0)) if type_choice is not enums.SetResponseType.NORMAL: raise ValueError( "The type of the SetResponse is not for a SetResponseNormal" ) invoke_id_and_priority = InvokeIdAndPriority.from_bytes( data.pop(0).to_bytes(1, "big") ) result = enums.DataAccessResult(data.pop(0)) return cls(result=result, invoke_id_and_priority=invoke_id_and_priority)
def from_bytes(cls, source_bytes: bytes): data = bytearray(source_bytes) tag = data.pop(0) if tag != cls.TAG: raise ValueError( f"Tag {tag} is not correct for ActionResponse. Should be {cls.TAG}" ) action_type = enumerations.ActionType(data.pop(0)) if action_type != enumerations.ActionType.NORMAL: raise ValueError( f"Bytes are not representing a ActionResponseNormal. Action type " f"is {action_type}") invoke_id_and_priority = InvokeIdAndPriority.from_bytes( data.pop(0).to_bytes(1, "big")) status = enumerations.ActionResultStatus(data.pop(0)) has_data = bool(data.pop(0)) if has_data: data_is_result = data.pop(0) == 0 if not data_is_result: raise ValueError( "Data is not a ActionResponseNormalWithData, maybe a " "ActionResponseNormalWithError") response_data = data else: raise ValueError( f"ActionResponseNormalWithData does not contain any data. " f"Should probably be an ActionResponseNormal") return cls( invoke_id_and_priority=invoke_id_and_priority, status=status, data=response_data, )