def from_frames(self, frames, datatype_crc=None): # Ignore frame index for anonymous transfers since they can't be # multi-frame if frames[0].source_node_id: for i, f in enumerate(frames): if i != f.frame_index: raise IndexError(("Frame index mismatch: expected {0}, " + "got {1}").format(i, f.frame_index)) self.transfer_id = frames[0].transfer_id self.transfer_priority = frames[0].transfer_priority self.source_node_id = frames[0].source_node_id self.data_type_id = frames[0].data_type_id self.dest_node_id = frames[0].dest_node_id self.payload = sum((f.payload for f in frames), bytearray()) self.request_not_response = frames[0].request_not_response self.broadcast_not_unicast = frames[0].broadcast_not_unicast # For a multi-frame transfer, validate the CRC and frame indexes if len(frames) > 1: transfer_crc = self.payload[0] + (self.payload[1] << 8) self.payload = self.payload[2:] crc = common.crc16_from_bytes(self.payload, initial=datatype_crc) if crc != transfer_crc: raise ValueError(("CRC mismatch: expected {0:x}, got {1:x} " + "for payload {2!r} (DTID {3:d})").format( crc, transfer_crc, self.payload, self.data_type_id))
def to_frames(self): out_frames = [] remaining_payload = self.payload # Prepend the transfer CRC to the payload if the transfer requires # multiple frames if len(remaining_payload) > 7: crc = common.crc16_from_bytes(self.payload, initial=self.data_type_crc) remaining_payload = bytearray([crc & 0xFF, crc >> 8 ]) + remaining_payload # Generate the frame sequence tail = 0x20 # set toggle bit high so the first frame is emitted with it cleared while True: # Tail byte contains start-of-transfer, end-of-transfer, toggle, and Transfer ID tail = ((0x80 if len(out_frames) == 0 else 0) | (0x40 if len(remaining_payload) <= 7 else 0) | ((tail ^ 0x20) & 0x20) | (self.transfer_id & 0x1F)) out_frames.append( Frame(message_id=self.message_id, data=remaining_payload[0:7] + bchr(tail))) remaining_payload = remaining_payload[7:] if not remaining_payload: break return out_frames
def to_frames(self): out_frames = [] remaining_payload = self.payload # Prepend the transfer CRC to the payload if the transfer requires # multiple frames if len(remaining_payload) > 7: crc = common.crc16_from_bytes(self.payload, initial=self.data_type_crc) remaining_payload = bytearray([crc & 0xFF, crc >> 8]) + remaining_payload # Generate the frame sequence tail = 0x20 # set toggle bit high so the first frame is emitted with it cleared while True: # Tail byte contains start-of-transfer, end-of-transfer, toggle, and Transfer ID tail = ((0x80 if len(out_frames) == 0 else 0) | (0x40 if len(remaining_payload) <= 7 else 0) | ((tail ^ 0x20) & 0x20) | (self.transfer_id & 0x1F)) out_frames.append(Frame(message_id=self.message_id, data=remaining_payload[0:7] + bchr(tail))) remaining_payload = remaining_payload[7:] if not remaining_payload: break return out_frames
def from_frames(self, frames): # Initialize transfer timestamps from the first frame self.ts_monotonic = frames[0].ts_monotonic self.ts_real = frames[0].ts_real # Validate the flags in the tail byte expected_toggle = 0 expected_transfer_id = frames[0].bytes[-1] & 0x1F for idx, f in enumerate(frames): tail = f.bytes[-1] if (tail & 0x1F) != expected_transfer_id: raise TransferError("Transfer ID {0} incorrect, expected {1}".format(tail & 0x1F, expected_transfer_id)) elif idx == 0 and not (tail & 0x80): raise TransferError("Start of transmission not set on frame 0") elif idx > 0 and tail & 0x80: raise TransferError("Start of transmission set unexpectedly on frame {0}".format(idx)) elif idx == len(frames) - 1 and not (tail & 0x40): raise TransferError("End of transmission not set on last frame") elif idx < len(frames) - 1 and (tail & 0x40): raise TransferError("End of transmission set unexpectedly on frame {0}".format(idx)) elif (tail & 0x20) != expected_toggle: raise TransferError("Toggle bit value {0} incorrect on frame {1}".format(tail & 0x20, idx)) expected_toggle ^= 0x20 self.transfer_id = expected_transfer_id self.message_id = frames[0].message_id payload_bytes = bytearray(b''.join(bytes(f.bytes[0:-1]) for f in frames)) # Find the data type if self.service_not_message: kind = dsdl.CompoundType.KIND_SERVICE else: kind = dsdl.CompoundType.KIND_MESSAGE datatype = uavcan.DATATYPES.get((self.data_type_id, kind)) if not datatype: raise TransferError("Unrecognised {0} type ID {1}" .format("service" if self.service_not_message else "message", self.data_type_id)) # For a multi-frame transfer, validate the CRC and frame indexes if len(frames) > 1: transfer_crc = payload_bytes[0] + (payload_bytes[1] << 8) payload_bytes = payload_bytes[2:] crc = common.crc16_from_bytes(payload_bytes, initial=datatype.base_crc) if crc != transfer_crc: raise TransferError("CRC mismatch: expected {0:x}, got {1:x} for payload {2!r} (DTID {3:d})" .format(crc, transfer_crc, payload_bytes, self.data_type_id)) self.data_type_id = datatype.default_dtid self.data_type_signature = datatype.get_data_type_signature() self.data_type_crc = datatype.base_crc if self.service_not_message: self.payload = datatype(_mode="request" if self.request_not_response else "response") else: self.payload = datatype() # noinspection PyProtectedMember self.payload._unpack(bits_from_bytes(payload_bytes))
def to_frames(self, datatype_crc=None): # Broadcast frames support up to 8 bytes, other frames have a # destination node ID which consumes the first byte of the message if self.dest_node_id is None: bytes_per_frame = 8 else: bytes_per_frame = 7 out_frames = [] remaining_payload = self.payload # Prepend the transfer CRC to the payload if the transfer requires # multiple frames if len(remaining_payload) > bytes_per_frame: crc = common.crc16_from_bytes(self.payload, initial=datatype_crc) remaining_payload = bytearray([crc & 0xFF, crc >> 8]) + \ remaining_payload # Generate the frame sequence while True: frame = Frame(message_id=0) frame.transfer_priority = self.transfer_priority frame.transfer_id = self.transfer_id frame.frame_index = len(out_frames) frame.last_frame = len(remaining_payload) <= bytes_per_frame frame.source_node_id = self.source_node_id frame.data_type_id = self.data_type_id frame.dest_node_id = self.dest_node_id frame.payload = remaining_payload[0:bytes_per_frame] frame.request_not_response = self.request_not_response frame.broadcast_not_unicast = False if self.dest_node_id else True out_frames.append(frame) remaining_payload = remaining_payload[bytes_per_frame:] if not remaining_payload: break return out_frames
def test_bytearray(self): self.assertEqual( common.crc16_from_bytes(bytearray('123456789', 'utf-8')), 0x29B1)
def test_bytes(self): self.assertEqual(common.crc16_from_bytes(b'123456789'), 0x29B1)
def test_str(self): self.assertEqual(common.crc16_from_bytes("123456789"), 0x29B1)
def test_bytearray(self): self.assertEqual(common.crc16_from_bytes(bytearray("123456789", "utf-8")), 0x29B1)