sistemate alcuni problemi di aggiornamento.
This commit is contained in:
parent
747ec3b40f
commit
23bd5ee6dc
103
scenarios.json
103
scenarios.json
@ -162,6 +162,107 @@
|
||||
"use_spline": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"scenario3": {
|
||||
"name": "scenario3",
|
||||
"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": 0.0,
|
||||
"target_altitude_ft": 10000.0,
|
||||
"target_velocity_fps": 506.343,
|
||||
"target_heading_deg": 180.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": 40.0,
|
||||
"target_range_nm": 20.0,
|
||||
"target_azimuth_deg": 0.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": 40.0,
|
||||
"target_range_nm": 20.0,
|
||||
"target_azimuth_deg": -90.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": 40.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"
|
||||
}
|
||||
],
|
||||
"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": 25.0,
|
||||
"target_altitude_ft": 10000.0,
|
||||
"target_velocity_fps": 506.343,
|
||||
"target_heading_deg": 180.0,
|
||||
"longitudinal_acceleration_g": 0.0,
|
||||
"lateral_acceleration_g": 0.0,
|
||||
"vertical_acceleration_g": 0.0,
|
||||
"turn_direction": "Right"
|
||||
},
|
||||
{
|
||||
"maneuver_type": "Fly for Duration",
|
||||
"duration_s": 50.0,
|
||||
"target_altitude_ft": 10000.0,
|
||||
"target_velocity_fps": 1519.029,
|
||||
"target_heading_deg": 45.0,
|
||||
"longitudinal_acceleration_g": 0.0,
|
||||
"lateral_acceleration_g": 0.0,
|
||||
"vertical_acceleration_g": 0.0,
|
||||
"turn_direction": "Right"
|
||||
},
|
||||
{
|
||||
"maneuver_type": "Fly for Duration",
|
||||
"duration_s": 80.0,
|
||||
"target_altitude_ft": 10000.0,
|
||||
"target_velocity_fps": 1181.467,
|
||||
"target_heading_deg": -30.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,
|
||||
"max_range": 100,
|
||||
"geometry": "1599x1024+501+84",
|
||||
"last_selected_scenario": "scenario1",
|
||||
"last_selected_scenario": "scenario3",
|
||||
"connection": {
|
||||
"target": {
|
||||
"type": "sfp",
|
||||
|
||||
@ -165,6 +165,20 @@ class SimulationStateHub:
|
||||
with self._lock:
|
||||
return list(self._target_data.keys())
|
||||
|
||||
def has_active_real_targets(self) -> bool:
|
||||
"""
|
||||
Checks if there is any real target data currently stored in the hub.
|
||||
|
||||
Returns:
|
||||
True if at least one target has a non-empty 'real' data history,
|
||||
False otherwise.
|
||||
"""
|
||||
with self._lock:
|
||||
for target_info in self._target_data.values():
|
||||
if target_info.get("real"): # Check if the 'real' deque is not empty
|
||||
return True
|
||||
return False
|
||||
|
||||
def reset(self):
|
||||
"""Clears all stored data for all targets."""
|
||||
with self._lock:
|
||||
|
||||
@ -139,18 +139,29 @@ class SFPCommunicator(CommunicatorInterface):
|
||||
|
||||
self.logger.info(f"Sending scenario '{scenario.name}' via SFP...")
|
||||
|
||||
if not self._send_single_command(command_builder.build_pause()):
|
||||
return False
|
||||
# Build a list of commands to be sent atomically
|
||||
commands = [
|
||||
command_builder.build_pause(),
|
||||
command_builder.build_aclatch() # Add aclatch for atomic update
|
||||
]
|
||||
|
||||
for target in scenario.get_all_targets():
|
||||
cmd = command_builder.build_tgtinit(target)
|
||||
if not self._send_single_command(cmd):
|
||||
self.logger.error(f"Failed to send init for target {target.target_id}")
|
||||
return False
|
||||
time.sleep(0.01)
|
||||
commands.append(command_builder.build_tgtinit(target))
|
||||
|
||||
if not self._send_single_command(command_builder.build_continue()):
|
||||
return False
|
||||
commands.extend([
|
||||
command_builder.build_acunlatch(), # Add acunlatch to apply changes
|
||||
command_builder.build_continue()
|
||||
])
|
||||
|
||||
# Send commands with a small delay between each to prevent packet loss
|
||||
for cmd in commands:
|
||||
if not self._send_single_command(cmd):
|
||||
self.logger.error(f"Failed to send command '{cmd}'. Aborting scenario send.")
|
||||
# Attempt to leave the server in a good state
|
||||
self._send_single_command(command_builder.build_acunlatch())
|
||||
self._send_single_command(command_builder.build_continue())
|
||||
return False
|
||||
time.sleep(0.05) # Use a safer 50ms delay between commands
|
||||
|
||||
self.logger.info("Finished sending scenario via SFP.")
|
||||
return True
|
||||
|
||||
@ -589,54 +589,40 @@ class MainView(tk.Tk):
|
||||
|
||||
self.logger.info("Sending reset commands to deactivate all radar targets...")
|
||||
|
||||
# Prefer an atomic reset command to deactivate all targets on the server
|
||||
# instead of sending many individual tgtinit commands which is slow and
|
||||
# can cause dropped messages. The server understands 'tgtset /-s' which
|
||||
# clears all targets atomically.
|
||||
# Build the atomic reset command. Use command_builder.tgtset if available,
|
||||
# otherwise build the raw command string.
|
||||
# Build the atomic reset command.
|
||||
try:
|
||||
# Some command_builder implementations may provide build_tgtset; use it if present
|
||||
if hasattr(command_builder, "build_tgtset"):
|
||||
reset_command = command_builder.build_tgtset("/-s")
|
||||
else:
|
||||
# Fallback: raw command string
|
||||
reset_command = "tgtset /-s"
|
||||
reset_command = "tgtset /-s"
|
||||
except Exception:
|
||||
# In case command_builder raises for unexpected inputs, fallback to raw
|
||||
self.logger.exception("Error while building atomic reset command; falling back to raw string.")
|
||||
reset_command = "tgtset /-s"
|
||||
|
||||
# Some radar servers require adjusting internal parameters to accept
|
||||
# large multi-target operations. Send a preparatory command to set
|
||||
# the server t_rows parameter before issuing the atomic tgtset reset.
|
||||
# Some radar servers require adjusting internal parameters.
|
||||
prep_command = "$mex.t_rows=80"
|
||||
|
||||
commands_to_send = [prep_command, reset_command]
|
||||
|
||||
# Send the preparatory command followed by the atomic reset using the
|
||||
# communicator's send_commands API which accepts a list of commands.
|
||||
if not self.target_communicator.send_commands(commands_to_send):
|
||||
self.logger.error("Failed to send preparatory/reset commands to the radar.")
|
||||
messagebox.showerror("Reset Error", "Failed to send reset command to the radar.")
|
||||
return False
|
||||
|
||||
self.logger.info("Successfully sent preparatory and atomic reset commands: %s", commands_to_send)
|
||||
# Poll the simulation hub for up to a short timeout to ensure the server
|
||||
# processed the reset and returned no active targets.
|
||||
|
||||
# Poll the simulation hub to confirm the server processed the reset.
|
||||
# The success condition is that there are no more active REAL targets being reported.
|
||||
timeout_s = 3.0
|
||||
poll_interval = 0.2
|
||||
waited = 0.0
|
||||
while waited < timeout_s:
|
||||
# If there are no real target entries in the hub, assume reset succeeded
|
||||
if not self.simulation_hub.get_all_target_ids():
|
||||
self.logger.info("Radar reported zero active targets after reset.")
|
||||
# MODIFICATION: Use the new, correct check.
|
||||
if not self.simulation_hub.has_active_real_targets():
|
||||
self.logger.info("Radar reported zero active real targets after reset.")
|
||||
return True
|
||||
time.sleep(poll_interval)
|
||||
waited += poll_interval
|
||||
|
||||
# If we reach here, the hub still reports targets — treat as failure
|
||||
self.logger.error("Radar did not clear targets after reset within timeout.")
|
||||
# If we reach here, the hub still reports active real targets — treat as failure
|
||||
self.logger.error("Radar did not clear real targets after reset within timeout.")
|
||||
messagebox.showerror("Reset Error", "Radar did not clear targets after reset.")
|
||||
return False
|
||||
|
||||
@ -692,27 +678,15 @@ class MainView(tk.Tk):
|
||||
self.logger.info("Resetting simulation data hub and PPI trails.")
|
||||
self.simulation_hub.reset()
|
||||
self.ppi_widget.clear_trails()
|
||||
# If SFP reception was deferred, establish SFP connection now so we
|
||||
# can receive server state updates during simulation.
|
||||
if hasattr(self.target_communicator, "connect") and not self.target_communicator.is_open:
|
||||
try:
|
||||
# Try to connect with stored config; if connect fails, we abort
|
||||
sfp_cfg = self.connection_config.get("target", {}).get("sfp", {})
|
||||
if sfp_cfg:
|
||||
self.logger.info("Deferred SFP connect: attempting to connect for simulation start.")
|
||||
if not self.target_communicator.connect(sfp_cfg):
|
||||
self.logger.error("Failed to connect SFP communicator at simulation start.")
|
||||
messagebox.showerror("Connection Error", "Failed to establish SFP receive connection.")
|
||||
return
|
||||
except Exception:
|
||||
self.logger.exception("Exception while attempting deferred SFP connect.")
|
||||
messagebox.showerror("Connection Error", "Exception while establishing SFP receive connection.")
|
||||
return
|
||||
|
||||
if not self._reset_radar_state():
|
||||
self.logger.error("Aborting simulation start due to radar reset failure.")
|
||||
return
|
||||
|
||||
# MODIFICATION: Add a short delay to allow the server to process the reset
|
||||
# before it receives the new scenario initialization commands.
|
||||
time.sleep(1) # 1 second delay
|
||||
|
||||
self.logger.info(
|
||||
"Sending initial scenario state before starting live updates..."
|
||||
)
|
||||
@ -733,13 +707,11 @@ class MainView(tk.Tk):
|
||||
|
||||
self.scenario.reset_simulation()
|
||||
|
||||
# --- MODIFICATION HERE ---
|
||||
self.simulation_engine = SimulationEngine(
|
||||
communicator=self.target_communicator,
|
||||
update_queue=self.gui_update_queue,
|
||||
simulation_hub=self.simulation_hub, # Pass the hub to the engine
|
||||
simulation_hub=self.simulation_hub,
|
||||
)
|
||||
# --- END MODIFICATION ---
|
||||
|
||||
self.simulation_engine.set_time_multiplier(self.time_multiplier)
|
||||
self.simulation_engine.set_update_interval(update_interval)
|
||||
|
||||
@ -14,6 +14,7 @@ import logging
|
||||
import math
|
||||
import json
|
||||
import ctypes
|
||||
import time
|
||||
from queue import Queue, Full
|
||||
from typing import Dict, Optional, Any, List, Callable
|
||||
|
||||
@ -135,19 +136,9 @@ class DebugPayloadRouter:
|
||||
|
||||
if self._hub:
|
||||
try:
|
||||
parsed_for_hub = SfpRisStatusPayload.from_buffer_copy(payload)
|
||||
ts_s = parsed_for_hub.scenario.timetag / 1000.0
|
||||
|
||||
# First: clear real data for any targets that the server marked as inactive
|
||||
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, 'clear_real_target_data'):
|
||||
self._hub.clear_real_target_data(i)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
# Use the client's monotonic clock as the single source of truth for timestamps
|
||||
# to ensure simulated and real data can be correlated.
|
||||
reception_timestamp = time.monotonic()
|
||||
|
||||
# Add real states for active targets
|
||||
for target in real_targets:
|
||||
@ -157,19 +148,16 @@ class DebugPayloadRouter:
|
||||
getattr(target, '_pos_z_ft', 0.0),
|
||||
)
|
||||
self._hub.add_real_state(
|
||||
target_id=target.target_id, timestamp=ts_s, state=state_tuple
|
||||
target_id=target.target_id, timestamp=reception_timestamp, 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.
|
||||
# Propagate heading information (if available) into the hub
|
||||
try:
|
||||
for target in real_targets:
|
||||
if hasattr(self._hub, 'set_real_heading'):
|
||||
raw_val = getattr(target, '_raw_heading', None)
|
||||
self._hub.set_real_heading(target.target_id, getattr(target, 'current_heading_deg', 0.0), raw_value=raw_val)
|
||||
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:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user