S1005403_RisCC/doc/ARCHITECTURE.md
2025-11-24 08:24:07 +01:00

349 lines
28 KiB
Markdown

# Guida all'Architettura e al Codice Sorgente del Radar Target Simulator
Questo documento descrive l'architettura interna, le decisioni di progettazione e la struttura del codice sorgente dell'applicazione Radar Target Simulator. È destinato agli sviluppatori e agli ingegneri che necessitano di comprendere, manutenere o estendere il software.
## 1. Filosofia del Progetto e Principi Architetturali
L'architettura del software si basa su alcuni principi chiave volti a garantire manutenibilità, estendibilità e robustezza.
### 1.1. Separation of Concerns (SoC)
Il principio fondamentale è la netta separazione delle responsabilità tra i vari componenti del sistema:
* **Interfaccia Utente (GUI):** Responsabile solo della presentazione dei dati e della cattura dell'input utente. Non contiene logica di business.
* **Logica di Simulazione (Core/Engine):** Responsabile del calcolo della cinematica dei target. È completamente agnostica rispetto a come i dati vengono visualizzati o trasmessi.
* **Comunicazione (Communicators):** Responsabile della traduzione dei comandi di alto livello in messaggi specifici di un protocollo e della gestione della comunicazione a basso livello.
Questa separazione permette, ad esempio, di sostituire l'interfaccia basata su Tkinter con una basata su web senza modificare il motore di simulazione, o di aggiungere un nuovo protocollo di comunicazione senza toccare la GUI.
### 1.2. Thread Safety e Comunicazione tra Thread
L'applicazione è multi-threaded per garantire che l'interfaccia utente rimanga reattiva durante le operazioni potenzialmente lunghe (come la simulazione o l'attesa di pacchetti di rete).
* **GUI Thread (Main Thread):** Esegue il loop di Tkinter e gestisce tutti i widget.
* **Simulation Thread:** Esegue il loop del `SimulationEngine` per aggiornare lo stato dei target a intervalli regolari.
* **Communication Thread(s):** Componenti come `SfpTransport` avviano i propri thread per l'ascolto non bloccante di pacchetti UDP.
Per garantire una comunicazione sicura tra questi thread, l'architettura si affida a due meccanismi principali:
1. **`SimulationStateHub`:** Un contenitore dati centralizzato e thread-safe (protetto da `threading.Lock`). Funge da "single source of truth" per lo stato della simulazione. Tutti i thread scrivono e leggono da questo hub, che agisce come un intermediario sicuro, disaccoppiando i produttori di dati (motore di simulazione, ricevitore di rete) dai consumatori (GUI).
2. **`queue.Queue`:** Utilizzata per inviare notifiche o dati che richiedono un'elaborazione immediata dalla GUI (es. aggiornamenti per finestre di debug o notifiche di fine simulazione).
### 1.3. Modularità e Astrazione
Per facilitare l'estensione, il codice fa uso di astrazioni e interfacce. L'esempio più significativo è l'interfaccia `CommunicatorInterface`. Qualsiasi protocollo di comunicazione (Seriale, TFTP, SFP, o futuri) deve semplicemente implementare i metodi definiti da questa classe base (`connect`, `disconnect`, `send_scenario`, etc.). Il resto dell'applicazione interagisce solo con l'interfaccia, rendendo l'aggiunta di un nuovo protocollo un'operazione a basso impatto.
### 1.4. Standard e Convenzioni
Il codice aderisce alle seguenti convenzioni per garantire leggibilità e coerenza:
* **Stile del Codice:** Segue lo standard **PEP8**.
* **Linguaggio:** Nomi di funzioni, variabili, commenti e docstring sono scritti in **inglese**.
* **Tipizzazione:** Viene fatto uso di type hints (secondo PEP 484) per migliorare la robustezza e la comprensibilità del codice.
---
## 2. Struttura delle Cartelle del Progetto
Il codice sorgente è organizzato in un pacchetto Python (`target_simulator`) con una struttura modulare che riflette la separazione delle responsabilità.
```.
├── doc/ # Documentazione (manuali, guide come questa)
│ ├── manual/
│ └── user_guide/
├── scenarios/ # File JSON contenenti gli scenari predefiniti
├── target_simulator/ # Codice sorgente principale del pacchetto
│ ├── analysis/ # Moduli per l'analisi dati (es. PerformanceAnalyzer, SimulationStateHub)
│ ├── core/ # Cuore della logica di business e simulazione
│ │ ├── models.py # Definizioni dei dati (Target, Waypoint, Scenario)
│ │ ├── simulation_engine.py # Il motore di calcolo della simulazione
│ │ └── ... (moduli di comunicazione: sfp, serial, tftp)
│ ├── gui/ # Componenti dell'interfaccia utente (widget Tkinter)
│ │ ├── main_view.py # Classe principale della finestra dell'applicazione
│ │ ├── ppi_display.py # Widget per la visualizzazione radar
│ │ └── ... (altre finestre e widget personalizzati)
│ ├── simulation/ # Classi di alto livello per l'orchestrazione (es. SimulationController)
│ ├── utils/ # Funzioni e classi di utilità trasversali
│ │ ├── config_manager.py # Gestione dei file di configurazione
│ │ ├── logger.py # Configurazione del sistema di logging
│ │ └── ... (altre utilità)
│ ├── __init__.py # Entry point del pacchetto
│ ├── __main__.py # Entry point per l'esecuzione dell'applicazione (`python -m target_simulator`)
│ └── config.py # Configurazioni globali e costanti (es. LOGGING_CONFIG)
├── README.md # Readme del progetto
└── requirements.txt # Dipendenze Python
```
## 3. Mappa dei Componenti Chiave e Flusso dei Dati
Comprendere come i dati fluiscono attraverso l'applicazione è fondamentale per capire il suo funzionamento. Questa sezione descrive le interazioni tra i componenti principali durante due operazioni chiave: l'**esecuzione di una simulazione** e la **ricezione di dati reali**.
### 3.1. Diagramma Architetturale e Flusso Dati
Il seguente diagramma illustra le relazioni e il flusso di informazioni tra i componenti chiave del sistema.
*(Placeholder per l'immagine)*
**Descrizione dell'Immagine:** `architettura_dettagliata.png`
*(Questo è il diagramma descritto nel capitolo precedente del manuale tecnico, che possiamo riutilizzare e dettagliare qui. Mostra i blocchi `MainView (GUI)`, `SimulationController`, `SimulationEngine`, `SimulationStateHub`, `CommunicatorManager` e le loro interazioni).*
---
### 3.2. Flusso 1: Esecuzione di una Simulazione (Dati in Uscita)
Questo flusso descrive cosa accade dal momento in cui l'utente preme "Start Live" fino all'invio dei dati dei target.
1. **Azione Utente (GUI Thread):**
* L'utente clicca il pulsante `Start Live` sulla `MainView`.
* `MainView` delega la richiesta al `SimulationController`. Per evitare avvii multipli, imposta un flag (`_start_in_progress_main`) e aggiorna lo stato dei pulsanti.
2. **Orchestrazione (SimulationController - Nuovo Thread):**
* Il `SimulationController` avvia un thread in background per non bloccare la GUI.
* **Reset Radar:** Chiama il `CommunicatorManager` per inviare un comando di reset al device under test.
* **Cattura Origine:** Legge lo stato corrente dell'ownship dallo `SimulationStateHub` e lo salva come "origine della simulazione" fissa chiamando `set_simulation_origin()`.
* **Invio Scenario:** Chiama il `CommunicatorManager` per inviare lo stato iniziale di tutti i target.
* **Avvio Motore:** Se i passaggi precedenti hanno successo, crea e avvia un'istanza del `SimulationEngine` in un nuovo thread.
3. **Ciclo di Simulazione (SimulationEngine - Thread di Simulazione):**
* Il `SimulationEngine` entra nel suo loop principale, che si ripete a una frequenza fissa (es. 20 Hz).
* Ad ogni "tick":
a. Calcola il `delta_time` dall'ultimo tick.
b. Chiama `scenario.update_state(delta_time)` per aggiornare la posizione di ogni target.
c. Scrive il nuovo stato `(timestamp, x, y, z)` di ogni target nello `SimulationStateHub` chiamando `add_simulated_state()`.
* A intervalli più lenti (es. 1 Hz), definiti dall'utente:
a. Prepara una lista di comandi di aggiornamento (es. `tgtset` o payload JSON).
b. Invia i comandi tramite l'interfaccia del `CommunicatorInterface`.
4. **Visualizzazione (GUI Thread):**
* Il `_gui_refresh_loop` di `MainView` si esegue periodicamente (es. ogni 40 ms).
* Chiama `build_display_data()` (in `ppi_adapter.py`).
* `build_display_data()` legge dallo `SimulationStateHub`:
* Gli stati più recenti dei target simulati.
* L'origine della simulazione.
* Lo stato corrente dell'ownship.
* Esegue la trasformazione di coordinate (rotazione + traslazione) per calcolare la posizione dei target simulati relativa all'ownship corrente.
* `MainView` passa i dati trasformati al `PPIDisplay`, che aggiorna il canvas.
### 3.3. Flusso 2: Ricezione di Dati Reali (Dati in Ingresso)
Questo flusso descrive come i dati inviati dal radar vengono ricevuti, processati e visualizzati.
1. **Ricezione di Rete (Thread di Comunicazione):**
* Il `SfpTransport` (o un altro componente di comunicazione) ha un thread in background in ascolto su una porta UDP.
* Quando riceve un pacchetto, lo processa (es. riassembla i frammenti SFP).
2. **Routing del Payload (Thread di Comunicazione):**
* Una volta che un payload completo è disponibile, il `SfpTransport` lo passa al `DebugPayloadRouter` in base al suo Flow ID.
* Il `DebugPayloadRouter` ispeziona il payload. Se è un messaggio di stato del radar (es. Flow ID 'R'):
a. Decodifica il payload binario in una struttura dati (`SfpRisStatusPayload`).
b. Estrae i dati dell'ownship (posizione, heading, etc.) e dei target reali.
3. **Aggiornamento dello Stato (Thread di Comunicazione):**
* Il `DebugPayloadRouter` aggiorna lo `SimulationStateHub` (che è thread-safe):
* Chiama `set_ownship_state()` con i nuovi dati di navigazione.
* Per ogni target reale ricevuto, chiama `add_real_state()` con la sua posizione.
* Chiama `set_antenna_azimuth()` per aggiornare la posizione dell'antenna.
4. **Visualizzazione (GUI Thread):**
* Il `_gui_refresh_loop` di `MainView`, al suo ciclo successivo, legge i dati "reali" aggiornati dallo `SimulationStateHub`.
* `build_display_data()` calcola la posizione dei target reali relativa all'ownship.
* `MainView` aggiorna il `PPIDisplay`, che disegna i target reali (rossi), e aggiorna la posizione e l'orientamento dell'ownship e dell'antenna.
Questo doppio flusso di dati, che converge nello `SimulationStateHub` e viene poi letto dalla GUI, è il cuore del funzionamento in tempo reale dell'applicazione.
## 4. Analisi Dettagliata dei Moduli Principali
Questa sezione fornisce una scomposizione delle responsabilità e delle interazioni per i moduli e le classi più importanti del progetto.
### 4.1. Entry Point e Orchestrazione GUI (`__main__.py`, `gui/main_view.py`)
* **`target_simulator/__main__.py`**
* **Responsabilità:** È l'entry point principale dell'applicazione. Il suo unico scopo è inizializzare il sistema di logging di base e creare un'istanza della classe `MainView`, per poi avviare il loop principale di Tkinter (`app.mainloop()`).
* **`target_simulator/gui/main_view.py` -> `MainView(tk.Tk)`**
* **Responsabilità:** È la classe "Dio" della GUI, ma con responsabilità ben definite. Orchestra tutti i componenti principali, costruisce la finestra e gestisce il ciclo di aggiornamento dell'interfaccia.
* **Input/Stato Interno:**
* Possiede le istanze uniche di `ConfigManager`, `SimulationStateHub`, `CommunicatorManager` e `SimulationController`.
* Mantiene lo `Scenario` corrente in memoria.
* **Output/Side-effect:**
* Disegna e aggiorna tutti i widget della GUI.
* Delega le azioni complesse (start/stop) al `SimulationController`.
* Salva le configurazioni tramite il `ConfigManager`.
* **Interazioni Principali:**
* **`_gui_refresh_loop`:** È il cuore pulsante della GUI. A intervalli regolari, legge i dati dallo `SimulationStateHub` tramite il `ppi_adapter` e aggiorna il `PPIDisplay` e altri widget.
* **Callback degli Eventi:** I metodi `_on_*` (es. `_on_start_simulation`) rispondono agli input dell'utente e attivano la logica appropriata, solitamente delegando al controller.
### 4.2. Logica di Simulazione (`simulation/`, `core/simulation_engine.py`)
* **`target_simulator/simulation/simulation_controller.py` -> `SimulationController`**
* **Responsabilità:** Agisce come un "regista" per le operazioni di simulazione. Gestisce la logica di alto livello per l'avvio e l'arresto, che coinvolge più componenti e deve avvenire in una sequenza precisa.
* **Input:** Riceve richieste da `MainView`.
* **Output/Side-effect:** Crea e distrugge istanze del `SimulationEngine`.
* **Interazioni Principali:**
* Usa il `CommunicatorManager` per inviare comandi di controllo (reset, invio scenario).
* Usa lo `SimulationStateHub` per impostare l'origine della simulazione.
* **`target_simulator/core/simulation_engine.py` -> `SimulationEngine(threading.Thread)`**
* **Responsabilità:** Eseguire i calcoli cinematici della simulazione in un thread separato per non bloccare la GUI. È il "motore" che fa avanzare il tempo per i target.
* **Input:** Un oggetto `Scenario` da simulare e un `CommunicatorInterface` per inviare i dati.
* **Output/Side-effect:** Scrive continuamente gli stati aggiornati dei target nello `SimulationStateHub` e invia comandi al `CommunicatorInterface`.
* **Interazioni Principali:**
* Nel suo `run()` loop:
1. Chiama `target.update_state()` per ogni target.
2. Chiama `simulation_hub.add_simulated_state()`.
3. Chiama `communicator.send_commands()`.
### 4.3. Modelli dei Dati (`core/models.py`)
* **`target_simulator/core/models.py`**
* **Responsabilità:** Definisce le strutture dati fondamentali dell'applicazione: `Scenario`, `Target`, `Waypoint`. Queste classi contengono non solo i dati, ma anche la logica per manipolarli.
* **`Target` class:** Contiene la logica per calcolare la propria posizione (`update_state`) e per generare il percorso completo a partire dai waypoint (`_generate_path`). È il componente che implementa la cinematica.
* **`Scenario` class:** È un contenitore di `Target` con metodi helper per gestire l'intero gruppo (es. `update_state` su tutti i target).
* **`Waypoint` dataclass:** Una semplice struttura dati per descrivere una manovra.
### 4.4. Hub dei Dati (`analysis/simulation_state_hub.py`)
* **`target_simulator/analysis/simulation_state_hub.py` -> `SimulationStateHub`**
* **Responsabilità:** Essere il contenitore dati centrale e thread-safe. Disaccoppia i produttori di dati (engine, comunicatori) dai consumatori (GUI). Mantiene una cronologia limitata degli stati per l'analisi e la visualizzazione delle tracce.
* **Input:** Riceve dati tramite i suoi metodi `add_*_state()` e `set_*_state()`.
* **Output:** Fornisce dati tramite i suoi metodi `get_*()`.
* **Interazioni Principali:** È il componente più connesso. Praticamente tutti gli altri moduli principali interagiscono con esso. La sua natura thread-safe (tutti i metodi pubblici usano un `threading.Lock`) è cruciale per la stabilità dell'applicazione.
### 4.5. Comunicazione (`core/communicator_manager.py`, `core/*_communicator.py`)
* **`target_simulator/core/communicator_interface.py` -> `CommunicatorInterface`**
* **Responsabilità:** Definire il contratto (interfaccia astratta) che tutti i moduli di comunicazione devono rispettare.
* **`target_simulator/core/communicator_manager.py` -> `CommunicatorManager`**
* **Responsabilità:** Funge da "fabbrica" e facciata per i communicator. Crea l'istanza del communicator corretto (SFP, TFTP, etc.) in base alla configurazione e inoltra le chiamate (es. `connect`, `send_scenario`).
* **Interazioni Principali:** Viene usato dal `SimulationController` per gestire le connessioni e inviare comandi di controllo.
* **`SFPCommunicator`, `TFTPCommunicator`, `SerialCommunicator`**
* **Responsabilità:** Ognuna di queste classi implementa la logica specifica per un protocollo. `SFPCommunicator`, ad esempio, si affida a `SfpTransport` per la gestione a basso livello dei pacchetti UDP e dei frammenti.
### 4.6. Visualizzazione (`gui/ppi_display.py`, `gui/ppi_adapter.py`)
* **`target_simulator/gui/ppi_display.py` -> `PPIDisplay`**
* **Responsabilità:** È un widget complesso e auto-contenuto che si occupa esclusivamente di disegnare lo scenario radar su un canvas Matplotlib. Gestisce la grafica di target, tracce, ownship, settore di scansione e antenna.
* **Input:** Riceve liste di oggetti `Target` (già in coordinate relative) dai suoi metodi `update_simulated_targets` e `update_real_targets`. Riceve l'heading dell'ownship per orientare la vista.
* **Output/Side-effect:** Aggiorna il canvas.
* **`target_simulator/gui/ppi_adapter.py` -> `build_display_data`**
* **Responsabilità:** Funzione helper "pura" che funge da adattatore. Il suo unico scopo è leggere i dati grezzi dallo `SimulationStateHub` ed eseguire le trasformazioni di coordinate necessarie per preparare i dati nel formato atteso dal `PPIDisplay`.
* **Input:** L'istanza dello `SimulationStateHub`.
* **Output:** Un dizionario contenente le liste di `Target` pronti per essere disegnati.
### 4.7. Utilità (`utils/`)
* **`target_simulator/utils/config_manager.py` -> `ConfigManager`**
* **Responsabilità:** Abstrae la lettura e la scrittura dei file di configurazione (`settings.json`) e degli scenari (`scenarios.json`). Gestisce il path dei file e la serializzazione/deserializzazione da/verso JSON.
* **`target_simulator/utils/logger.py`**
* **Responsabilità:** Configura il sistema di logging centralizzato basato su `Queue`, permettendo ai thread in background di inviare log in modo sicuro che verranno poi scritti sulla console o sul widget della GUI dal thread principale.
## 5. Guida all'Estensione del Progetto
Questa sezione fornisce esempi pratici su come estendere l'applicazione con nuove funzionalità, seguendo i principi architetturali stabiliti.
### 5.1. Caso d'Uso: Aggiungere un Nuovo Tipo di Manovra
Supponiamo di voler aggiungere una nuova manovra, ad esempio "Orbita attorno a un punto".
1. **Modificare i Modelli (`core/models.py`):**
* Aggiungere un nuovo valore all'enum `ManeuverType`:
```python
class ManeuverType(Enum):
# ...
ORBIT_POINT = "Orbit Point"
```
* Aggiungere i campi necessari alla `dataclass Waypoint` per supportare la nuova manovra (es. `orbit_center_x`, `orbit_center_y`, `orbit_radius_nm`, `orbit_direction`).
2. **Aggiornare la Logica di Calcolo (`core/models.py`):**
* Modificare il metodo statico `Target.generate_path_from_waypoints` per gestire il nuovo `ManeuverType.ORBIT_POINT`. Qui andrà implementata la logica matematica per generare i punti `(t, x, y, z)` che descrivono l'orbita.
3. **Aggiornare l'Interfaccia Utente (`gui/waypoint_editor_window.py`):**
* Nella classe `WaypointEditorWindow`, creare un nuovo `ttk.Frame` contenente i widget (Spinbox, Combobox, etc.) per inserire i parametri della nuova manovra (raggio, centro, etc.).
* Modificare il metodo `_on_maneuver_type_change` per mostrare questo nuovo frame quando l'utente seleziona "Orbit Point" dal menu a tendina.
* Aggiornare il metodo `_on_ok` per leggere i valori dai nuovi widget e popolare correttamente l'oggetto `Waypoint` quando si salva.
### 5.2. Caso d'Uso: Aggiungere un Nuovo Protocollo di Comunicazione
Supponiamo di voler aggiungere il supporto per un nuovo protocollo basato su TCP.
1. **Creare la Classe del Communicator (`core/tcp_communicator.py`):**
* Creare un nuovo file `tcp_communicator.py` nel modulo `core`.
* Definire una nuova classe `TCPCommunicator` che eredita da `CommunicatorInterface` (`core/communicator_interface.py`).
* Implementare tutti i metodi astratti richiesti dall'interfaccia:
* `connect(self, config)`: Logica per stabilire la connessione TCP.
* `disconnect(self)`: Logica per chiudere la connessione.
* `is_open(self)`: Proprietà che restituisce lo stato della connessione.
* `send_scenario(self, scenario)`: Logica per serializzare e inviare lo stato iniziale dello scenario su TCP.
* `send_commands(self, commands)`: Logica per inviare gli aggiornamenti in tempo reale.
* `test_connection(config)` e `list_available_ports()`: Metodi statici.
2. **Aggiornare il Gestore (`core/communicator_manager.py`):**
* Nella classe `CommunicatorManager`, modificare il metodo `_setup_communicator` per riconoscere il nuovo tipo "tcp".
```python
# In _setup_communicator
from target_simulator.core.tcp_communicator import TCPCommunicator
# ...
elif comm_type == "tcp":
communicator = TCPCommunicator()
config_data = config.get("tcp", {})
```
3. **Aggiornare l'Interfaccia Utente (`gui/connection_settings_window.py`):**
* Aggiungere "TCP" alla lista dei `values` nel `ttk.Combobox` del tipo di connessione.
* Creare un nuovo `ttk.Frame` (es. `tcp_frame`) con i widget per i parametri TCP (IP, porta).
* Aggiungere una nuova scheda al `ttk.Notebook` per ospitare il `tcp_frame`.
* Aggiornare i metodi `_load_settings` e `_on_save` per leggere e scrivere la nuova sezione di configurazione `tcp` nel dizionario `connection_config`.
* Aggiungere la logica per `_test_connection` per il tipo "TCP".
### 5.3. Flusso di Debug Consigliato
Quando si affronta un bug o si sviluppa una nuova funzionalità, si consiglia il seguente approccio:
1. **Isolare il Problema:** Il bug è nella visualizzazione (GUI), nel calcolo (Engine) o nella comunicazione (Communicator)? La separazione delle responsabilità dovrebbe aiutare a identificare il dominio del problema.
2. **Aumentare il Livello di Log:** Usare la finestra `Debug -> Logger Levels...` per impostare a `DEBUG` il livello di log del modulo sospetto. Ad esempio:
* Problemi di visualizzazione? Abilita il debug per `target_simulator.gui.ppi_display` e `target_simulator.gui.ppi_adapter`.
* Problemi di comunicazione? Abilita il debug per `target_simulator.core.sfp_communicator` e `target_simulator.core.sfp_transport`.
3. **Usare gli Strumenti di Debug:**
* L'`SFP Packet Inspector` è fondamentale per problemi di comunicazione. Controlla la scheda `Raw` per vedere esattamente cosa viene ricevuto e la scheda `RIS` per vedere come viene interpretato.
* Usa il `Simple Target Sender` per inviare comandi isolati e testare specifiche risposte del sistema radar senza eseguire uno scenario completo.
4. **Ispezionare lo `SimulationStateHub`:** Se c'è una discrepanza tra ciò che il motore dovrebbe calcolare e ciò che la GUI mostra, il problema è probabilmente nel `ppi_adapter`. Se i dati nello `Hub` stesso sono sbagliati, il problema è a monte (nel `SimulationEngine` o nel `DebugPayloadRouter`).
Seguendo questi principi e utilizzando gli strumenti forniti, la manutenzione e l'estensione dell'applicazione dovrebbero risultare più semplici e strutturate.
---
## 6. Recent Changes (Nov 2025) — aggiornamenti architetturali e punti d'interesse
Negli ultimi aggiornamenti del codice sono state introdotte alcune modifiche significative nella struttura e nei punti di estensione del progetto. Qui di seguito sono riepilogati i cambiamenti più rilevanti che gli sviluppatori/maintainer devono conoscere e che sono stati riflessi nella documentazione utente e tecnica.
- **Target types / profiles centralizzati:** è stato introdotto il file `target_types.json` (gestito da `target_simulator/utils/target_type_manager.py`) che contiene la libreria condivisa di profili target (es. `F-16`, `B-747`). I singoli target nello `scenarios.json` fanno ora riferimento a questi profili per nome e possono applicare sovrascritture locali (es. `rcs`, `amplitude`). Questo migliora la riusabilità e il versioning dei profili.
- **Persistenza scenari:** la persistenza degli scenari è gestita centralmente da `ConfigManager` con un file `scenarios.json` e scritture atomiche + backup rotativi. Nota: i profili predefiniti rimangono in `target_types.json`, mentre le assegnazioni e sovrascritture sono nel `scenarios.json`.
- **Profiling delle prestazioni interno:** il `DebugPayloadRouter` e i componenti correlati raccolgono ora campioni di performance (es. `parse_ms`, `hub_ms`, `archive_ms`, `listener_ms`, `clock_ms`) quando la flag di debug `enable_performance_profiling` è attiva. I campioni vengono memorizzati in memoria e, se presenti, esportati come file `<timestamp>_<scenario>.perf.csv` nell'archivio della simulazione (`archive_simulations/`).
- **IO-trace e CSV:** la scrittura asincrona delle tracce I/O (posizioni inviate/ricevute) è gestita da `target_simulator/utils/csv_logger.py`. I file temporanei sono creati nella cartella indicata da `temp_folder_name` (default `Temp/`) e poi finalizzati e spostati in `archive_simulations/` con suffissi standard (`.trail.csv`, `.latency.csv`).
- **Sync Tool & External Profiler:** introdotte nuove funzionalità di debug esterne:
- `Sync Tool`: tool che invia messaggi `SYNC` al server per misurare RTT, offset di clock e generare report CSV di latenza (`*_sync_latency.csv`).
- `External Profiler`: componente che forwarda pacchetti di profilazione verso collector esterni (UDP/TCP) per permettere correlazioni tra eventi applicativi e catture di rete (PCAP). Questi strumenti sono integrati nella GUI (`SfpDebugWindow`, `ExternalProfilerWindow`) e nel flusso di logging/profiling.
- **Reset atomico e comandi preparatori:** il meccanismo di avvio simulazione (`SimulationController`) invia ora reset atomici quando possibile (payload JSON unico) evitando sequenze multiple di comandi che potrebbero essere perse; quando il reset atomico non è supportato dal device, il controller applica fallback compatibili per garantire coerenza di stato.
- **Mapping dei campi di profilazione nel payload JSON:** i payload `tgtinit`/`tgtset` includono ora campi opzionali come `profile`, `rcs` e `amplitude` quando la modalità JSON è abilitata. La GUI offre una schermata di Profiling/Characteristics che permette di mappare i campi di profilo e testare l'invio di payload di prova.
- **Archiviazione performance e correlazione con PCAP:** oltre ai CSV interni la piattaforma suggerisce di catturare il traffico di rete (Wireshark / dumpcap / tshark) durante i test e di usare marker (`SYNC-SEQ`, campi `seq`) per facilitare la correlazione tra CSV applicativi e PCAP. I file PCAP possono essere esportati e analizzati con `tshark` per estrarre timestamp e payload utili alla correlazione.
Questi cambiamenti sono stati progettati per mantenere la compatibilità con le installazioni esistenti, offrendo però strumenti più robusti per la profilazione, la diagnostica e la condivisione dei profili target.
---
## 7. Note finali per i Maintainer
Quando si modifica il formato del payload JSON o si aggiungono nuovi campi di profilazione, assicurarsi di:
1. Aggiornare i test unitari e l'eventuale integrazione che verifica la serializzazione dei payload (es. `tests/` che controllano `tgtinit`/`tgtset`).
2. Aggiornare il manuale utente (`doc/manual`) e la user guide (`doc/user_guide`) con esempi di payload e screenshot della UI.
3. Verificare la retrocompatibilità verso `target_types.json`/`scenarios.json` e fornire procedure di migrazione se necessario.
Per domande o chiarimenti sull'architettura attuale, contatta il team di sviluppo principale o apri un issue nel repository.