aggiunta nella finestra di debug sfp l'interpretazione dei dati header

This commit is contained in:
VALLONGOL 2025-10-16 11:49:57 +02:00
parent 3bee128952
commit ead6c13ba2
3 changed files with 154 additions and 1 deletions

Binary file not shown.

View File

@ -43,7 +43,7 @@ class SfpTransport:
"""Manages SFP communication and payload reassembly.""" """Manages SFP communication and payload reassembly."""
# --- INIZIO MODIFICHE --- # --- INIZIO MODIFICHE ---
def __init__(self, host: str, port: int, payload_handlers: Dict[int, PayloadHandler], ack_config: Optional[Dict[int, int]] = None): def __init__(self, host: str, port: int, payload_handlers: Dict[int, PayloadHandler], ack_config: Optional[Dict[int, int]] = None, raw_packet_callback: Optional[Callable[[bytes, tuple], None]] = None):
""" """
Initializes the SFP Transport layer. Initializes the SFP Transport layer.
@ -64,6 +64,8 @@ class SfpTransport:
self._payload_handlers = payload_handlers self._payload_handlers = payload_handlers
# --- INIZIO MODIFICHE --- # --- INIZIO MODIFICHE ---
self._ack_config = ack_config if ack_config is not None else {} self._ack_config = ack_config if ack_config is not None else {}
# Optional callback invoked with (raw_packet_bytes, addr) for every packet received
self._raw_packet_callback = raw_packet_callback
# --- FINE MODIFICHE --- # --- FINE MODIFICHE ---
self._socket: Optional[socket.socket] = None self._socket: Optional[socket.socket] = None
self._receiver_thread: Optional[threading.Thread] = None self._receiver_thread: Optional[threading.Thread] = None
@ -127,6 +129,12 @@ class SfpTransport:
data, addr = self._socket.recvfrom(65535) data, addr = self._socket.recvfrom(65535)
if not data: if not data:
continue continue
# Deliver raw packet to optional callback for inspection (non-blocking best-effort)
try:
if self._raw_packet_callback:
self._raw_packet_callback(data, addr)
except Exception:
logger.exception(f"{log_prefix} raw_packet_callback raised an exception")
except socket.timeout: except socket.timeout:
continue continue
except OSError: except OSError:

View File

@ -39,6 +39,8 @@ class DebugPayloadRouter:
self._lock = threading.Lock() self._lock = threading.Lock()
# Buffer to store the last received payload for each flow type # Buffer to store the last received payload for each flow type
self._latest_payloads: Dict[str, bytearray] = {} self._latest_payloads: Dict[str, bytearray] = {}
# Buffer to store the last raw packet received (bytes, addr)
self._last_raw_packet: Optional[tuple] = None
logging.info(f"{self._log_prefix} Initialized.") logging.info(f"{self._log_prefix} Initialized.")
def get_handlers(self) -> Dict[int, PayloadHandler]: def get_handlers(self) -> Dict[int, PayloadHandler]:
@ -69,6 +71,17 @@ class DebugPayloadRouter:
self._latest_payloads = {} self._latest_payloads = {}
return new_payloads return new_payloads
def update_raw_packet(self, raw_bytes: bytes, addr: tuple):
"""Store the last raw packet received (overwritten by subsequent packets)."""
with self._lock:
self._last_raw_packet = (raw_bytes, addr)
def get_and_clear_raw_packet(self) -> Optional[tuple]:
with self._lock:
pkt = self._last_raw_packet
self._last_raw_packet = None
return pkt
# --- Main Debug Window Class --- # --- Main Debug Window Class ---
class SfpDebugWindow(tk.Toplevel): class SfpDebugWindow(tk.Toplevel):
@ -131,6 +144,16 @@ class SfpDebugWindow(tk.Toplevel):
self.notebook.add(self.mfd_tab["frame"], text="MFD Image") self.notebook.add(self.mfd_tab["frame"], text="MFD Image")
self.sar_tab = self._create_image_tab("SAR Image") self.sar_tab = self._create_image_tab("SAR Image")
self.notebook.add(self.sar_tab["frame"], text="SAR Image") self.notebook.add(self.sar_tab["frame"], text="SAR Image")
# Raw SFP packet view
self.raw_tab = scrolledtext.ScrolledText(self.notebook, state=tk.DISABLED, wrap=tk.NONE, font=("Consolas", 9))
self.notebook.add(self.raw_tab, text="SFP Raw")
# Configure visual tags for flags (set/unset)
try:
self.raw_tab.tag_config("flag_set", background="#d4ffd4", foreground="#006400")
self.raw_tab.tag_config("flag_unset", background="#f0f0f0", foreground="#808080")
self.raw_tab.tag_config("hdr_field", foreground="#000080")
except Exception:
pass
self.bin_tab = scrolledtext.ScrolledText(self.notebook, state=tk.DISABLED, wrap=tk.NONE, font=("Consolas", 10)) self.bin_tab = scrolledtext.ScrolledText(self.notebook, state=tk.DISABLED, wrap=tk.NONE, font=("Consolas", 10))
self.notebook.add(self.bin_tab, text="Binary (Hex)") self.notebook.add(self.bin_tab, text="Binary (Hex)")
self.json_tab = scrolledtext.ScrolledText(self.notebook, state=tk.DISABLED, wrap=tk.WORD, font=("Consolas", 10)) self.json_tab = scrolledtext.ScrolledText(self.notebook, state=tk.DISABLED, wrap=tk.WORD, font=("Consolas", 10))
@ -220,6 +243,13 @@ class SfpDebugWindow(tk.Toplevel):
else: else:
self._log_to_widget("Connection failed. Check IP/Port and logs.", "ERROR") self._log_to_widget("Connection failed. Check IP/Port and logs.", "ERROR")
self.sfp_transport = None self.sfp_transport = None
# Register raw packet callback regardless of start result (safe no-op if None)
if self.sfp_transport:
# Provide the router.update_raw_packet method as callback
try:
self.sfp_transport._raw_packet_callback = self.payload_router.update_raw_packet
except Exception:
self.logger.exception("Failed to register raw_packet_callback on SfpTransport")
def _on_disconnect(self): def _on_disconnect(self):
if self.sfp_transport: if self.sfp_transport:
@ -260,6 +290,121 @@ class SfpDebugWindow(tk.Toplevel):
# Reschedule the next check # Reschedule the next check
self.after(self.GUI_POLL_INTERVAL_MS, self._process_latest_payloads) self.after(self.GUI_POLL_INTERVAL_MS, self._process_latest_payloads)
# Also check and display last raw packet
raw_pkt = self.payload_router.get_and_clear_raw_packet()
if raw_pkt:
raw_bytes, addr = raw_pkt
self._display_raw_packet(raw_bytes, addr)
def _display_raw_packet(self, raw_bytes: bytes, addr: tuple):
"""Show the raw SFP packet bytes and the parsed header (if possible)."""
try:
header_size = SFPHeader.size()
if len(raw_bytes) < header_size:
raise ValueError("Packet smaller than SFP header")
header = SFPHeader.from_buffer_copy(raw_bytes)
body = raw_bytes[header_size:]
# Build a compact two-column header table to save horizontal space
field_list = [
"SFP_MARKER", "SFP_DIRECTION", "SFP_PROT_VER", "SFP_PT_SPARE",
"SFP_TAG", "SFP_SRC", "SFP_FLOW", "SFP_TID",
"SFP_FLAGS", "SFP_WIN", "SFP_ERR", "SFP_ERR_INFO",
"SFP_TOTFRGAS", "SFP_FRAG", "SFP_RECTYPE", "SFP_RECSPARE",
"SFP_PLDAP", "SFP_PLEXT", "SFP_RECCOUNTER", "SFP_PLSIZE",
"SFP_TOTSIZE", "SFP_PLOFFSET"
]
# Collect (label, value) pairs, handle FLAGS specially
pairs = []
flag_val = None
for f in field_list:
try:
val = getattr(header, f)
except Exception:
val = '<N/A>'
if f == 'SFP_FLAGS':
flag_val = val
# still include placeholder for alignment; actual flags printed later
pairs.append((f, f"{val} (0x{val:X})" if isinstance(val, int) else str(val)))
continue
if isinstance(val, int):
pairs.append((f, f"{val} (0x{val:X})"))
else:
pairs.append((f, str(val)))
# Render two columns: pair up items two-per-line
self.raw_tab.config(state=tk.NORMAL)
self.raw_tab.delete("1.0", tk.END)
self.raw_tab.insert(tk.END, f"From {addr}\n\nSFP Header:\n\n", "hdr_field")
col_width = 36 # width for each column block
for i in range(0, len(pairs), 2):
left = pairs[i]
right = pairs[i+1] if (i+1) < len(pairs) else None
left_text = f"{left[0]:12s}: {left[1]}"
if right:
right_text = f"{right[0]:12s}: {right[1]}"
# Pad left_text to column width then append right_text
line = f"{left_text:<{col_width}} {right_text}"
else:
line = left_text
self.raw_tab.insert(tk.END, line + "\n", "hdr_field")
# FLAG decoding: example bits mapping (adjust if protocol defines differently)
# Let's assume bit0 = ACK, bit1 = MORE, bit2 = RESERVED, etc.
flag_defs = [
(0, "ACK"),
(1, "MORE"),
(2, "RESV2"),
(3, "RESV3"),
(4, "RESV4"),
(5, "RESV5"),
(6, "RESV6"),
(7, "RESV7"),
]
flag_line_start = self.raw_tab.index(tk.END)
self.raw_tab.insert(tk.END, f"SFP_FLAGS : {flag_val} (0x{flag_val:X}) ")
# Append colored flag labels
for bit, name in flag_defs:
is_set = False
try:
is_set = bool((flag_val >> bit) & 1)
except Exception:
is_set = False
tag = "flag_set" if is_set else "flag_unset"
self.raw_tab.insert(tk.END, f" [{name}]", tag)
# Fixed legend text for flags (always visible)
self.raw_tab.insert(tk.END, "\n\nFlags legend:\n", "hdr_field")
legend_map = {
"ACK": "Acknowledgement requested/received",
"MORE": "More fragments follow (not final fragment)",
"RESV2": "Reserved",
"RESV3": "Reserved",
"RESV4": "Reserved",
"RESV5": "Reserved",
"RESV6": "Reserved",
"RESV7": "Reserved",
}
for _, name in flag_defs:
desc = legend_map.get(name, "")
self.raw_tab.insert(tk.END, f" {name:6s}: {desc}\n")
self.raw_tab.insert(tk.END, "\nBODY (hex):\n")
self.raw_tab.insert(tk.END, self._format_hex_dump(body))
self.raw_tab.config(state=tk.DISABLED)
return
except Exception as e:
text = f"Failed to format raw packet: {e}\n\nRaw dump:\n"
text += self._format_hex_dump(raw_bytes)
self.raw_tab.config(state=tk.NORMAL)
self.raw_tab.delete("1.0", tk.END)
self.raw_tab.insert("1.0", text)
self.raw_tab.config(state=tk.DISABLED)
def _display_image_data(self, payload: bytearray, tab_widgets: Dict[str, Any], photo_attr: str): def _display_image_data(self, payload: bytearray, tab_widgets: Dict[str, Any], photo_attr: str):
"""Parses an image payload and displays it. Now handles simplified structure.""" """Parses an image payload and displays it. Now handles simplified structure."""
try: try: