tolto visualizzazione ppi da debug e rivista la visualizzazione dei dati in debug
This commit is contained in:
parent
0bc190a257
commit
243dc5732b
@ -169,6 +169,9 @@ class SimulationStateHub:
|
||||
"""Clears all stored data for all targets."""
|
||||
with self._lock:
|
||||
self._target_data.clear()
|
||||
# also clear heading caches
|
||||
self._latest_real_heading.clear()
|
||||
self._latest_raw_heading.clear()
|
||||
|
||||
def _initialize_target(self, target_id: int):
|
||||
"""Internal helper to create the data structure for a new target."""
|
||||
@ -177,3 +180,17 @@ class SimulationStateHub:
|
||||
"simulated": collections.deque(maxlen=self._history_size),
|
||||
"real": collections.deque(maxlen=self._history_size),
|
||||
}
|
||||
|
||||
def remove_target(self, target_id: int):
|
||||
"""Remove all stored data for a specific target id."""
|
||||
with self._lock:
|
||||
try:
|
||||
tid = int(target_id)
|
||||
if tid in self._target_data:
|
||||
del self._target_data[tid]
|
||||
if tid in self._latest_real_heading:
|
||||
del self._latest_real_heading[tid]
|
||||
if tid in self._latest_raw_heading:
|
||||
del self._latest_raw_heading[tid]
|
||||
except Exception:
|
||||
pass
|
||||
@ -137,6 +137,19 @@ class DebugPayloadRouter:
|
||||
try:
|
||||
parsed_for_hub = SfpRisStatusPayload.from_buffer_copy(payload)
|
||||
ts_s = parsed_for_hub.scenario.timetag / 1000.0
|
||||
|
||||
# First: remove any targets that the server marked as inactive (flags == 0)
|
||||
try:
|
||||
for i, ris_t in enumerate(parsed_for_hub.tgt.tgt):
|
||||
try:
|
||||
if ris_t.flags == 0 and self._hub and hasattr(self._hub, 'remove_target'):
|
||||
self._hub.remove_target(i)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Add real states for active targets
|
||||
for target in real_targets:
|
||||
state_tuple = (
|
||||
getattr(target, '_pos_x_ft', 0.0),
|
||||
@ -146,6 +159,7 @@ class DebugPayloadRouter:
|
||||
self._hub.add_real_state(
|
||||
target_id=target.target_id, timestamp=ts_s, state=state_tuple
|
||||
)
|
||||
|
||||
# Propagate heading information (if available) into the hub so
|
||||
# GUI builders that reconstruct lightweight Target objects
|
||||
# from the hub can also pick up the last known heading.
|
||||
@ -157,6 +171,7 @@ class DebugPayloadRouter:
|
||||
except Exception:
|
||||
# Never allow heading propagation to break payload handling
|
||||
self._logger.debug("Failed to propagate heading to hub", exc_info=True)
|
||||
|
||||
if self._update_queue:
|
||||
try:
|
||||
self._update_queue.put_nowait([])
|
||||
|
||||
@ -11,6 +11,7 @@ import datetime
|
||||
import os
|
||||
import ctypes
|
||||
import math
|
||||
import socket
|
||||
from queue import Queue, Empty
|
||||
from typing import Dict, Optional, Any, List
|
||||
|
||||
@ -27,7 +28,7 @@ from target_simulator.core.sfp_structures import ImageLeaderData, SFPHeader
|
||||
from target_simulator.gui.payload_router import DebugPayloadRouter
|
||||
from target_simulator.core.models import Target, Waypoint, ManeuverType, KNOTS_TO_FPS
|
||||
from target_simulator.core import command_builder
|
||||
from target_simulator.gui.ppi_display import PPIDisplay
|
||||
|
||||
|
||||
DEF_TEST_ID = 1
|
||||
DEF_TEST_RANGE = 30.0
|
||||
@ -67,7 +68,6 @@ class SfpDebugWindow(tk.Toplevel):
|
||||
self.payload_router.add_ris_target_listener(self._queue_ris_target_update)
|
||||
|
||||
self.image_area_size = 150
|
||||
self._ppi_visible = False
|
||||
self.ip_var = tk.StringVar(value="127.0.0.1")
|
||||
self.local_port_var = tk.StringVar(value="60002")
|
||||
self.server_port_var = tk.StringVar(value="60001")
|
||||
@ -81,7 +81,18 @@ class SfpDebugWindow(tk.Toplevel):
|
||||
self.tgt_active_var = tk.BooleanVar(value=True)
|
||||
self.tgt_traceable_var = tk.BooleanVar(value=True)
|
||||
self.tgt_restart_var = tk.BooleanVar(value=False)
|
||||
self._master_mode_names = [ "idle_master_mode", "int_bit_master_mode", "gm_master_mode", "dbs_master_mode", "rws_master_mode", "vs_master_mode", "acm_master_mode", "tws_master_mode", "sea_low_master_mode", "sea_high_master_mode", "gmti_master_mode", "bcn_master_mode", "sam_master_mode", "ta_master_mode", "wa_master_mode", "stt_master_mode", "dtt_master_mode", "sstt_master_mode", "acq_master_mode", "ftt_master_mode", "agr_master_mode", "sar_master_mode", "invalid_master_mode_", "xtst_dummy_mode", "xtst_hw_validation_mode", "boot_master_mode", "master_mode_id_cardinality_", ]
|
||||
# View settings for RIS/status presentation
|
||||
self.scenario_view_mode = tk.StringVar(value='simplified')
|
||||
self.simplified_decimals = tk.IntVar(value=2)
|
||||
self._master_mode_names = [
|
||||
"idle_master_mode", "int_bit_master_mode", "gm_master_mode", "dbs_master_mode",
|
||||
"rws_master_mode", "vs_master_mode", "acm_master_mode", "tws_master_mode",
|
||||
"sea_low_master_mode", "sea_high_master_mode", "gmti_master_mode", "bcn_master_mode",
|
||||
"sam_master_mode", "ta_master_mode", "wa_master_mode", "stt_master_mode",
|
||||
"dtt_master_mode", "sstt_master_mode", "acq_master_mode", "ftt_master_mode",
|
||||
"agr_master_mode", "sar_master_mode", "invalid_master_mode_", "xtst_dummy_mode",
|
||||
"xtst_hw_validation_mode", "boot_master_mode", "master_mode_id_cardinality_",
|
||||
]
|
||||
|
||||
self._create_widgets()
|
||||
|
||||
@ -109,10 +120,11 @@ class SfpDebugWindow(tk.Toplevel):
|
||||
"""
|
||||
This method runs on the GUI thread and processes all queued updates.
|
||||
"""
|
||||
# 1. Process target updates for the PPI
|
||||
# 1. Process target updates (no PPI here; still support queued updates)
|
||||
try:
|
||||
while not self.debug_update_queue.empty():
|
||||
real_targets = self.debug_update_queue.get_nowait()
|
||||
# kept for compatibility; this window no longer shows a PPI
|
||||
self.update_ppi_targets(real_targets)
|
||||
except Empty:
|
||||
pass
|
||||
@ -136,15 +148,18 @@ class SfpDebugWindow(tk.Toplevel):
|
||||
import json
|
||||
struct = json.loads(payload.decode("utf-8")) if isinstance(payload, (bytes, bytearray)) else payload
|
||||
|
||||
# --- START OF NEW LOGIC ---
|
||||
|
||||
view_mode = self.scenario_view_mode.get()
|
||||
decimals = self.simplified_decimals.get()
|
||||
|
||||
# Helper functions for conversion
|
||||
def to_deg(rad): return rad * 180.0 / math.pi
|
||||
def m_s_to_ft_s(ms): return ms * 3.28084
|
||||
def m_to_ft(m): return m * 3.28084
|
||||
def to_deg(rad):
|
||||
return rad * 180.0 / math.pi
|
||||
|
||||
def m_s_to_ft_s(ms):
|
||||
return ms * 3.28084
|
||||
|
||||
def m_to_ft(m):
|
||||
return m * 3.28084
|
||||
|
||||
def decimal_deg_to_dms(deg, is_lat):
|
||||
d = abs(deg)
|
||||
degrees = int(d)
|
||||
@ -176,7 +191,7 @@ class SfpDebugWindow(tk.Toplevel):
|
||||
elif field == 'mode' and value < len(self._master_mode_names):
|
||||
display_value = f"{value} ({self._master_mode_names[value].replace('_master_mode', '')})"
|
||||
elif isinstance(value, list):
|
||||
display_value = str(value) # Keep arrays as string
|
||||
display_value = str(value)
|
||||
|
||||
self.scenario_tree.insert("", tk.END, values=(field, display_value))
|
||||
|
||||
@ -192,7 +207,7 @@ class SfpDebugWindow(tk.Toplevel):
|
||||
x_pos = f"{m_to_ft(t.get('x', 0.0)):.{decimals}f} ft"
|
||||
y_pos = f"{m_to_ft(t.get('y', 0.0)):.{decimals}f} ft"
|
||||
z_pos = f"{m_to_ft(t.get('z', 0.0)):.{decimals}f} ft"
|
||||
else: # Raw mode
|
||||
else:
|
||||
heading = f"{t.get('heading', 0.0):.6f}"
|
||||
x_pos = f"{t.get('x', 0.0):.3f}"
|
||||
y_pos = f"{t.get('y', 0.0):.3f}"
|
||||
@ -201,8 +216,6 @@ class SfpDebugWindow(tk.Toplevel):
|
||||
vals = (i, flags_display, heading, x_pos, y_pos, z_pos)
|
||||
self.ris_tree.insert("", tk.END, values=vals)
|
||||
|
||||
# --- END OF NEW LOGIC ---
|
||||
|
||||
except Exception:
|
||||
self.logger.exception("Failed to update RIS tables from JSON payload.")
|
||||
|
||||
@ -238,27 +251,9 @@ class SfpDebugWindow(tk.Toplevel):
|
||||
self.notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||
self._create_notebook_tabs()
|
||||
|
||||
def _toggle_ppi(self):
|
||||
try:
|
||||
if self._ppi_visible:
|
||||
self.ris_ppi_container.pack_forget()
|
||||
self.ris_table_container.pack(fill=tk.BOTH, expand=True)
|
||||
self._ppi_visible = False
|
||||
self.ppi_toggle_btn.config(text="Show PPI Map")
|
||||
else:
|
||||
self.ris_table_container.pack_forget()
|
||||
self.ris_ppi_container.pack(fill=tk.BOTH, expand=True)
|
||||
self._ppi_visible = True
|
||||
self.ppi_toggle_btn.config(text="Hide PPI Map")
|
||||
except Exception:
|
||||
self.logger.exception("Toggle PPI failed")
|
||||
|
||||
def update_ppi_targets(self, targets: List[Target]):
|
||||
try:
|
||||
if self.ris_ppi_widget:
|
||||
self.ris_ppi_widget.update_targets({"real": targets})
|
||||
except Exception:
|
||||
self.logger.exception("Failed to update RIS PPI targets")
|
||||
# PPI removed from SFP Debug Window; keep for compatibility but no-op
|
||||
return
|
||||
|
||||
def _create_connection_widgets(self, parent):
|
||||
ttk.Label(parent, text="IP:").pack(side=tk.LEFT, padx=(4, 2))
|
||||
@ -304,99 +299,125 @@ class SfpDebugWindow(tk.Toplevel):
|
||||
ttk.Button(quick_cmd_frame, text="pause", command=lambda: self._on_send_simple_command(command_builder.build_pause())).pack(side=tk.LEFT, padx=4)
|
||||
ttk.Button(quick_cmd_frame, text="continue", command=lambda: self._on_send_simple_command(command_builder.build_continue())).pack(side=tk.LEFT, padx=4)
|
||||
ttk.Button(quick_cmd_frame, text="tgtset (cur)", command=lambda: self._on_send_tgtset()).pack(side=tk.LEFT, padx=8)
|
||||
self.ppi_toggle_btn = ttk.Button(quick_cmd_frame, text="Show PPI Map", command=self._toggle_ppi)
|
||||
self.ppi_toggle_btn.pack(side=tk.RIGHT, padx=4)
|
||||
|
||||
def _create_script_sender_widgets(self, parent):
|
||||
ttk.Label(parent, text="Script to send:").pack(side=tk.LEFT, padx=(5, 2))
|
||||
ttk.Entry(parent, textvariable=self.script_var, width=60).pack(side=tk.LEFT, expand=True, fill=tk.X, padx=(0, 5))
|
||||
self.send_script_btn = ttk.Button(parent, text="Send script", command=self._on_send_script)
|
||||
self.send_script_btn.pack(side=tk.LEFT, padx=5)
|
||||
# PPI widget and toggle removed from this debug window
|
||||
|
||||
def _create_notebook_tabs(self):
|
||||
self.log_tab = scrolledtext.ScrolledText(self.notebook, state=tk.DISABLED, wrap=tk.WORD, font=("Consolas", 9))
|
||||
self.notebook.add(self.log_tab, text="Raw Log")
|
||||
if _IMAGE_LIBS_AVAILABLE:
|
||||
self.mfd_tab = self._create_image_tab("MFD Image")
|
||||
self.notebook.add(self.mfd_tab["frame"], text="MFD Image")
|
||||
self.sar_tab = self._create_image_tab("SAR Image")
|
||||
self.notebook.add(self.sar_tab["frame"], text="SAR Image")
|
||||
"""Create the notebook tabs used by the SFP Debug Window.
|
||||
|
||||
This method ensures the widgets referenced elsewhere (log_tab, raw_tab_text,
|
||||
ris_tree, scenario_tree, history_tree, image tabs, bin/json views) exist.
|
||||
"""
|
||||
# Raw packet tab
|
||||
raw_frame = ttk.Frame(self.notebook)
|
||||
self.raw_tab_text = scrolledtext.ScrolledText(raw_frame, height=12, state=tk.DISABLED, wrap=tk.NONE, font=("Consolas", 9))
|
||||
self.raw_tab_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||
self.notebook.add(raw_frame, text="Raw")
|
||||
|
||||
# Log tab
|
||||
log_frame = ttk.Frame(self.notebook)
|
||||
self.log_tab = scrolledtext.ScrolledText(log_frame, height=12, state=tk.DISABLED, wrap=tk.WORD, font=("Consolas", 9))
|
||||
self.log_tab.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||
self.notebook.add(log_frame, text="Log")
|
||||
|
||||
# RIS tab - scenario and targets (side-by-side)
|
||||
ris_frame = ttk.Frame(self.notebook)
|
||||
|
||||
# Controls above the paned area: view mode (raw/simplified) and decimals
|
||||
controls = ttk.Frame(ris_frame)
|
||||
controls.pack(side=tk.TOP, fill=tk.X, padx=5, pady=(4, 2))
|
||||
ttk.Label(controls, text="View:").pack(side=tk.LEFT, padx=(2, 4))
|
||||
self.scenario_view_mode_opt = ttk.OptionMenu(controls, self.scenario_view_mode, self.scenario_view_mode.get(), 'simplified', 'raw')
|
||||
self.scenario_view_mode_opt.pack(side=tk.LEFT)
|
||||
ttk.Label(controls, text="Decimals:").pack(side=tk.LEFT, padx=(12, 4))
|
||||
self.dec_spin = ttk.Spinbox(controls, from_=0, to=6, textvariable=self.simplified_decimals, width=4)
|
||||
self.dec_spin.pack(side=tk.LEFT)
|
||||
|
||||
# Use a paned window to place scenario (left) and targets (right) side-by-side
|
||||
paned = ttk.Panedwindow(ris_frame, orient=tk.HORIZONTAL)
|
||||
paned.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||
|
||||
left = ttk.Frame(paned)
|
||||
right = ttk.Frame(paned)
|
||||
paned.add(left, weight=1)
|
||||
paned.add(right, weight=3)
|
||||
|
||||
# Scenario tree on the left with scrollbar
|
||||
self.scenario_tree = ttk.Treeview(left, columns=("field", "value"), show="headings", height=12)
|
||||
self.scenario_tree.heading("field", text="Field")
|
||||
self.scenario_tree.heading("value", text="Value")
|
||||
self.scenario_tree.column("field", width=140, anchor="w")
|
||||
self.scenario_tree.column("value", width=160, anchor="w")
|
||||
self.scenario_tree.pack(fill=tk.BOTH, expand=True)
|
||||
paned.add(left, weight=1)
|
||||
right = ttk.Frame(paned)
|
||||
self.ris_table_container = ttk.Frame(right)
|
||||
self.ris_ppi_container = ttk.Frame(right)
|
||||
cols = ("idx", "flags", "heading", "x", "y", "z")
|
||||
self.ris_tree = ttk.Treeview(self.ris_table_container, columns=cols, show="headings", height=12)
|
||||
for c, txt in zip(cols, ("#", "flags", "heading", "x", "y", "z")):
|
||||
self.ris_tree.heading(c, text=txt)
|
||||
self.ris_tree.column(c, width=70, anchor="center")
|
||||
self.ris_tree.pack(fill=tk.BOTH, expand=True)
|
||||
self.ris_table_container.pack(fill=tk.BOTH, expand=True)
|
||||
paned.add(right, weight=2)
|
||||
gm = getattr(self.master, "config_manager", None)
|
||||
trail_len = None
|
||||
if gm:
|
||||
general = gm.get_general_settings() or {}
|
||||
trail_len = general.get("ppi_trail_length")
|
||||
self.ris_ppi_widget = PPIDisplay(self.ris_ppi_container, max_range_nm=100, trail_length=trail_len)
|
||||
self.ris_ppi_widget.pack(fill=tk.BOTH, expand=True)
|
||||
btn_frame = ttk.Frame(ris_frame)
|
||||
btn_frame.pack(fill=tk.X, padx=5, pady=(0, 5))
|
||||
self.scenario_view_mode = tk.StringVar(value="simplified")
|
||||
mode_frame = ttk.Frame(btn_frame)
|
||||
mode_frame.pack(side=tk.LEFT, padx=(4, 0))
|
||||
ttk.Label(mode_frame, text="View:").pack(side=tk.LEFT, padx=(0, 6))
|
||||
ttk.Radiobutton(mode_frame, text="Raw", value="raw", variable=self.scenario_view_mode).pack(side=tk.LEFT)
|
||||
ttk.Radiobutton(mode_frame, text="Simplified", value="simplified", variable=self.scenario_view_mode).pack(side=tk.LEFT)
|
||||
self.simplified_decimals = tk.IntVar(value=4)
|
||||
ttk.Label(mode_frame, text=" Decimals:").pack(side=tk.LEFT, padx=(8, 2))
|
||||
ttk.Spinbox(mode_frame, from_=0, to=8, width=3, textvariable=self.simplified_decimals).pack(side=tk.LEFT)
|
||||
self.ris_save_csv_btn = ttk.Button(btn_frame, text="Save CSV", command=self._on_save_ris_csv)
|
||||
self.ris_save_csv_btn.pack(side=tk.RIGHT)
|
||||
self.notebook.add(ris_frame, text="RIS Status")
|
||||
raw_frame = ttk.Frame(self.notebook)
|
||||
history_frame = ttk.Frame(raw_frame, width=380)
|
||||
history_frame.pack(side=tk.LEFT, fill=tk.Y, padx=(5, 2), pady=5)
|
||||
ttk.Label(history_frame, text="History (latest)").pack(anchor=tk.W, padx=4)
|
||||
list_container = ttk.Frame(history_frame)
|
||||
list_container.pack(fill=tk.BOTH, expand=True, padx=4, pady=(2, 4))
|
||||
columns = ("ts", "flow", "tid", "size")
|
||||
self.history_tree = ttk.Treeview(list_container, columns=columns, show="headings", height=20)
|
||||
self.history_tree.heading("ts", text="Timestamp")
|
||||
# Set sensible column widths and stretching
|
||||
self.scenario_tree.column("field", width=160, anchor=tk.W, stretch=False)
|
||||
self.scenario_tree.column("value", width=220, anchor=tk.W, stretch=True)
|
||||
scen_scroll = ttk.Scrollbar(left, orient=tk.VERTICAL, command=self.scenario_tree.yview)
|
||||
self.scenario_tree.configure(yscrollcommand=scen_scroll.set)
|
||||
scen_scroll.pack(side=tk.RIGHT, fill=tk.Y)
|
||||
self.scenario_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||
|
||||
# RIS targets table on the right with vertical and horizontal scrollbars
|
||||
cols = ("index", "flags", "heading", "x", "y", "z")
|
||||
self.ris_tree = ttk.Treeview(right, columns=cols, show="headings", height=16)
|
||||
self.ris_tree.heading("index", text="#")
|
||||
self.ris_tree.heading("flags", text="flags")
|
||||
self.ris_tree.heading("heading", text="heading")
|
||||
self.ris_tree.heading("x", text="x")
|
||||
self.ris_tree.heading("y", text="y")
|
||||
self.ris_tree.heading("z", text="z")
|
||||
# Column sizing
|
||||
self.ris_tree.column("index", width=40, anchor=tk.CENTER, stretch=False)
|
||||
self.ris_tree.column("flags", width=60, anchor=tk.CENTER, stretch=False)
|
||||
self.ris_tree.column("heading", width=80, anchor=tk.CENTER, stretch=False)
|
||||
self.ris_tree.column("x", width=120, anchor=tk.E, stretch=True)
|
||||
self.ris_tree.column("y", width=120, anchor=tk.E, stretch=True)
|
||||
self.ris_tree.column("z", width=100, anchor=tk.E, stretch=False)
|
||||
ris_v = ttk.Scrollbar(right, orient=tk.VERTICAL, command=self.ris_tree.yview)
|
||||
ris_h = ttk.Scrollbar(right, orient=tk.HORIZONTAL, command=self.ris_tree.xview)
|
||||
self.ris_tree.configure(yscrollcommand=ris_v.set, xscrollcommand=ris_h.set)
|
||||
ris_v.pack(side=tk.RIGHT, fill=tk.Y)
|
||||
ris_h.pack(side=tk.BOTTOM, fill=tk.X)
|
||||
self.ris_tree.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||
|
||||
self.notebook.add(ris_frame, text="RIS")
|
||||
|
||||
# History tab
|
||||
history_frame = ttk.Frame(self.notebook)
|
||||
self.history_tree = ttk.Treeview(history_frame, columns=("time", "flow", "tid", "size"), show="headings", height=8)
|
||||
self.history_tree.heading("time", text="Time")
|
||||
self.history_tree.heading("flow", text="Flow")
|
||||
self.history_tree.heading("tid", text="TID")
|
||||
self.history_tree.heading("size", text="Size")
|
||||
self.history_tree.column("ts", width=100, anchor="w")
|
||||
self.history_tree.column("flow", width=50, anchor="w")
|
||||
self.history_tree.column("tid", width=40, anchor="center")
|
||||
self.history_tree.column("size", width=50, anchor="e")
|
||||
self.history_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||
self.history_vscroll = ttk.Scrollbar(list_container, orient=tk.VERTICAL, command=self.history_tree.yview)
|
||||
self.history_vscroll.pack(side=tk.RIGHT, fill=tk.Y)
|
||||
self.history_tree.config(yscrollcommand=self.history_vscroll.set)
|
||||
hb_frame = ttk.Frame(history_frame)
|
||||
hb_frame.pack(fill=tk.X, padx=4, pady=(4, 4))
|
||||
self.history_settings_btn = ttk.Button(hb_frame, text="Settings", command=self._open_history_settings_dialog)
|
||||
self.history_settings_btn.pack(side=tk.LEFT)
|
||||
self.history_clear_btn = ttk.Button(hb_frame, text="Clear", command=self._on_clear_history)
|
||||
self.history_clear_btn.pack(side=tk.RIGHT)
|
||||
self.raw_tab_text = scrolledtext.ScrolledText(raw_frame, state=tk.DISABLED, wrap=tk.NONE, font=("Consolas", 9))
|
||||
self.raw_tab_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(2, 5), pady=5)
|
||||
self.notebook.insert(1, raw_frame, text="SFP Raw")
|
||||
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.json_tab = scrolledtext.ScrolledText(self.notebook, state=tk.DISABLED, wrap=tk.WORD, font=("Consolas", 10))
|
||||
self.notebook.add(self.json_tab, text="JSON")
|
||||
self.history_tree.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||
btn_frame = ttk.Frame(history_frame)
|
||||
btn_frame.pack(fill=tk.X, padx=5, pady=5)
|
||||
ttk.Button(btn_frame, text="Clear", command=self._on_clear_history).pack(side=tk.LEFT)
|
||||
ttk.Button(btn_frame, text="Settings", command=self._open_history_settings_dialog).pack(side=tk.LEFT, padx=4)
|
||||
self.notebook.add(history_frame, text="History")
|
||||
|
||||
# Image tabs (MFD, SAR)
|
||||
self.mfd_tab = self._create_image_tab("MFD")
|
||||
self.notebook.add(self.mfd_tab["frame"], text="MFD")
|
||||
self.sar_tab = self._create_image_tab("SAR")
|
||||
self.notebook.add(self.sar_tab["frame"], text="SAR")
|
||||
|
||||
# Binary and JSON viewers
|
||||
bin_frame = ttk.Frame(self.notebook)
|
||||
self.bin_tab = scrolledtext.ScrolledText(bin_frame, height=12, state=tk.DISABLED, wrap=tk.NONE, font=("Consolas", 9))
|
||||
self.bin_tab.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||
self.notebook.add(bin_frame, text="BIN")
|
||||
|
||||
json_frame = ttk.Frame(self.notebook)
|
||||
self.json_tab = scrolledtext.ScrolledText(json_frame, height=12, state=tk.DISABLED, wrap=tk.NONE, font=("Consolas", 9))
|
||||
self.json_tab.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||
self.notebook.add(json_frame, text="JSON")
|
||||
|
||||
def _create_script_sender_widgets(self, parent):
|
||||
"""Create a small UI to enter and send ad-hoc script/commands."""
|
||||
frame = ttk.Frame(parent, padding=4)
|
||||
frame.pack(fill=tk.X)
|
||||
ttk.Label(frame, text="Script:").pack(side=tk.LEFT, padx=(0, 4))
|
||||
entry = ttk.Entry(frame, textvariable=self.script_var, width=72)
|
||||
entry.pack(side=tk.LEFT, fill=tk.X, expand=True)
|
||||
send_btn = ttk.Button(frame, text="Send", command=self._on_send_script)
|
||||
send_btn.pack(side=tk.LEFT, padx=(6, 0))
|
||||
|
||||
def _on_send_target(self):
|
||||
if not self.shared_communicator or not self.shared_communicator.is_open:
|
||||
|
||||
56
tools/test_ris_remove.py
Normal file
56
tools/test_ris_remove.py
Normal file
@ -0,0 +1,56 @@
|
||||
# Test removal of targets with flags == 0
|
||||
import logging
|
||||
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s [%(levelname)s] %(name)s: %(message)s')
|
||||
|
||||
from target_simulator.gui.payload_router import DebugPayloadRouter
|
||||
from target_simulator.analysis.simulation_state_hub import SimulationStateHub
|
||||
import target_simulator.gui.payload_router as pr_mod
|
||||
|
||||
class FakeRisTarget:
|
||||
def __init__(self, flags, x, y, z, heading):
|
||||
self.flags = flags
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.z = z
|
||||
self.heading = heading
|
||||
|
||||
class FakeScenario:
|
||||
def __init__(self, timetag=123456789):
|
||||
self.timetag = timetag
|
||||
|
||||
class FakeParsed:
|
||||
def __init__(self, tgt_list):
|
||||
self.tgt = type('T', (), {'tgt': tgt_list})()
|
||||
self.scenario = FakeScenario()
|
||||
|
||||
@staticmethod
|
||||
def from_buffer_copy(payload):
|
||||
raise RuntimeError('Should be monkeypatched')
|
||||
|
||||
# first, create initial payload with 2 active targets
|
||||
rads = [0.0, 1.5707964897155762]
|
||||
fake_targets = []
|
||||
for i, h in enumerate(rads):
|
||||
fake_targets.append(FakeRisTarget(flags=1, x=1000+i, y=2000+i, z=100.0, heading=h))
|
||||
|
||||
original_parser = pr_mod.SfpRisStatusPayload
|
||||
|
||||
def make_parser(targets):
|
||||
return type('P', (), {'from_buffer_copy': staticmethod(lambda payload: FakeParsed(targets))})
|
||||
|
||||
# inject initial
|
||||
pr_mod.SfpRisStatusPayload = make_parser(fake_targets)
|
||||
hub = SimulationStateHub()
|
||||
router = DebugPayloadRouter(simulation_hub=hub, update_queue=None)
|
||||
router._handle_ris_status(b'INJ')
|
||||
print('After first injection, hub ids:', hub.get_all_target_ids())
|
||||
|
||||
# now create a payload where target 1 is inactive (flags=0)
|
||||
fake_targets2 = [FakeRisTarget(flags=1, x=1000, y=2000, z=100.0, heading=0.0), FakeRisTarget(flags=0, x=0, y=0, z=0, heading=0.0)]
|
||||
pr_mod.SfpRisStatusPayload = make_parser(fake_targets2)
|
||||
router._handle_ris_status(b'INJ2')
|
||||
print('After second injection (one inactive), hub ids:', hub.get_all_target_ids())
|
||||
|
||||
# restore
|
||||
pr_mod.SfpRisStatusPayload = original_parser
|
||||
print('Done')
|
||||
Loading…
Reference in New Issue
Block a user