213 lines
20 KiB
Markdown
213 lines
20 KiB
Markdown
Certamente! È un'ottima idea avere un piano di sviluppo aggiornato e chiaro per tenere traccia dei progressi e dei passi successivi.
|
||
|
||
Ecco una versione rivista e dettagliata del piano di sviluppo, che include quanto fatto finora e i prossimi passi che abbiamo discusso. Ho cercato di mantenere la struttura a fasi, ma ho integrato le nuove comprensioni e priorità.
|
||
|
||
---
|
||
|
||
## **Piano di Sviluppo: FlightMonitor Application**
|
||
|
||
**🎯 Obiettivo Principale dell'Applicazione:**
|
||
Monitorare il traffico aereo in tempo reale (live) e analizzare voli passati (storico) tramite una GUI Tkinter, con visualizzazione testuale e grafica (mappa/lista).
|
||
|
||
**🧱 Architettura Proposta (Modulare):**
|
||
* **GUI (Tkinter):** `MainWindow`, Viste specifiche (Live, Storico), Widget riutilizzabili.
|
||
* **Controller:** Gestione eventi GUI, coordinamento moduli, logica applicativa principale.
|
||
* **Data Layer:**
|
||
* **Adapters:** Moduli specifici per provider (es. `OpenSkyLiveAdapter`) che recuperano dati grezzi e li trasformano in...
|
||
* **Common Models:** Modelli dati canonici (`CanonicalFlightState`) usati internamente dall'applicazione.
|
||
* **Storage:** Gestione della persistenza dei dati (es. `DataStorage` per file SQLite giornalieri).
|
||
* **Configuration:** Gestione delle impostazioni globali (`config.py`).
|
||
* **Utils:** Moduli di utilità (es. `logger.py`).
|
||
* **Business Logic (Futuro):** Filtri avanzati, analisi, allerte (potrebbero risiedere nel controller o in un modulo `logic/` separato).
|
||
|
||
---
|
||
|
||
**📋 Fasi di Sviluppo e Priorità:**
|
||
|
||
**✔️ Fase 0 – Fondamenta e Setup Iniziale (Completata)**
|
||
* [X] Struttura base del progetto (cartelle `gui`, `controller`, `data`, `utils`).
|
||
* [X] Entry point `__main__.py`.
|
||
* [X] Modulo di configurazione base (`data/config.py`).
|
||
* [X] Modulo di logging robusto e configurabile (`utils/logger.py`) con output su console e GUI.
|
||
|
||
**✔️ Fase 1 – GUI Minimale e Interazione Base (Completata)**
|
||
* [X] `MainWindow` (`gui/main_window.py`) con layout di base:
|
||
* [X] Controlli (selezione modalità Live/Storico, pulsanti Start/Stop).
|
||
* [X] Input per Bounding Box (lat/lon min/max).
|
||
* [X] Area di output iniziale (Canvas per la mappa).
|
||
* [X] Area per i log testuali (`ScrolledText`).
|
||
* [X] Status Bar con semaforo visivo e messaggi di testo.
|
||
* [X] `AppController` (`controller/app_controller.py`) per:
|
||
* [X] Gestire gli eventi base della GUI (cambio modalità, start/stop).
|
||
* [X] Coordinare l'interazione tra GUI e logica dati (inizialmente placeholder).
|
||
|
||
**✔️ Fase 2 – Modalità Live: Fetching Asincrono e Visualizzazione Base (Completata)**
|
||
* [X] Definizione Modelli Dati Canonici (`data/common_models.py` -> `CanonicalFlightState`).
|
||
* [X] `OpenSkyLiveAdapter` (`data/opensky_live_adapter.py`):
|
||
* [X] Esecuzione in un thread separato (`threading.Thread`).
|
||
* [X] Polling periodico dell'API OpenSky (`/states/all`) per un bounding box.
|
||
* [X] Parsing della risposta JSON e trasformazione dei dati grezzi in oggetti `CanonicalFlightState`.
|
||
* [X] Gestione robusta degli errori API (incluso HTTP 429 Rate Limit) con strategia di **backoff esponenziale**.
|
||
* [X] Comunicazione asincrona con `AppController` tramite `queue.Queue`.
|
||
* [X] Invio di messaggi strutturati (dati di volo o messaggi di stato/errore) sulla coda.
|
||
* [X] `AppController` aggiornato per:
|
||
* [X] Gestire il ciclo di vita dell' `OpenSkyLiveAdapter` (avvio/arresto).
|
||
* [X] Consumare messaggi (dati e stati) dalla coda dell'adapter.
|
||
* [X] Interpretare i messaggi di stato dell'adapter e aggiornare la GUI.
|
||
* [X] `MainWindow` aggiornata per:
|
||
* [X] Visualizzare i voli (`List[CanonicalFlightState]`) sul `tk.Canvas` (punti e etichette base).
|
||
* [X] Utilizzare il metodo `update_semaphore_and_status` per riflettere lo stato comunicato dall'`AppController`.
|
||
|
||
**✔️ Fase 3 – Persistenza Dati Live (Completata)**
|
||
* [X] `DataStorage` (`data/storage.py`):
|
||
* [X] Logica per creare e gestire file **SQLite giornalieri** (basati su data UTC).
|
||
* [X] Creazione automatica della directory dei database e delle tabelle (`flights`, `positions`) se non esistono.
|
||
* [X] Schema DB definito per memorizzare dati da `CanonicalFlightState` (inclusi timestamp, altitudini, velocità, ecc., e campi aggiuntivi come `squawk`, `spi`, `raw_data_provider`).
|
||
* [X] Metodi `add_or_update_flight_daily` e `add_position_daily` che accettano/usano `CanonicalFlightState`.
|
||
* [X] Integrazione in `AppController`:
|
||
* [X] Quando si ricevono `CanonicalFlightState` dall'adapter, vengono passati a `DataStorage` per il salvataggio nel DB giornaliero appropriato.
|
||
* [X] Gestione dell'inizializzazione e chiusura della connessione a `DataStorage`.
|
||
|
||
---
|
||
**📍 Punto Attuale (Dopo Fase 3):**
|
||
* L'applicazione può recuperare dati live da OpenSky per un'area definita.
|
||
* I dati vengono aggiornati periodicamente senza bloccare la GUI.
|
||
* Gli errori API (come rate limit) sono gestiti con backoff.
|
||
* Lo stato dell'applicazione e del fetching è comunicato all'utente tramite una status bar con semaforo e messaggi.
|
||
* I dati live recuperati vengono salvati in file SQLite giornalieri.
|
||
* La struttura del codice è modulare e utilizza modelli dati canonici, preparandoci per future estensioni.
|
||
|
||
---
|
||
**➡️ Fase 4 – Modalità Storico: Visualizzazione e Filtri Base (Prossima Priorità Alta)**
|
||
* **Caricamento Dati Storici:**
|
||
* Implementare la logica in `DataStorage` per interrogare i file SQLite giornalieri.
|
||
* Metodo per ottenere voli e/o posizioni per un dato intervallo di tempo (che potrebbe coinvolgere l'apertura di più file giornalieri).
|
||
* Metodo per cercare voli per `icao24` o `callsign` in un range di date.
|
||
* **GUI per la Modalità Storico (`gui/history_view.py` o integrazione in `MainWindow`):**
|
||
* Input per selezionare l'intervallo di date/ore.
|
||
* Input per filtri base (es. ICAO24, callsign, forse aeroporto se avessimo quei dati).
|
||
* Pulsante per caricare/visualizzare i dati storici.
|
||
* **Visualizzazione Dati Storici:**
|
||
* Tabella (`ttk.Treeview` o un widget `FlightTable` personalizzato) per mostrare la lista dei voli e/o le loro posizioni.
|
||
* Opzionale: Visualizzazione statica della traccia di un volo selezionato sul `tk.Canvas`.
|
||
* **`AppController`:**
|
||
* Logica per `start_history_monitoring` e `stop_history_monitoring`.
|
||
* Interazione con `DataStorage` per recuperare i dati.
|
||
* Passaggio dei dati storici alla GUI per la visualizzazione.
|
||
|
||
**➡️ Fase 5 – Funzionalità Avanzate e Miglioramenti**
|
||
|
||
* **Miglioramenti Mappa (Media Priorità):**
|
||
* Valutare l'integrazione di una libreria di mappe più avanzata (es. `tkintermapview` o `folium` se si considera un output web/HTML separato, anche se `tkintermapview` è più integrato).
|
||
* Disegnare icone di aerei più realistiche (magari orientate in base al `true_track`).
|
||
* Possibilità di cliccare su un aereo per vedere più dettagli.
|
||
* Visualizzazione delle tracce dei voli (dallo storico o segmenti live).
|
||
* **Analisi Semplice (Business Logic - Media Priorità):**
|
||
* Creare un modulo `logic/analytics.py`.
|
||
* Funzioni per calcolare statistiche base dai dati storici (es. numero di voli per ora/giorno, rotte più frequenti in un'area, altitudini medie).
|
||
* Visualizzazione di queste analisi nella GUI.
|
||
* **Filtri Avanzati (Media Priorità):**
|
||
* Creare `logic/filters.py`.
|
||
* Filtri dinamici applicabili sia ai dati live che storici (tipo di aereo, compagnia – richiederebbe dati aggiuntivi – altitudine, velocità).
|
||
* **Recupero Dati Storici da API Esterne (Bassa/Media Priorità, come discusso):**
|
||
* Implementare la logica (magari in un `OpenSkyHistoryAdapter`) per scaricare dati storici da OpenSky (o altri provider) se mancano nei nostri DB giornalieri.
|
||
* Questo si integrerebbe con la Fase 4, quando si cercano dati storici.
|
||
* **Configurazioni Utente (Bassa/Media Priorità):**
|
||
* Salvataggio/caricamento delle preferenze utente (es. ultimo bounding box usato, tema GUI, API keys se necessarie).
|
||
* Finestra di dialogo per le impostazioni.
|
||
* **Esportazione Dati (Bassa Priorità):**
|
||
* Funzionalità per esportare i dati selezionati (live o storici) in formati come CSV o JSON.
|
||
|
||
**➡️ Fase 6 – Pulizia, Test, Documentazione e Rilascio (Continua)**
|
||
* **Test Unitari:** Scrivere unit test per i componenti core (modelli, adapter, storage, logica di business).
|
||
* **Test di Integrazione:** Testare il flusso completo dell'applicazione.
|
||
* **Refactoring e Pulizia Codice:** Continuare a migliorare la qualità del codice.
|
||
* **Documentazione:**
|
||
* Migliorare docstring e commenti.
|
||
* Scrivere/aggiornare `README.md` con istruzioni d'uso, setup, dipendenze.
|
||
* **Gestione Dipendenze:** (es. `requirements.txt`).
|
||
* **Packaging (Opzionale):** Creare un eseguibile distribuibile (es. con PyInstaller).
|
||
|
||
---
|
||
|
||
Questo piano è una guida; le priorità possono cambiare e nuove idee possono emergere. Spero che questa visione d'insieme ti sia utile! Abbiamo fatto ottimi progressi sulle fondamenta e sulla modalità live. La Fase 4 sulla modalità storico sarà il prossimo grande blocco di funzionalità.
|
||
|
||
|
||
Certamente. Ho analizzato il codice che mi hai fornito per i moduli `__main__.py`, `common_models.py`, `storage.py`, `logging_config.py`, `__init__.py`, `opensky_live_adapter.py`, `config.py`, `main_window.py`, `app_controller.py`, `map_canvas_manager.py`, `map_services.py`, `map_drawing.py`, `map_utils.py`, `map_tile_manager.py`, e `map_manager.py`.
|
||
|
||
Ho verificato le importazioni, le chiamate tra funzioni e classi nei diversi moduli e la coerenza generale dell'architettura definita dal codice.
|
||
|
||
L'architettura generale che prevede un `AppController` che gestisce la logica applicativa, un `MainWindow` per la GUI, moduli per la gestione dei dati (`storage`, `common_models`), moduli per la comunicazione con sorgenti esterne (`opensky_live_adapter`), e moduli per la visualizzazione mappa (`map_canvas_manager`, `map_tile_manager`, `map_services`, `map_utils`, `map_drawing`) sembra ben definita e le interazioni tra questi componenti sono in gran parte corrette, con il controller che fa da mediatore tra la GUI e i worker threads/moduli dati, comunicando spesso tramite code per garantire la thread-safety con la GUI Tkinter.
|
||
|
||
L'uso dei `try/except ImportError` per le librerie opzionali (`pyproj`, `mercantile`, `Pillow`, `cv2`/`numpy`) e dei controlli `winfo_exists()` prima di interagire con i widget Tkinter è una buona pratica che aumenta la robustezza dell'applicazione.
|
||
|
||
Tuttavia, ho riscontrato alcune incongruenze o aree che potrebbero essere migliorate per garantire una separazione più pulita delle responsabilità e correggere piccole imprecisioni.
|
||
|
||
Ecco i punti specifici che ho identificato:
|
||
|
||
1. **Interazione Diretta GUI nell'Adapter `OpenSkyLiveAdapter`**:
|
||
* **Dove:** Modulo `opensky_live_adapter.py`, in particolare nei metodi `_send_status_to_queue` e `run`.
|
||
* **Incongruenza:** L'adapter (`OpenSkyLiveAdapter`) è un thread worker che dovrebbe essere completamente disaccoppiato dalla GUI. La sua unica forma di comunicazione verso l'esterno dovrebbe essere la `output_queue`. Invece, il codice contiene riferimenti diretti all'istanza di `main_window` (`self.main_window`) e controlli come `self.main_window.root.winfo_exists()` prima di inviare messaggi di stato alla coda.
|
||
* **Perché è un problema:** L'accesso diretto a un widget Tkinter (`self.main_window.root`) o il controllo del suo stato (`winfo_exists()`) da un thread diverso da quello principale di Tkinter **non è thread-safe** e può portare a `TclError` imprevedibili, specialmente durante la chiusura dell'applicazione o in caso di errori nella GUI. L'adapter non dovrebbe mai "sapere" dell'esistenza della finestra principale.
|
||
* **Come dovrebbe essere gestito:** L'adapter dovrebbe semplicemente mettere *sempre* i messaggi di stato nella `output_queue`. È compito del `AppController` (che processa la coda sul thread principale Tkinter usando `root.after`) leggere questi messaggi e, solo a quel punto, decidere se e come aggiornare la GUI (`main_window`). I controlli `if self.main_window and hasattr(...) and self.main_window.root.winfo_exists()` devono essere spostati dall'adapter al `_process_flight_data_queue` nel `AppController` (dove in parte già ci sono, ma non per *tutte* le azioni che l'adapter tenta di fare direttamente).
|
||
|
||
2. **Dipendenza da Moduli Esterni (`geoelevation`) nel Disegno Mappa**:
|
||
* **Dove:** Moduli `map_drawing.py` e `map_tile_manager.py`.
|
||
* **Incongruenza:** Il codice importa costanti e potenzialmente classi/funzioni dai percorsi `geoelevation.image_processor` e `.geo_map_viewer`. Questi moduli (`geoelevation`) non sono inclusi nel codice fornito e sembrano appartenere a un progetto o libreria separata.
|
||
* **Perché è un potenziale problema:** Se questi moduli esterni non sono disponibili nell'ambiente di esecuzione, le importazioni falliranno. Sebbene l'uso dei `try/except ImportError` e la definizione di valori di fallback per le costanti (`DEM_BOUNDARY_COLOR`, `DEFAULT_FONT`, ecc.) mitighino il rischio di crash, alcune funzionalità di disegno o lo stile visivo previsto potrebbero non essere disponibili.
|
||
* **Stato Attuale:** Il codice attuale gestisce la cosa in modo robusto con fallback e logging di warning/error, quindi non causa un blocco critico dell'applicazione, ma è una dipendenza non fornita che limita la piena funzionalità del disegno mappa.
|
||
|
||
3. **Controllo Redondante/Errato nel `MapTileManager`**:
|
||
* **Dove:** Modulo `map_tile_manager.py`, metodo `_get_bounds_for_tile_range`.
|
||
* **Incongruenza:** Il codice controlla la disponibilità della libreria `mercantile` utilizzando il flag `MERCANTILE_MODULE_LOCALLY_AVAILABLE`, che però è definito nel modulo `map_utils.py` e non è importato né definito in `map_tile_manager.py`. Di conseguenza, questo flag non esisterà nel namespace di `map_tile_manager.py` e causerà un `NameError` se `mercantile` non è disponibile all'importazione in quel modulo. Il controllo corretto nel contesto di `map_tile_manager.py` è verificare se la variabile locale `mercantile` (importata all'inizio del file con `try/except`) è `None`.
|
||
* **Stato Attuale:** Fortunatamente, il codice include già anche il controllo corretto `if mercantile is None:`, che è quello che effettivamente funziona se `mercantile` non viene importato. Il controllo errato con il flag `MERCANTILE_MODULE_LOCALLY_AVAILABLE` è semplicemente redondante e, di per sé, errato.
|
||
|
||
4. **Coerenza Nomi Variabili/Chiavi:**
|
||
* **Dove:** Modulo `map_utils.py`, metodo `get_combined_geographic_bounds_from_tile_info_list`.
|
||
* **Incongruenza:** Nella versione che ho analizzato, c'era un probabile errore di battitura `longitude_longitude` al posto di `longitude_coord` quando si accedeva al dizionario `tile_info`.
|
||
* **Stato Attuale:** Nello snippet fornito, questa correzione (`longitude_coord`) sembra già essere stata applicata, rendendo questo punto non più un'incongruenza nel codice attuale, ma è bene notarlo come potenziale area di attenzione in versioni precedenti o future se si ripresentasse l'errore.
|
||
|
||
A parte il punto 1 (l'interazione diretta GUI dall'adapter, che è un rischio per la stabilità dell'applicazione), gli altri punti sono gestiti in modo che l'applicazione possa comunque funzionare, sebbene con funzionalità potenzialmente ridotte in assenza di librerie o moduli esterni.
|
||
|
||
Il resto del codice, inclusa la gestione della coda nel controller, l'inizializzazione del logging thread-safe, l'uso dei modelli canonici e la logica di base dello storage, appare consistente e ben strutturato all'interno dei file forniti.
|
||
|
||
Sono pronto a procedere non appena mi dirai quali modifiche desideri apportare, tenendo conto di questa analisi.
|
||
|
||
|
||
|
||
Ottimo lavoro nel fornire una base di codice così completa! Ho analizzato i moduli che mi hai passato e ho individuato alcuni punti che possiamo discutere per migliorare ulteriormente la coerenza e la robustezza del progetto.
|
||
|
||
Ecco una sintesi delle principali osservazioni:
|
||
|
||
1. **Alias di Importazione per il Modulo di Configurazione (`data.config`)**:
|
||
* Ho notato che il modulo `data.config.py` viene importato con alias diversi nei vari file (es. `config`, `app_config`, `fm_config`). Sarebbe opportuno standardizzare un unico alias (ad esempio `app_config`) per migliorare la leggibilità e la manutenibilità.
|
||
|
||
2. **Centralizzazione delle Costanti di Stato della GUI**:
|
||
* Le costanti `GUI_STATUS_*` (come `GUI_STATUS_OK`, `GUI_STATUS_ERROR`, ecc.) sono definite in `app_controller.py` e anche in `utils/gui_utils.py`. Il modulo `gui_utils.py` sembra la collocazione ideale per queste costanti. `AppController` e `MainWindow` potrebbero importarle da lì per evitare duplicazioni e garantire una singola fonte di verità. Anche i colori associati (`GUI_STATUS_COLORS_DEFAULT`) sono ben posizionati in `gui_utils.py`.
|
||
|
||
3. **Dipendenza Esterna Inattesa in `map_tile_manager.py`**:
|
||
* Nei metodi `stitch_map_image` e `_create_placeholder_tile_image` di `map_tile_manager.py` (precedentemente `map_manager.py`), c'è un'importazione `from geoelevation.image_processor import DEFAULT_FONT`. Questa sembra una dipendenza da un progetto esterno (`geoelevation`) che potrebbe non essere inclusa in `FlightMonitor`. Se si tratta di un refuso, il font dovrebbe essere gestito localmente o tramite `map_constants.py`.
|
||
|
||
4. **Funzione `_draw_text_on_placeholder` Duplicata/Spostata**:
|
||
* Una funzione con questo nome (o molto simile) sembra esistere sia in `map_drawing.py` che come metodo privato `_draw_text_on_placeholder` in `map_tile_manager.py`. La versione in `MapTileManager` sembra quella attualmente utilizzata internamente per i suoi placeholder. Bisognerebbe verificare se la versione in `map_drawing.py` ha uno scopo diverso o se può essere rimossa/consolidata.
|
||
|
||
5. **Tecnica di Mocking nei Test di `storage.py`**:
|
||
* Nel blocco `if __name__ == "__main__":` di `storage.py`, la configurazione `app_config` viene sovrascritta globalmente usando `globals()['app_config'] = MockConfig()`. Sebbene funzioni per test standalone, è una pratica un po' invasiva. Per test più strutturati (es. con `unittest`), sarebbe preferibile usare `unittest.mock.patch` o passare l'oggetto di configurazione come dipendenza.
|
||
|
||
6. **Utilizzo di `print` invece del Logger**:
|
||
* Nel modulo `data/opensky_live_adapter.py` (nei metodi `run` e `stop`), ci sono alcune istruzioni `print` che potrebbero essere sostituite con chiamate al `module_logger` per uniformare la gestione dei log.
|
||
|
||
7. **Coerenza Nomi File/Classi**:
|
||
* Inizialmente, avevi menzionato un file `map_manager.py` contenente la classe `MapTileManager`. Dai file forniti, il file si chiama `map_tile_manager.py` e contiene la classe `MapTileManager`, quindi questo punto sembra risolto e coerente.
|
||
|
||
**Analisi delle Costanti e Variabili:**
|
||
|
||
* **`logging_config.py`**: Sembra essere il posto corretto per tutte le configurazioni specifiche del logging (livelli, formati, colori per il logger GUI, ecc.). Il dizionario `LOGGING_CONFIG` è ben strutturato.
|
||
* **`data/config.py`**: Contiene costanti globali dell'applicazione come URL dell'API, BBOX di default, configurazioni del database. È la collocazione appropriata. L'unica osservazione è l'alias di importazione menzionato sopra.
|
||
* **`map/map_constants.py`**: Contiene costanti specifiche per la visualizzazione e l'interazione della mappa (colori, spessori, livelli di zoom di default, formattazione coordinate). Anche questa è una buona centralizzazione.
|
||
* **`utils/gui_utils.py`**: Come discusso, è il posto ideale per le costanti di stato della GUI (`GUI_STATUS_*`) e i relativi colori.
|
||
* **Costanti interne ai moduli/classi**: Molte costanti specifiche per la logica interna di una classe o di un modulo (es. `INITIAL_BACKOFF_DELAY_SECONDS` in `OpenSkyLiveAdapter`, `SEMAPHORE_SIZE` in `MainWindow`) sono definite localmente, il che è corretto perché il loro scope è limitato.
|
||
|
||
Nel complesso, la struttura per la gestione delle configurazioni e delle costanti è buona, con una chiara separazione delle responsabilità. Le principali aree di miglioramento riguardano la coerenza degli alias e la centralizzazione di alcune costanti GUI.
|
||
|
||
Se sei d'accordo, possiamo iniziare ad affrontare questi punti uno per uno. Inizieresti con la standardizzazione degli alias per `data.config.py` o preferisci un altro punto? Fammi sapere come vuoi procedere! |