Compare commits
No commits in common. "master" and "v.0.0.0.38" have entirely different histories.
master
...
v.0.0.0.38
3
.vscode/launch.json
vendored
3
.vscode/launch.json
vendored
@ -8,8 +8,7 @@
|
|||||||
"name": "aircraft db",
|
"name": "aircraft db",
|
||||||
"type": "debugpy",
|
"type": "debugpy",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"module": "flightmonitor.data.aircraft_database_manager",
|
"module": "/data/aircraft_database_manager"
|
||||||
"cwd": "${workspaceFolder}"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "flight monitor",
|
"name": "flight monitor",
|
||||||
|
|||||||
25
GEMINI.md
25
GEMINI.md
@ -1,25 +0,0 @@
|
|||||||
Sono un ingegnere informatico che sviluppa principalmente in python e c++.
|
|
||||||
|
|
||||||
Quando mi proponi del codice ricordati di indicarmi le modifiche con il codice precedente, perchè le hai fatte, dove e come sono state fatte.
|
|
||||||
Utilizzando tutte le regole per scrivere in maniera migliore possibile codice in python.
|
|
||||||
Non dare nulla per scontato e spiega tutti i passi che segui nei tuoi ragionamenti.
|
|
||||||
Con me parla in italiano.
|
|
||||||
|
|
||||||
Come deve essere scritto il codice:
|
|
||||||
1) come standard di scrittura del codice Python, lo standard PEP8.
|
|
||||||
2) una istruzione per ogni riga di codice
|
|
||||||
3) nomi di funzioni, variabili, commenti, doc_string devono essere in inglese
|
|
||||||
4) codice più chiaro, ordinato, riutilizzabile
|
|
||||||
5) i commenti nel codice devono essere essenziali, stringati e chiari, non devono essere prolissi. dobbiamo cercare di mantenere il codice più pulito possibile, senza troppi fronzoli
|
|
||||||
6) Non indicare le modifche che fai come commento al codice, ma solo in linea generale in chat. il codice lascialo più pulito possibile
|
|
||||||
|
|
||||||
Per semplificare l'operazione di aggiornamento del codice:
|
|
||||||
1) se le modifiche che proponi interessano solo poche funzioni del modulo, allora indicami il contenuto di tutte le funzioni dove ci sono le modifiche.
|
|
||||||
2) se le modifiche impattano la maggior parte delle funzioni dello stesso modulo, allora ripeti per intero il codice del modulo senza omissioni.
|
|
||||||
3) se le modifiche che proponi interessano meno di 5 righe di una funzione, indicami quali sono le linee che cambiano e come modificarle
|
|
||||||
4) passami sempre un modulo alla volta e ti dico io quando passarmi il successivo, sempre in maniera completa e senza omissioni
|
|
||||||
|
|
||||||
Se vedi che il codice di un singolo modulo è più lungo di 1000 righe, prendi in considerazione il fatto di creare un nuovo modulo spostando quelle funzioni che sono omogenee per argomento in questo nuovo modulo e rendere più leggere il file che sta crescendo troppo.
|
|
||||||
|
|
||||||
Quando ti passo del codice da analizzare, cerca sempre di capirne le funzionalità e se hai da proporre dei miglioramenti o delle modifiche prima ne discuti con me e poi decidiamo se applicarlo oppure no.
|
|
||||||
Se noti che nel codice c'è qualcosa da migliorare, ne parli con me e poi vediamo se applicarlo oppure no, per evitare di mettere mano a funzioni che sono già state ottimizzate e funzionano come io voglio, e concentrarsi sulla risoluzione di problemi o l'introduzione di nuove funzioni.
|
|
||||||
@ -1,8 +1 @@
|
|||||||
{
|
{}
|
||||||
"NordItaliaCentrale_44-48_7-12": {
|
|
||||||
"lat_min": 44.0,
|
|
||||||
"lon_min": 7.0,
|
|
||||||
"lat_max": 48.0,
|
|
||||||
"lon_max": 12.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -616,21 +616,8 @@ class MapCanvasManager:
|
|||||||
new_lon, new_lat, _ = geod.fwd(new_lon, new_lat, 0, dmy)
|
new_lon, new_lat, _ = geod.fwd(new_lon, new_lat, 0, dmy)
|
||||||
self._request_base_map_render(new_lat, new_lon, self._current_zoom_gui)
|
self._request_base_map_render(new_lat, new_lon, self._current_zoom_gui)
|
||||||
|
|
||||||
def center_map_and_fit_patch(
|
def center_map_and_fit_patch(self, lat: float, lon: float, size_km: float):
|
||||||
self, lat: float, lon: float, size_km: Optional[float] = None, patch_size_km: Optional[float] = None
|
zoom_w = calculate_zoom_level_for_geographic_size(lat, size_km * 1000, self.canvas_width, self.tile_manager.tile_size)
|
||||||
):
|
zoom_h = calculate_zoom_level_for_geographic_size(lat, size_km * 1000, self.canvas_height, self.tile_manager.tile_size)
|
||||||
"""
|
|
||||||
Center the map at (lat, lon) and set zoom to fit a square patch of approx size_km (or patch_size_km).
|
|
||||||
Backwards-compatible: callers may pass either `size_km` (old name) or `patch_size_km` (new callers).
|
|
||||||
If both are provided, `patch_size_km` takes precedence.
|
|
||||||
"""
|
|
||||||
# Prefer explicitly named `patch_size_km` if provided for newer callers
|
|
||||||
chosen_size_km = patch_size_km if patch_size_km is not None else size_km
|
|
||||||
if chosen_size_km is None:
|
|
||||||
logger.error("center_map_and_fit_patch called without a size_km or patch_size_km value")
|
|
||||||
return
|
|
||||||
|
|
||||||
zoom_w = calculate_zoom_level_for_geographic_size(lat, chosen_size_km * 1000, self.canvas_width, self.tile_manager.tile_size)
|
|
||||||
zoom_h = calculate_zoom_level_for_geographic_size(lat, chosen_size_km * 1000, self.canvas_height, self.tile_manager.tile_size)
|
|
||||||
zoom = min(zoom_w, zoom_h) - 1 if zoom_w and zoom_h else map_constants.DEFAULT_INITIAL_ZOOM
|
zoom = min(zoom_w, zoom_h) - 1 if zoom_w and zoom_h else map_constants.DEFAULT_INITIAL_ZOOM
|
||||||
self._request_base_map_render(lat, lon, max(map_constants.MIN_ZOOM_LEVEL, zoom))
|
self._request_base_map_render(lat, lon, max(map_constants.MIN_ZOOM_LEVEL, zoom))
|
||||||
86
todo.md
86
todo.md
@ -1,15 +1,91 @@
|
|||||||
|
Perfetto, ora ho capito esattamente cosa intendi e hai assolutamente ragione. La mia proposta precedente era troppo semplicistica e non teneva conto del rate-limiting dell'API in un ciclo di download potenzialmente lungo. La tua osservazione è corretta e fondamentale per la robustezza della funzione.
|
||||||
|
|
||||||
# TODOs
|
Mi scuso per l'incomprensione. La tua spiegazione è chiarissima. Riformuliamo il concetto e il piano, integrando correttamente i due parametri.
|
||||||
# owner: team-ris
|
|
||||||
# conventions: - use tags #tag, owner @user, prio:<low|med|high>, est:<time>
|
|
||||||
|
|
||||||
|
### Correzione del Concetto e del Flusso
|
||||||
|
|
||||||
- [ ] https://opensky-network.org/datasets/#metadata/ aggiungere la funzione che permetta di scaricare i dati dei voli dal sito
|
Hai due esigenze distinte ma collegate:
|
||||||
|
|
||||||
|
1. **Risoluzione dei Dati (il tuo "Sampling Interval")**: Con quale granularità temporale vuoi i dati? Ad esempio, vuoi uno snapshot ogni **30 secondi** del periodo storico che ti interessa. Questo è il `time_step` del nostro ciclo.
|
||||||
|
2. **Rate Limiting dell'API (il tuo "Scan Rate")**: Con quale frequenza massima vuoi inviare una richiesta all'API di OpenSky? Ad esempio, non più di una richiesta ogni **15 secondi**. Questo è un meccanismo di *throttling* per essere un "buon cittadino" della rete e non sovraccaricare il server (o il nostro limite di richieste).
|
||||||
|
|
||||||
|
**Il flusso corretto del `HistoricalAdapter` diventa quindi questo:**
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Dentro il metodo run() dell'HistoricalAdapter
|
||||||
|
current_time = start_time
|
||||||
|
last_api_request_time = 0
|
||||||
|
|
||||||
# FIXME List
|
while current_time <= end_time:
|
||||||
|
|
||||||
|
# 1. THROTTLING (Controllo del Rate Limiting)
|
||||||
|
time_since_last_request = time.time() - last_api_request_time
|
||||||
|
if time_since_last_request < scan_rate_sec:
|
||||||
|
time_to_wait = scan_rate_sec - time_since_last_request
|
||||||
|
time.sleep(time_to_wait) # Attesa per rispettare il rate
|
||||||
|
|
||||||
|
# 2. CONTROLLO CRONOLOGIA (Opzionale, come discusso)
|
||||||
|
# if is_interval_already_scanned(current_time, bbox):
|
||||||
|
# # Salta questa iterazione e avanza l'orologio
|
||||||
|
# current_time += sampling_interval_sec
|
||||||
|
# continue
|
||||||
|
|
||||||
|
# 3. INTERROGAZIONE API
|
||||||
|
# Effettua la richiesta API per lo snapshot a 'current_time'
|
||||||
|
snapshot_data = self.api.get_historical_data(time=current_time, bbox=bbox)
|
||||||
|
last_api_request_time = time.time() # Aggiorna il timestamp dell'ultima richiesta
|
||||||
|
|
||||||
|
# 4. ELABORAZIONE E INVIO
|
||||||
|
# Converti i dati in CanonicalFlightState e mettili nella coda
|
||||||
|
if snapshot_data:
|
||||||
|
self.output_queue.put(snapshot_data)
|
||||||
|
|
||||||
|
# 5. AVANZAMENTO OROLOGIO VIRTUALE
|
||||||
|
# Avanza l'orologio del nostro "sampling interval"
|
||||||
|
current_time += sampling_interval_sec
|
||||||
|
```
|
||||||
|
|
||||||
|
Questo flusso rispetta entrambi i parametri:
|
||||||
|
* Chiede i dati a intervalli definiti dal `sampling_interval_sec`.
|
||||||
|
* Ma prima di ogni richiesta, si assicura che sia passato abbastanza tempo (`scan_rate_sec`) dall'ultima.
|
||||||
|
|
||||||
|
### Piano di Sviluppo Definitivo (Aggiornato con i tuoi requisiti)
|
||||||
|
|
||||||
|
Il piano rimane lo stesso nelle sue fasi, ma con i contenuti corretti.
|
||||||
|
|
||||||
|
1. **Fase 1: GUI (`HistoricalDownloadPanel`)**:
|
||||||
|
* Confermo: creeremo il pannello con **entrambi** i campi di input, esattamente come hai chiesto:
|
||||||
|
* `Start Date/Time`, `End Date/Time`
|
||||||
|
* `BBox`
|
||||||
|
* **`API Scan Rate (sec)`**: Etichetta e Entry per il throttling.
|
||||||
|
* **`Sampling Interval (sec)`**: Etichetta e Entry per la risoluzione dei dati.
|
||||||
|
* L'orologio virtuale e i bottoni rimangono come discusso.
|
||||||
|
|
||||||
|
2. **Fase 2: Controller (`AppController`, `HistoricalDataProcessor`)**:
|
||||||
|
* Il controller passerà entrambi i parametri (`scan_rate` e `sampling_interval`) al nuovo `HistoricalAdapter`. Il resto della sua logica (gestione thread, avvisi, etc.) rimane valido.
|
||||||
|
|
||||||
|
3. **Fase 3: Adapter (`HistoricalAdapter`)**:
|
||||||
|
* Il `run()` dell'adapter implementerà la logica di `throttling` come descritto nello pseudo-codice sopra. Sarà la modifica chiave rispetto al piano precedente.
|
||||||
|
|
||||||
|
4. **Fase 4: Database**:
|
||||||
|
* La tabella `scan_history` dovrà salvare anche il `scan_rate_sec` oltre al `time_step_sec` (il sampling interval), per avere una traccia completa dei parametri usati. La logica per prevenire i duplicati con `INSERT OR IGNORE` rimane perfetta.
|
||||||
|
* Struttura finale della tabella `scan_history`:
|
||||||
|
```sql
|
||||||
|
CREATE TABLE IF NOT EXISTS scan_history (
|
||||||
|
scan_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
start_timestamp REAL NOT NULL,
|
||||||
|
end_timestamp REAL NOT NULL,
|
||||||
|
sampling_interval_sec INTEGER, -- Tuo "Sampling Interval"
|
||||||
|
scan_rate_sec INTEGER, -- Tuo "Scan Rate"
|
||||||
|
lat_min REAL NOT NULL,
|
||||||
|
lon_min REAL NOT NULL,
|
||||||
|
lat_max REAL NOT NULL,
|
||||||
|
lon_max REAL NOT NULL,
|
||||||
|
status TEXT,
|
||||||
|
completed_at REAL
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
Grazie mille per la precisazione, era fondamentale. Ora il piano è corretto e completo.
|
||||||
|
|
||||||
|
**Sei d'accordo con questa versione finale?** Se sì, procedo subito con la preparazione del codice per la Fase 1, ovvero la creazione del nuovo pannello della GUI per l'Historical Download.
|
||||||
Loading…
Reference in New Issue
Block a user