rimosso moltiplicatore per simulazione.
This commit is contained in:
parent
d1315c714a
commit
b03777ae13
@ -416,6 +416,38 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"use_spline": false
|
"use_spline": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"target_id": 1,
|
||||||
|
"active": true,
|
||||||
|
"traceable": true,
|
||||||
|
"trajectory": [
|
||||||
|
{
|
||||||
|
"maneuver_type": "Fly to Point",
|
||||||
|
"duration_s": 10.0,
|
||||||
|
"target_range_nm": 40.0,
|
||||||
|
"target_azimuth_deg": -45.0,
|
||||||
|
"target_altitude_ft": 10000.0,
|
||||||
|
"target_velocity_fps": 506.343,
|
||||||
|
"target_heading_deg": 90.0,
|
||||||
|
"longitudinal_acceleration_g": 0.0,
|
||||||
|
"lateral_acceleration_g": 0.0,
|
||||||
|
"vertical_acceleration_g": 0.0,
|
||||||
|
"turn_direction": "Right"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"maneuver_type": "Fly to Point",
|
||||||
|
"duration_s": 30.0,
|
||||||
|
"target_range_nm": 40.0,
|
||||||
|
"target_azimuth_deg": 45.0,
|
||||||
|
"target_altitude_ft": 10000.0,
|
||||||
|
"longitudinal_acceleration_g": 0.0,
|
||||||
|
"lateral_acceleration_g": 0.0,
|
||||||
|
"vertical_acceleration_g": 0.0,
|
||||||
|
"turn_direction": "Right"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"use_spline": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
"scan_limit": 60,
|
"scan_limit": 60,
|
||||||
"max_range": 100,
|
"max_range": 100,
|
||||||
"geometry": "1305x929+587+179",
|
"geometry": "1305x929+587+179",
|
||||||
"last_selected_scenario": "scenario2",
|
"last_selected_scenario": "corto",
|
||||||
"connection": {
|
"connection": {
|
||||||
"target": {
|
"target": {
|
||||||
"type": "sfp",
|
"type": "sfp",
|
||||||
@ -17,8 +17,8 @@
|
|||||||
},
|
},
|
||||||
"sfp": {
|
"sfp": {
|
||||||
"ip": "127.0.0.1",
|
"ip": "127.0.0.1",
|
||||||
"port": 60003,
|
"port": 60013,
|
||||||
"local_port": 60002,
|
"local_port": 60012,
|
||||||
"use_json_protocol": true
|
"use_json_protocol": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -346,21 +346,18 @@ class MainView(tk.Tk):
|
|||||||
spacer = ttk.Frame(engine_frame)
|
spacer = ttk.Frame(engine_frame)
|
||||||
spacer.grid(row=0, column=3, sticky="ew")
|
spacer.grid(row=0, column=3, sticky="ew")
|
||||||
|
|
||||||
|
# Display fixed speed indicator (1x). The real system runs at
|
||||||
|
# 1x and the speed should not be changed at runtime, so show a
|
||||||
|
# read-only label instead of an editable combobox.
|
||||||
ttk.Label(engine_frame, text="Speed:").grid(
|
ttk.Label(engine_frame, text="Speed:").grid(
|
||||||
row=0, column=4, sticky="e", padx=(10, 2), pady=5
|
row=0, column=4, sticky="e", padx=(10, 2), pady=5
|
||||||
)
|
)
|
||||||
self.time_multiplier_var = tk.StringVar(value="1x")
|
self.time_multiplier_var = tk.StringVar(value="1x")
|
||||||
self.multiplier_combo = ttk.Combobox(
|
# Keep attribute name `multiplier_combo` for backward
|
||||||
engine_frame,
|
# compatibility with other code that references it, but expose
|
||||||
textvariable=self.time_multiplier_var,
|
# a non-interactive label instead of a Combobox.
|
||||||
values=["1x", "2x", "4x", "10x", "20x"],
|
self.multiplier_combo = ttk.Label(engine_frame, text="1x")
|
||||||
state="readonly",
|
|
||||||
width=4,
|
|
||||||
)
|
|
||||||
self.multiplier_combo.grid(row=0, column=5, sticky="w", padx=(0, 5), pady=5)
|
self.multiplier_combo.grid(row=0, column=5, sticky="w", padx=(0, 5), pady=5)
|
||||||
self.multiplier_combo.bind(
|
|
||||||
"<<ComboboxSelected>>", self._on_time_multiplier_changed
|
|
||||||
)
|
|
||||||
|
|
||||||
ttk.Label(engine_frame, text="Update Time (s):").grid(
|
ttk.Label(engine_frame, text="Update Time (s):").grid(
|
||||||
row=0, column=6, sticky="e", padx=(10, 2), pady=5
|
row=0, column=6, sticky="e", padx=(10, 2), pady=5
|
||||||
@ -930,6 +927,16 @@ class MainView(tk.Tk):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def _on_start_simulation(self):
|
def _on_start_simulation(self):
|
||||||
|
# Prevent duplicate start attempts: use a simple re-entrancy guard.
|
||||||
|
if getattr(self, "_start_in_progress_main", False):
|
||||||
|
try:
|
||||||
|
self.logger.info("Start already in progress; ignoring duplicate request.")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return
|
||||||
|
|
||||||
|
self._start_in_progress_main = True
|
||||||
|
try:
|
||||||
# Delegate to SimulationController if available
|
# Delegate to SimulationController if available
|
||||||
try:
|
try:
|
||||||
if hasattr(self, "simulation_controller") and self.simulation_controller:
|
if hasattr(self, "simulation_controller") and self.simulation_controller:
|
||||||
@ -939,11 +946,20 @@ class MainView(tk.Tk):
|
|||||||
self.logger.exception("SimulationController start failed; falling back to inline start.")
|
self.logger.exception("SimulationController start failed; falling back to inline start.")
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# If controller is not present or failed, attempt no-op fallback
|
# If controller is not present or failed, attempt no-op fallback
|
||||||
try:
|
try:
|
||||||
messagebox.showerror("Start Error", "Unable to start simulation (controller unavailable).")
|
messagebox.showerror("Start Error", "Unable to start simulation (controller unavailable).")
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
finally:
|
||||||
|
# Clear the guard so future explicit retries are allowed. The
|
||||||
|
# actual button state will be managed by _update_button_states
|
||||||
|
# based on `is_simulation_running`.
|
||||||
|
try:
|
||||||
|
self._start_in_progress_main = False
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
def _on_stop_simulation(self):
|
def _on_stop_simulation(self):
|
||||||
try:
|
try:
|
||||||
@ -1102,15 +1118,45 @@ class MainView(tk.Tk):
|
|||||||
tk.NORMAL if (not is_running and has_data_to_analyze) else tk.DISABLED
|
tk.NORMAL if (not is_running and has_data_to_analyze) else tk.DISABLED
|
||||||
)
|
)
|
||||||
|
|
||||||
state = tk.DISABLED if is_running else tk.NORMAL
|
# If a start is currently in progress (either via the controller
|
||||||
|
# path or via the SimulationControls immediate handler), keep the
|
||||||
|
# Start button disabled to avoid duplicate starts even though
|
||||||
|
# `is_simulation_running` may not yet be True.
|
||||||
|
start_in_progress_flag = False
|
||||||
|
try:
|
||||||
|
if getattr(self, "_start_in_progress_main", False):
|
||||||
|
start_in_progress_flag = True
|
||||||
|
except Exception:
|
||||||
|
start_in_progress_flag = start_in_progress_flag
|
||||||
|
|
||||||
|
try:
|
||||||
|
sc = getattr(self, "simulation_controls", None)
|
||||||
|
if sc and getattr(sc, "_start_in_progress", False):
|
||||||
|
start_in_progress_flag = True
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
state = tk.DISABLED if (is_running or start_in_progress_flag) else tk.NORMAL
|
||||||
|
|
||||||
self.reset_radar_button.config(state=state)
|
self.reset_radar_button.config(state=state)
|
||||||
self.start_button.config(state=tk.DISABLED if is_running else tk.NORMAL)
|
self.start_button.config(state=tk.DISABLED if is_running else tk.NORMAL)
|
||||||
self.stop_button.config(state=tk.NORMAL if is_running else tk.DISABLED)
|
self.stop_button.config(state=tk.NORMAL if is_running else tk.DISABLED)
|
||||||
# Analysis tab has its own controls; nothing to update here.
|
# Analysis tab has its own controls; nothing to update here.
|
||||||
|
# multiplier_combo may be a non-interactive label after the
|
||||||
|
# speed-combobox removal. Attempt to set state if supported,
|
||||||
|
# otherwise ignore errors so UI updates remain robust.
|
||||||
|
try:
|
||||||
|
if hasattr(self, "multiplier_combo") and self.multiplier_combo is not None:
|
||||||
|
try:
|
||||||
self.multiplier_combo.config(
|
self.multiplier_combo.config(
|
||||||
state="readonly" if not is_running else tk.DISABLED
|
state="readonly" if not is_running else tk.DISABLED
|
||||||
)
|
)
|
||||||
|
except Exception:
|
||||||
|
# Some widget types (e.g., ttk.Label) don't accept
|
||||||
|
# a 'state' option; ignore in that case.
|
||||||
|
pass
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
self.scenario_controls.new_button.config(state=state)
|
self.scenario_controls.new_button.config(state=state)
|
||||||
self.scenario_controls.save_button.config(state=state)
|
self.scenario_controls.save_button.config(state=state)
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import ttk
|
from tkinter import ttk, messagebox
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
@ -35,21 +35,23 @@ class SimulationControls(ttk.LabelFrame):
|
|||||||
|
|
||||||
self.stop_button = ttk.Button(self, text="Stop Live", command=self._stop, state=tk.DISABLED)
|
self.stop_button = ttk.Button(self, text="Stop Live", command=self._stop, state=tk.DISABLED)
|
||||||
self.stop_button.grid(row=0, column=1, sticky="w", padx=5, pady=5)
|
self.stop_button.grid(row=0, column=1, sticky="w", padx=5, pady=5)
|
||||||
|
# Guard to prevent re-entrant start attempts while a start is in progress
|
||||||
|
self._start_in_progress = False
|
||||||
|
|
||||||
# spacer
|
# spacer
|
||||||
spacer = ttk.Frame(self)
|
spacer = ttk.Frame(self)
|
||||||
spacer.grid(row=0, column=3, sticky="ew")
|
spacer.grid(row=0, column=3, sticky="ew")
|
||||||
|
|
||||||
|
# The simulation runs at real-time (1x) for live mode; show a
|
||||||
|
# non-editable indicator instead of an interactive combobox so the
|
||||||
|
# UI cannot change the runtime multiplier.
|
||||||
ttk.Label(self, text="Speed:").grid(row=0, column=4, sticky="e", padx=(10, 2), pady=5)
|
ttk.Label(self, text="Speed:").grid(row=0, column=4, sticky="e", padx=(10, 2), pady=5)
|
||||||
self.multiplier_combo = ttk.Combobox(
|
# Use a simple (non-interactive) label to indicate fixed 1x speed.
|
||||||
self,
|
# Keep the attribute name `multiplier_combo` for backward
|
||||||
textvariable=self.time_multiplier_var,
|
# compatibility with code that expects this attribute, but expose a
|
||||||
values=["1x", "2x", "4x", "10x", "20x"],
|
# read-only display instead of a Combobox.
|
||||||
state="readonly",
|
self.multiplier_combo = ttk.Label(self, text="1x")
|
||||||
width=4,
|
|
||||||
)
|
|
||||||
self.multiplier_combo.grid(row=0, column=5, sticky="w", padx=(0, 5), pady=5)
|
self.multiplier_combo.grid(row=0, column=5, sticky="w", padx=(0, 5), pady=5)
|
||||||
self.multiplier_combo.bind("<<ComboboxSelected>>", getattr(main_view, "_on_time_multiplier_changed", lambda e=None: None))
|
|
||||||
|
|
||||||
ttk.Label(self, text="Update Time (s):").grid(row=0, column=6, sticky="e", padx=(10, 2), pady=5)
|
ttk.Label(self, text="Update Time (s):").grid(row=0, column=6, sticky="e", padx=(10, 2), pady=5)
|
||||||
self.update_time_entry = ttk.Entry(self, textvariable=self.update_time, width=5)
|
self.update_time_entry = ttk.Entry(self, textvariable=self.update_time, width=5)
|
||||||
@ -104,9 +106,59 @@ class SimulationControls(ttk.LabelFrame):
|
|||||||
|
|
||||||
# Button handlers that delegate to main_view methods
|
# Button handlers that delegate to main_view methods
|
||||||
def _start(self):
|
def _start(self):
|
||||||
|
# 1) Verify connection before attempting to start
|
||||||
try:
|
try:
|
||||||
|
connected = False
|
||||||
|
try:
|
||||||
|
if hasattr(self.main_view, "target_communicator") and self.main_view.target_communicator:
|
||||||
|
connected = bool(getattr(self.main_view.target_communicator, "is_open", False))
|
||||||
|
except Exception:
|
||||||
|
connected = False
|
||||||
|
|
||||||
|
if not connected:
|
||||||
|
# Inform the user and do NOT disable the Start button
|
||||||
|
try:
|
||||||
|
messagebox.showinfo(
|
||||||
|
"Not connected",
|
||||||
|
"Target communicator is not connected. Please connect before starting the simulation.",
|
||||||
|
parent=self.main_view,
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
# As a fallback, log to stdout (rare in GUI) and return
|
||||||
|
print("Target communicator is not connected. Please connect before starting the simulation.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 3) If connected, disable Start immediately to prevent duplicates
|
||||||
|
try:
|
||||||
|
self.start_button.config(state=tk.DISABLED)
|
||||||
|
# Force the UI to process this change immediately so the
|
||||||
|
# button appears disabled before any potentially blocking
|
||||||
|
# start/reset work begins in MainView.
|
||||||
|
try:
|
||||||
|
if hasattr(self.main_view, "update_idletasks"):
|
||||||
|
self.main_view.update_idletasks()
|
||||||
|
if hasattr(self.main_view, "update"):
|
||||||
|
# update() processes pending events and redraws UI
|
||||||
|
# immediately. Use cautiously but helpful here to
|
||||||
|
# avoid race where user can click again before the
|
||||||
|
# widget state is visually updated.
|
||||||
|
self.main_view.update()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Delegate to MainView to start the simulation. If it fails,
|
||||||
|
# re-enable the Start button so the user can retry.
|
||||||
if hasattr(self.main_view, "_on_start_simulation"):
|
if hasattr(self.main_view, "_on_start_simulation"):
|
||||||
|
try:
|
||||||
self.main_view._on_start_simulation()
|
self.main_view._on_start_simulation()
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
self.start_button.config(state=tk.NORMAL)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
raise
|
||||||
except Exception:
|
except Exception:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|||||||
@ -427,7 +427,8 @@ class SimulationController:
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
main_view.after(0, lambda: main_view._update_button_states())
|
# Clear start-in-progress flag and update UI on main thread
|
||||||
|
main_view.after(0, lambda: (setattr(main_view, "_start_in_progress_main", False), main_view._update_button_states()))
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
return
|
return
|
||||||
@ -454,7 +455,7 @@ class SimulationController:
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
main_view.after(0, lambda: main_view._update_button_states())
|
main_view.after(0, lambda: (setattr(main_view, "_start_in_progress_main", False), main_view._update_button_states()))
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
return
|
return
|
||||||
@ -476,7 +477,7 @@ class SimulationController:
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
main_view.after(0, lambda: main_view._update_button_states())
|
main_view.after(0, lambda: (setattr(main_view, "_start_in_progress_main", False), main_view._update_button_states()))
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
return
|
return
|
||||||
@ -510,7 +511,7 @@ class SimulationController:
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
main_view.after(0, lambda: main_view._update_button_states())
|
main_view.after(0, lambda: (setattr(main_view, "_start_in_progress_main", False), main_view._update_button_states()))
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
return
|
return
|
||||||
@ -537,6 +538,11 @@ class SimulationController:
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
main_view.is_simulation_running.set(True)
|
main_view.is_simulation_running.set(True)
|
||||||
|
# Clear the start-in-progress flag and update UI
|
||||||
|
try:
|
||||||
|
main_view._start_in_progress_main = False
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
try:
|
try:
|
||||||
main_view._update_button_states()
|
main_view._update_button_states()
|
||||||
except Exception:
|
except Exception:
|
||||||
@ -549,6 +555,13 @@ class SimulationController:
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# Mark that a start is in progress so the UI keeps Start disabled
|
||||||
|
try:
|
||||||
|
# Set synchronously so immediate callers see the flag
|
||||||
|
main_view._start_in_progress_main = True
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
# UI: show status and disable controls before background work
|
# UI: show status and disable controls before background work
|
||||||
try:
|
try:
|
||||||
main_view.after(0, lambda: main_view.status_bar.show_status_message("Starting simulation...", timeout_ms=0))
|
main_view.after(0, lambda: main_view.status_bar.show_status_message("Starting simulation...", timeout_ms=0))
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user