From ca13144c1dc7122d3d5f597d7f0962de7b710db0 Mon Sep 17 00:00:00 2001 From: VALLONGOL Date: Wed, 29 Oct 2025 13:43:50 +0100 Subject: [PATCH] modificato l'invio del reset con il comando json corretto --- scenarios.json | 169 +---------------------- scenarios.json.bak1 | 2 +- scenarios.json.bak2 | 168 ++++++++++++++++++++++ scenarios.json.bak3 | 5 + target_simulator/gui/main_view.py | 25 +++- target_simulator/gui/sfp_debug_window.py | 41 ++++++ tests/gui/test_main_view_reset.py | 58 +++++++- 7 files changed, 296 insertions(+), 172 deletions(-) create mode 100644 scenarios.json.bak2 create mode 100644 scenarios.json.bak3 diff --git a/scenarios.json b/scenarios.json index 3c9743e..187e2d6 100644 --- a/scenarios.json +++ b/scenarios.json @@ -1,168 +1,5 @@ { - "scenario1": { - "name": "scenario1", - "targets": [ - { - "target_id": 0, - "active": true, - "traceable": true, - "trajectory": [ - { - "maneuver_type": "Fly to Point", - "duration_s": 10.0, - "target_altitude_ft": 10000.0, - "target_range_nm": 20.0, - "target_azimuth_deg": 0.0 - }, - { - "maneuver_type": "Fly for Duration", - "target_velocity_fps": 1670.9318999999994, - "target_heading_deg": 10.0, - "duration_s": 300.0, - "target_altitude_ft": 10000.0 - }, - { - "maneuver_type": "Fly to Point", - "duration_s": 400.0, - "target_altitude_ft": 10000.0, - "target_range_nm": 25.0, - "target_azimuth_deg": -20.0 - } - ] - } - ] - }, - "scenario2": { - "name": "scenario2", - "targets": [ - { - "target_id": 0, - "active": true, - "traceable": true, - "trajectory": [ - { - "maneuver_type": "Fly to Point", - "duration_s": 10.0, - "target_range_nm": 10.0, - "target_azimuth_deg": 1.0, - "target_altitude_ft": 10000.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": 200.0, - "target_range_nm": 20.0, - "target_azimuth_deg": 10.0, - "target_altitude_ft": 10000.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": 200.0, - "target_range_nm": 30.0, - "target_azimuth_deg": -10.0, - "target_altitude_ft": 10000.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": 200.0, - "target_range_nm": 35.0, - "target_azimuth_deg": 10.0, - "target_altitude_ft": 10000.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": 200.0, - "target_range_nm": 35.0, - "target_azimuth_deg": 30.0, - "target_altitude_ft": 10000.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": 200.0, - "target_range_nm": 20.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": true - }, - { - "target_id": 1, - "active": true, - "traceable": true, - "trajectory": [ - { - "maneuver_type": "Fly to Point", - "duration_s": 10.0, - "target_range_nm": 10.0, - "target_azimuth_deg": 10.0, - "target_altitude_ft": 10000.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": 10.0, - "target_range_nm": 20.0, - "target_azimuth_deg": 20.0, - "target_altitude_ft": 10000.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": 30.0, - "target_azimuth_deg": 30.0, - "target_altitude_ft": 10000.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": 35.0, - "target_azimuth_deg": -10.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 - } - ] + "s1": { + "a": 1 } -} - - +} \ No newline at end of file diff --git a/scenarios.json.bak1 b/scenarios.json.bak1 index abdc1de..187e2d6 100644 --- a/scenarios.json.bak1 +++ b/scenarios.json.bak1 @@ -1,5 +1,5 @@ { "s1": { - "foo": 123 + "a": 1 } } \ No newline at end of file diff --git a/scenarios.json.bak2 b/scenarios.json.bak2 new file mode 100644 index 0000000..3c9743e --- /dev/null +++ b/scenarios.json.bak2 @@ -0,0 +1,168 @@ +{ + "scenario1": { + "name": "scenario1", + "targets": [ + { + "target_id": 0, + "active": true, + "traceable": true, + "trajectory": [ + { + "maneuver_type": "Fly to Point", + "duration_s": 10.0, + "target_altitude_ft": 10000.0, + "target_range_nm": 20.0, + "target_azimuth_deg": 0.0 + }, + { + "maneuver_type": "Fly for Duration", + "target_velocity_fps": 1670.9318999999994, + "target_heading_deg": 10.0, + "duration_s": 300.0, + "target_altitude_ft": 10000.0 + }, + { + "maneuver_type": "Fly to Point", + "duration_s": 400.0, + "target_altitude_ft": 10000.0, + "target_range_nm": 25.0, + "target_azimuth_deg": -20.0 + } + ] + } + ] + }, + "scenario2": { + "name": "scenario2", + "targets": [ + { + "target_id": 0, + "active": true, + "traceable": true, + "trajectory": [ + { + "maneuver_type": "Fly to Point", + "duration_s": 10.0, + "target_range_nm": 10.0, + "target_azimuth_deg": 1.0, + "target_altitude_ft": 10000.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": 200.0, + "target_range_nm": 20.0, + "target_azimuth_deg": 10.0, + "target_altitude_ft": 10000.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": 200.0, + "target_range_nm": 30.0, + "target_azimuth_deg": -10.0, + "target_altitude_ft": 10000.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": 200.0, + "target_range_nm": 35.0, + "target_azimuth_deg": 10.0, + "target_altitude_ft": 10000.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": 200.0, + "target_range_nm": 35.0, + "target_azimuth_deg": 30.0, + "target_altitude_ft": 10000.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": 200.0, + "target_range_nm": 20.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": true + }, + { + "target_id": 1, + "active": true, + "traceable": true, + "trajectory": [ + { + "maneuver_type": "Fly to Point", + "duration_s": 10.0, + "target_range_nm": 10.0, + "target_azimuth_deg": 10.0, + "target_altitude_ft": 10000.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": 10.0, + "target_range_nm": 20.0, + "target_azimuth_deg": 20.0, + "target_altitude_ft": 10000.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": 30.0, + "target_azimuth_deg": 30.0, + "target_altitude_ft": 10000.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": 35.0, + "target_azimuth_deg": -10.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 + } + ] + } +} + + diff --git a/scenarios.json.bak3 b/scenarios.json.bak3 new file mode 100644 index 0000000..abdc1de --- /dev/null +++ b/scenarios.json.bak3 @@ -0,0 +1,5 @@ +{ + "s1": { + "foo": 123 + } +} \ No newline at end of file diff --git a/target_simulator/gui/main_view.py b/target_simulator/gui/main_view.py index f88f0a0..81db40f 100644 --- a/target_simulator/gui/main_view.py +++ b/target_simulator/gui/main_view.py @@ -706,10 +706,29 @@ class MainView(tk.Tk): ) reset_command = "tgtset /-s" - # Some radar servers require adjusting internal parameters. - prep_command = "$mex.t_rows=80" + # Some radar servers require adjusting internal parameters for legacy mode. + # Use the non-$ textual commands and ensure newline termination per the + # user's request: send exactly "mex.t_rows=80\n" and "tgtset /-s\n". + prep_command = "mex.t_rows=80\n" - commands_to_send = [prep_command, reset_command] + # If the communicator was configured to use the JSON protocol, send + # the JSON reset command expected by the server. Otherwise, fall back + # to the legacy prep + atomic reset command sequence. + try: + use_json = bool( + getattr(self.target_communicator, "_use_json_protocol", False) + ) + except Exception: + use_json = False + + if use_json: + # The server expects a single JSON payload to perform a reset. + json_reset = '{"CMD":"reset"}\n' + commands_to_send = [json_reset] + self.logger.info("Using JSON reset payload for radar reset.") + else: + # Legacy textual reset sequence (no leading $; newline-terminated strings) + commands_to_send = [prep_command, reset_command + "\n"] if not self.target_communicator.send_commands(commands_to_send): self.logger.error("Failed to send preparatory/reset commands to the radar.") diff --git a/target_simulator/gui/sfp_debug_window.py b/target_simulator/gui/sfp_debug_window.py index 6020cf6..89bc61c 100644 --- a/target_simulator/gui/sfp_debug_window.py +++ b/target_simulator/gui/sfp_debug_window.py @@ -416,6 +416,15 @@ class SfpDebugWindow(tk.Toplevel): command_builder.build_tgtreset() ), ).pack(side=tk.LEFT, padx=4) + # Add a dedicated Reset button that sends either the JSON reset + # payload or the legacy textual reset sequence depending on the + # selected send mode. The legacy textual commands are sent exactly + # as requested (no leading '$', newline-terminated lines). + ttk.Button( + quick_cmd_frame, + text="reset", + command=self._on_send_reset_button, + ).pack(side=tk.LEFT, padx=4) ttk.Button( quick_cmd_frame, text="pause", @@ -827,6 +836,38 @@ class SfpDebugWindow(tk.Toplevel): f"ERROR: Invalid input for script sending: {e}", "ERROR" ) + def _on_send_reset_button(self): + """Send a reset to the radar: JSON reset if JSON mode selected, + otherwise send legacy textual reset lines exactly as requested by the user. + """ + if not self.shared_communicator or not self.shared_communicator.is_open: + self._log_to_widget("ERROR: Cannot send reset, not connected.", "ERROR") + messagebox.showerror( + "Connection Error", "Communicator is not connected.", parent=self + ) + return False + + try: + if self.send_mode_var.get() == "json": + json_payload = '{"CMD":"reset"}' + # Ensure trailing newline + if not json_payload.endswith("\n"): + json_payload = json_payload + "\n" + self._log_to_widget("Sending JSON reset payload...", "INFO") + return self._on_send_simple_command(json_payload) + else: + # Legacy textual reset: send precisely 'mex.t_rows=80' and 'tgtset /-s' each newline-terminated + cmd1 = "mex.t_rows=80\n" + cmd2 = "tgtset /-s\n" + self._log_to_widget("Sending legacy reset sequence...", "INFO") + ok1 = self._on_send_simple_command(cmd1) + ok2 = self._on_send_simple_command(cmd2) + return bool(ok1 and ok2) + except Exception: + self.logger.exception("Failed to send reset command from debug window") + self._log_to_widget("ERROR: Failed to send reset.", "ERROR") + return False + def _on_send_simple_command(self, command_str: str): if not self.shared_communicator or not self.shared_communicator.is_open: self._log_to_widget("ERROR: Cannot send command, not connected.", "ERROR") diff --git a/tests/gui/test_main_view_reset.py b/tests/gui/test_main_view_reset.py index fb78b18..a989768 100644 --- a/tests/gui/test_main_view_reset.py +++ b/tests/gui/test_main_view_reset.py @@ -1,3 +1,57 @@ +import logging +from unittest.mock import Mock + +import pytest + +from target_simulator.gui import main_view as mv + + +def _make_minimal_mainview_with_comm(use_json: bool): + # Bypass __init__ to avoid Tkinter initialization in tests + inst = mv.MainView.__new__(mv.MainView) + # Provide a simple logger + inst.logger = logging.getLogger("test.main_view") + + # Mock communicator + comm = Mock() + comm.is_open = True + # Emulate the internal flag used by MainView to decide JSON vs legacy + setattr(comm, "_use_json_protocol", use_json) + comm.send_commands.return_value = True + + inst.target_communicator = comm + + # Mock simulation hub: immediate empty state so reset returns quickly + hub = Mock() + hub.has_active_real_targets.return_value = False + inst.simulation_hub = hub + + return inst, comm + + +def test_reset_uses_json_when_configured(): + inst, comm = _make_minimal_mainview_with_comm(use_json=True) + + ok = inst._reset_radar_state() + assert ok is True + + # send_commands should be called once with the JSON reset payload + assert comm.send_commands.called + sent = comm.send_commands.call_args[0][0] + assert isinstance(sent, list) + assert sent == ['{"CMD":"reset"}\n'] + + +def test_reset_uses_legacy_when_not_configured(): + inst, comm = _make_minimal_mainview_with_comm(use_json=False) + + ok = inst._reset_radar_state() + assert ok is True + + assert comm.send_commands.called + sent = comm.send_commands.call_args[0][0] + assert isinstance(sent, list) + assert sent == ["mex.t_rows=80\n", "tgtset /-s\n"] import pytest from target_simulator.gui.main_view import MainView from target_simulator.core.models import Target @@ -40,7 +94,7 @@ def test_reset_radar_state_sends_atomic_tgtset(main_view): # Expect success assert result is True - # Verify that two commands were sent: preparatory $mex.t_rows=80 then tgtset /-s + # Verify that two commands were sent: preparatory mex.t_rows=80 then tgtset /-s assert len(mv.target_communicator.sent_commands) == 2 - assert mv.target_communicator.sent_commands[0].strip() == "$mex.t_rows=80" + assert mv.target_communicator.sent_commands[0].strip() == "mex.t_rows=80" assert mv.target_communicator.sent_commands[1].strip() == "tgtset /-s"