Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dronecan_gui_tool/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
#
#
__version__ = 1, 2, 28
__flytrex_version__ = 0, 0, 1
__flytrex_version__ = 0, 0, 2


93 changes: 92 additions & 1 deletion dronecan_gui_tool/widgets/bus_monitor/transfer_decoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 '<unknown>'
except Exception:
dtype = None
type_name = '<unknown>'

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
Expand Down Expand Up @@ -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
2 changes: 1 addition & 1 deletion pydronecan