sistemata preview della traiettoria simulata in editing
This commit is contained in:
parent
8a4f621f0c
commit
ab22246f54
@ -3,7 +3,7 @@
|
|||||||
"scan_limit": 60,
|
"scan_limit": 60,
|
||||||
"max_range": 100,
|
"max_range": 100,
|
||||||
"geometry": "1599x1089+587+179",
|
"geometry": "1599x1089+587+179",
|
||||||
"last_selected_scenario": "scenario1",
|
"last_selected_scenario": "scenario_dritto",
|
||||||
"connection": {
|
"connection": {
|
||||||
"target": {
|
"target": {
|
||||||
"type": "sfp",
|
"type": "sfp",
|
||||||
|
|||||||
@ -28,6 +28,7 @@ class SimulationEngine(threading.Thread):
|
|||||||
communicator: Optional[CommunicatorInterface],
|
communicator: Optional[CommunicatorInterface],
|
||||||
simulation_hub: Optional[SimulationStateHub] = None,
|
simulation_hub: Optional[SimulationStateHub] = None,
|
||||||
archive: Optional[str] = None,
|
archive: Optional[str] = None,
|
||||||
|
update_queue: Optional[Queue] = None,
|
||||||
):
|
):
|
||||||
super().__init__(daemon=True, name="SimulationEngineThread")
|
super().__init__(daemon=True, name="SimulationEngineThread")
|
||||||
self.logger = get_logger(__name__)
|
self.logger = get_logger(__name__)
|
||||||
@ -35,13 +36,19 @@ class SimulationEngine(threading.Thread):
|
|||||||
self.communicator = communicator
|
self.communicator = communicator
|
||||||
# Backwards-compat: older callers passed an update_queue as the
|
# Backwards-compat: older callers passed an update_queue as the
|
||||||
# second positional argument. Detect a Queue and treat it as the
|
# second positional argument. Detect a Queue and treat it as the
|
||||||
# update queue while leaving simulation_hub unset.
|
# update queue while leaving simulation_hub unset. Also accept an
|
||||||
|
# explicit `update_queue` keyword argument for newer callers.
|
||||||
self.update_queue: Optional[Queue] = None
|
self.update_queue: Optional[Queue] = None
|
||||||
if isinstance(simulation_hub, Queue):
|
if isinstance(simulation_hub, Queue):
|
||||||
self.update_queue = simulation_hub
|
self.update_queue = simulation_hub
|
||||||
self.simulation_hub = None
|
self.simulation_hub = None
|
||||||
else:
|
else:
|
||||||
self.simulation_hub = simulation_hub # Hub for data analysis
|
self.simulation_hub = simulation_hub # Hub for data analysis
|
||||||
|
|
||||||
|
# If caller provided update_queue explicitly, prefer it (overrides
|
||||||
|
# any queue accidentally passed in simulation_hub position).
|
||||||
|
if isinstance(update_queue, Queue):
|
||||||
|
self.update_queue = update_queue
|
||||||
self.archive = archive # Archive path if needed
|
self.archive = archive # Archive path if needed
|
||||||
self.time_multiplier = 1.0
|
self.time_multiplier = 1.0
|
||||||
self.update_interval_s = 1.0
|
self.update_interval_s = 1.0
|
||||||
@ -149,12 +156,44 @@ class SimulationEngine(threading.Thread):
|
|||||||
|
|
||||||
if self.scenario.is_finished():
|
if self.scenario.is_finished():
|
||||||
self.logger.info("Scenario finished. Stopping engine.")
|
self.logger.info("Scenario finished. Stopping engine.")
|
||||||
|
# Notify any GUI preview consumers that the simulation finished
|
||||||
|
try:
|
||||||
|
if self.update_queue:
|
||||||
|
try:
|
||||||
|
self.update_queue.put_nowait("SIMULATION_FINISHED")
|
||||||
|
except Exception:
|
||||||
|
# fallback to blocking put to ensure delivery
|
||||||
|
try:
|
||||||
|
self.update_queue.put("SIMULATION_FINISHED")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
break
|
break
|
||||||
|
|
||||||
# --- Throttled Communication Step ---
|
# --- Throttled Communication Step ---
|
||||||
if (current_time - self._last_update_time) >= self.update_interval_s:
|
if (current_time - self._last_update_time) >= self.update_interval_s:
|
||||||
self._last_update_time = current_time
|
self._last_update_time = current_time
|
||||||
|
|
||||||
|
# If an update_queue is provided (e.g., preview mode), send the
|
||||||
|
# current simulated targets to the queue so the UI can render
|
||||||
|
# them even when no communicator is present.
|
||||||
|
if self.update_queue:
|
||||||
|
try:
|
||||||
|
# Send a shallow list of target objects (UI will copy if needed)
|
||||||
|
targets_snapshot = [t for t in self.scenario.get_all_targets() if t.active]
|
||||||
|
try:
|
||||||
|
self.update_queue.put_nowait(targets_snapshot)
|
||||||
|
except Exception:
|
||||||
|
# fallback to blocking put
|
||||||
|
try:
|
||||||
|
self.update_queue.put(targets_snapshot)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
except Exception:
|
||||||
|
# Don't let queue failures stop the engine
|
||||||
|
pass
|
||||||
|
|
||||||
if self.communicator and self.communicator.is_open:
|
if self.communicator and self.communicator.is_open:
|
||||||
commands_to_send = []
|
commands_to_send = []
|
||||||
|
|
||||||
|
|||||||
@ -97,7 +97,11 @@ class PPIDisplay(ttk.Frame):
|
|||||||
top_frame.pack(side=tk.TOP, fill=tk.X, padx=5, pady=5)
|
top_frame.pack(side=tk.TOP, fill=tk.X, padx=5, pady=5)
|
||||||
self.controls_frame = ttk.Frame(top_frame)
|
self.controls_frame = ttk.Frame(top_frame)
|
||||||
self.controls_frame.pack(side=tk.LEFT, fill=tk.X, expand=True)
|
self.controls_frame.pack(side=tk.LEFT, fill=tk.X, expand=True)
|
||||||
ttk.Label(self.controls_frame, text="Range (NM):").pack(side=tk.LEFT)
|
# Use grid inside controls_frame so we can stack the antenna animation
|
||||||
|
# toggle immediately below the range selector as requested.
|
||||||
|
ttk.Label(self.controls_frame, text="Range (NM):").grid(
|
||||||
|
row=0, column=0, sticky="w"
|
||||||
|
)
|
||||||
all_steps = [10, 20, 40, 80, 100, 160, 240, 320]
|
all_steps = [10, 20, 40, 80, 100, 160, 240, 320]
|
||||||
valid_steps = sorted([s for s in all_steps if s <= self.max_range])
|
valid_steps = sorted([s for s in all_steps if s <= self.max_range])
|
||||||
if not valid_steps or self.max_range not in valid_steps:
|
if not valid_steps or self.max_range not in valid_steps:
|
||||||
@ -111,7 +115,17 @@ class PPIDisplay(ttk.Frame):
|
|||||||
state="readonly",
|
state="readonly",
|
||||||
width=5,
|
width=5,
|
||||||
)
|
)
|
||||||
self.range_selector.pack(side=tk.LEFT, padx=5)
|
self.range_selector.grid(row=0, column=1, padx=5, sticky="w")
|
||||||
|
# Place the antenna animate toggle directly under the range selector
|
||||||
|
# to match the requested layout: range combobox, then antenna flag.
|
||||||
|
cb_antenna_local = ttk.Checkbutton(
|
||||||
|
self.controls_frame,
|
||||||
|
text="Animate Antenna",
|
||||||
|
variable=self.animate_antenna_var,
|
||||||
|
command=self._on_antenna_animate_changed,
|
||||||
|
)
|
||||||
|
cb_antenna_local.grid(row=1, column=0, columnspan=2, sticky="w", pady=(4, 0))
|
||||||
|
|
||||||
options_frame = ttk.LabelFrame(top_frame, text="Display Options")
|
options_frame = ttk.LabelFrame(top_frame, text="Display Options")
|
||||||
options_frame.pack(side=tk.RIGHT, padx=(10, 0))
|
options_frame.pack(side=tk.RIGHT, padx=(10, 0))
|
||||||
cb_sim_points = ttk.Checkbutton(
|
cb_sim_points = ttk.Checkbutton(
|
||||||
@ -142,24 +156,24 @@ class PPIDisplay(ttk.Frame):
|
|||||||
command=self._on_display_options_changed,
|
command=self._on_display_options_changed,
|
||||||
)
|
)
|
||||||
cb_real_trail.grid(row=1, column=1, sticky="w", padx=5)
|
cb_real_trail.grid(row=1, column=1, sticky="w", padx=5)
|
||||||
# Antenna animate toggle (single Checkbutton)
|
# Legend: stack simulated and real swatches vertically to reduce width
|
||||||
cb_antenna = ttk.Checkbutton(
|
|
||||||
options_frame,
|
|
||||||
text="Animate Antenna",
|
|
||||||
variable=self.animate_antenna_var,
|
|
||||||
command=self._on_antenna_animate_changed,
|
|
||||||
)
|
|
||||||
cb_antenna.grid(row=2, column=0, columnspan=2, sticky="w", padx=5, pady=(6, 0))
|
|
||||||
legend_frame = ttk.Frame(top_frame)
|
legend_frame = ttk.Frame(top_frame)
|
||||||
legend_frame.pack(side=tk.RIGHT, padx=(10, 5))
|
legend_frame.pack(side=tk.RIGHT, padx=(10, 5))
|
||||||
sim_sw = tk.Canvas(legend_frame, width=16, height=12, highlightthickness=0)
|
# Use grid so items are stacked
|
||||||
|
sim_sw = tk.Canvas(
|
||||||
|
legend_frame, width=16, height=12, highlightthickness=0
|
||||||
|
)
|
||||||
sim_sw.create_rectangle(0, 0, 16, 12, fill="green", outline="black")
|
sim_sw.create_rectangle(0, 0, 16, 12, fill="green", outline="black")
|
||||||
sim_sw.pack(side=tk.LEFT, padx=(0, 4))
|
sim_sw.grid(row=0, column=0, padx=(0, 4), pady=(0, 2))
|
||||||
ttk.Label(legend_frame, text="Simulated").pack(side=tk.LEFT, padx=(0, 8))
|
ttk.Label(legend_frame, text="Simulated").grid(
|
||||||
real_sw = tk.Canvas(legend_frame, width=16, height=12, highlightthickness=0)
|
row=0, column=1, sticky="w", padx=(0, 8), pady=(0, 2)
|
||||||
|
)
|
||||||
|
real_sw = tk.Canvas(
|
||||||
|
legend_frame, width=16, height=12, highlightthickness=0
|
||||||
|
)
|
||||||
real_sw.create_rectangle(0, 0, 16, 12, fill="red", outline="black")
|
real_sw.create_rectangle(0, 0, 16, 12, fill="red", outline="black")
|
||||||
real_sw.pack(side=tk.LEFT, padx=(2, 4))
|
real_sw.grid(row=1, column=0, padx=(0, 4))
|
||||||
ttk.Label(legend_frame, text="Real").pack(side=tk.LEFT)
|
ttk.Label(legend_frame, text="Real").grid(row=1, column=1, sticky="w", padx=(0, 8))
|
||||||
|
|
||||||
def _create_plot(self):
|
def _create_plot(self):
|
||||||
fig = Figure(figsize=(5, 5), dpi=100, facecolor="#3E3E3E")
|
fig = Figure(figsize=(5, 5), dpi=100, facecolor="#3E3E3E")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user