From 3b37f64960c7a97a64aa0008af4636991ddb1dc2 Mon Sep 17 00:00:00 2001 From: "Ilan.G" Date: Wed, 17 Dec 2025 08:31:58 +0200 Subject: [PATCH] 1. Add extra fields to the packet parse in "node monitor" window: - Type - Frame Type - Data Type ID - Priority - Mode - Anonymous - First/last tail - Computed CRC - CRC match - Payload data 2. Point pydronecan to branch 'Flytrex_1_0_26' 3. Increment flytrex version to 0.0.2 --- dronecan_gui_tool/version.py | 2 +- .../widgets/bus_monitor/transfer_decoder.py | 93 ++++++++++++++++++- pydronecan | 2 +- 3 files changed, 94 insertions(+), 3 deletions(-) diff --git a/dronecan_gui_tool/version.py b/dronecan_gui_tool/version.py index a43ce3c..4acb2a6 100644 --- a/dronecan_gui_tool/version.py +++ b/dronecan_gui_tool/version.py @@ -9,6 +9,6 @@ # # __version__ = 1, 2, 28 -__flytrex_version__ = 0, 0, 1 +__flytrex_version__ = 0, 0, 2 diff --git a/dronecan_gui_tool/widgets/bus_monitor/transfer_decoder.py b/dronecan_gui_tool/widgets/bus_monitor/transfer_decoder.py index 2b5bca4..9047402 100644 --- a/dronecan_gui_tool/widgets/bus_monitor/transfer_decoder.py +++ b/dronecan_gui_tool/widgets/bus_monitor/transfer_decoder.py @@ -32,6 +32,96 @@ def _is_end_of_transfer(frame): return frame.data[-1] & 0b01000000 +def get_payload_from_transfer(transfer, frames=None): + """ + Inspect the recovered transfer to determine its DSDL type and + render the payload fields in a readable form. + + Uses the decoded, typed payload constructed by Transfer.from_frames(). + """ + try: + dtype = dronecan.get_dronecan_data_type(transfer.payload) + type_name = dtype.full_name if dtype else '' + except Exception: + dtype = None + type_name = '' + + kind = 'service' if transfer.service_not_message else 'message' + mode = None + if transfer.service_not_message: + mode = 'request' if transfer.request_not_response else 'response' + + # Anonymous applies to message frames with source_node_id == 0 + is_anonymous = (not transfer.service_not_message) and (transfer.source_node_id == 0) + + header = [ + f"Type: {type_name}", + f"Frame Type: {kind}", + f"Data Type ID: {transfer.data_type_id}", + f"Priority: {transfer.transfer_priority}", + ] + if mode: + header.append(f"Mode: {mode}") + header.append(f"Anonymous: {'yes' if is_anonymous else 'no'}") + if is_anonymous and transfer.discriminator is not None: + header.append(f"Discriminator: {transfer.discriminator}") + # Tail byte diagnostics and payload bytes if frames provided + if frames: + try: + def fmt_tail(tb): + tid = tb & 0x1F + tog = 1 if (tb & 0x20) else 0 + eot = 1 if (tb & 0x40) else 0 + sot = 1 if (tb & 0x80) else 0 + return f"0x{tb:02X} [Start of Transfer={sot} End of Transfer={eot} Toggle={tog} Transfer ID={tid}]" + + first_tail = frames[0].data[-1] if len(frames[0].data) else None + last_tail = frames[-1].data[-1] if len(frames[-1].data) else None + header.append(f"Frames: {len(frames)}") + if first_tail is not None: + header.append(f"First tail: {fmt_tail(first_tail)}") + if last_tail is not None and (len(frames) > 1 or last_tail != first_tail): + header.append(f"Last tail: {fmt_tail(last_tail)}") + + # Payload bytes (per frame) and reconstructed payload + header.append("Payload bytes (per frame):") + reconstructed = bytearray() + for idx, f in enumerate(frames): + part = bytes(f.data[:-1]) if len(f.data) else b"" + reconstructed += part + hex_part = ' '.join(f"{b:02X}" for b in part) + header.append(f" F{idx}: {len(part)} bytes: {hex_part}") + header.append(f"Reconstructed payload: {len(reconstructed)} bytes") + if reconstructed: + hex_full = ' '.join(f"{b:02X}" for b in reconstructed) + header.append(f"Payload hex: {hex_full}") + + # CRC reporting for multi-frame transfers + if len(frames) > 1: + payload_bytes = bytearray(b''.join(bytes(f.data[:-1]) for f in frames)) + if len(payload_bytes) >= 2: + transfer_crc = payload_bytes[0] | (payload_bytes[1] << 8) + header.append(f"Transfer CRC (from frames): 0x{transfer_crc:04X}") + try: + dtype = dronecan.get_dronecan_data_type(transfer.payload) + base_crc = getattr(dtype, 'base_crc', None) + if base_crc is not None: + computed = dronecan.dsdl.common.crc16_from_bytes(payload_bytes[2:], initial=base_crc) + header.append(f"Computed CRC: 0x{computed:04X} (base 0x{base_crc:04X})") + header.append(f"CRC match: {'yes' if computed == transfer_crc else 'no'}") + except Exception: + pass + except Exception: + # Reporting is best-effort; ignore failures + pass + + yaml_header = f"\nParsed payload:" + yaml_dump = dronecan.to_yaml(transfer.payload) + + parts = ['\n'.join(header), yaml_header, yaml_dump] + return "\n".join(parts) + + def decode_transfer_from_frame(entry_row, row_to_frame): entry_frame, direction = row_to_frame(entry_row) can_id = entry_frame.id @@ -66,4 +156,5 @@ def decode_transfer_from_frame(entry_row, row_to_frame): tr = Transfer() tr.from_frames([Frame(x.id, x.data, canfd=x.canfd) for x in frames]) - return related_rows, dronecan.to_yaml(tr.payload) + full_text = get_payload_from_transfer(tr, frames) + return related_rows, full_text diff --git a/pydronecan b/pydronecan index d4f219a..67ced1f 160000 --- a/pydronecan +++ b/pydronecan @@ -1 +1 @@ -Subproject commit d4f219a289b14de081330e84f7ce6e53134a9ba2 +Subproject commit 67ced1f2f3599bf5611bd3b452efe449cc1ee4a5