SXXXXXXX_PyMsc/doc/ARTOS_integration.md

302 lines
11 KiB
Markdown

# Integrazione di pybusmonitor1553 con ARTOS / uso come modulo
Questo documento descrive come caricare, configurare e utilizzare il modulo `pybusmonitor1553` all'interno di un'applicazione esterna (es. ARTOS Collector). Fornisce istruzioni chiare per l'inizializzazione, il controllo, la ricezione/invio di messaggi e le pratiche consigliate per l'integrazione.
## Scopo
- Consentire a un'applicazione esterna di usare `pybusmonitor1553` come modulo aggiuntivo.
- Fornire esempi pratici per inizializzazione, registrazione callback, invio comandi, registrazione/replay e diagnostica.
## Componenti chiave
- `BusMonitorCore` — controller principale e interfaccia di alto livello per integrazione.
- `ConnectionManager` — gestisce import lazy e loop di invio/ricezione.
- Libreria dei messaggi: `Grifo_E_1553lib` (inclusa nel package).
- Esempio di riferimento: `examples/artos_integration_example.py`.
## Requisiti
- Python compatibile con il repository.
- Le dipendenze del repository devono essere installate (usare l'ambiente del progetto).
- Accesso di rete UDP alle porte usate per invio/ricezione.
- Variabili d'ambiente utili:
- `PYBM_RX_IP` (bind, default `127.0.0.1`)
- `PYBM_RX_PORT` (porta ricezione, default `5002`)
- `PYBM_TX_PORT` (porta invio, default `5001`)
## Installazione e avvio rapido
Eseguire il monitor standalone (GUI):
```bash
python -m pybusmonitor1553
```
Per usare il modulo da un'altra applicazione, importare e istanziare `BusMonitorCore` (vedi esempi sotto).
## Pattern di integrazione consigliato
Sequenza tipica quando si usa `pybusmonitor1553` come modulo:
1. Creare istanza: `bm = BusMonitorCore()`
2. Inizializzare: `bm.initialize(config)` (config opzionale, altrimenti usa env)
3. Registrare callback: `bm.register_callback(label, callback)`
4. Avviare sessione: `bm.start_session()`
5. Fermare sessione: `bm.stop_session()`
6. Usare: `get_status()`, `get_message(label)`, `wait_for_message(label, timeout)` per interrogazioni e sincronizzazioni
## API principali (sintesi)
- `BusMonitorCore()` — crea l'istanza del modulo.
- `initialize(config: Dict) -> bool` — importa la libreria dei messaggi e prepara i wrapper.
- `config` può contenere `'ip'`, `'send_port'`, `'recv_port'`.
- `start_session()` / `stop_session()` — avvia/ferma i loop di invio/ricezione.
- `register_callback(label: str, callback: Callable)` — callback chiamata al ricevimento di messaggi (argomento: oggetto messaggio).
- `get_status() -> Dict` — restituisce stato corrente: `is_running`, `connected`, `messages_count`, `recording`, `recorded_events`, `errors`.
- `get_message(label: str) -> Optional[Any]` — ottiene il wrapper del messaggio.
- `get_all_messages() -> Dict[str, Any]` — lista wrapper disponibili.
- `wait_for_message(label: str, timeout: float) -> bool` — blocca fino al messaggio o timeout.
- `start_recording(filepath: Optional[Path])`, `stop_recording(save: bool=True)`, `load_recording(filepath: Path)` — registrazione e replay.
### Note importanti sulle callback
- I callback vengono invocati dal thread di ricezione: devono essere non-bloccanti. Per elaborazioni pesanti, spostare il lavoro su un worker thread o una coda.
## Come inviare comandi / modificare messaggi
1. Recuperare il wrapper:
```python
msg = bm.get_message("A2")
```
2. Modificare campi usando l'API dei field (varia per messaggio):
```python
try:
msg.message.rdr_mode_command.set_master_mode(1)
except Exception:
msg.message.rdr_mode_command.raw = 1
# Invio esplicito se supportato
if hasattr(msg, 'send'):
msg.send()
```
3. Se il wrapper non espone `.send()`, assicurarsi che il `MajorFrame`/send-loop sia attivo con `start_session()`.
## Esempio di integrazione (snippet)
```python
from pybusmonitor1553.core.bus_monitor_core import BusMonitorCore
bm = BusMonitorCore()
config = {'ip': '127.0.0.1', 'send_port': 5001, 'recv_port': 5002}
if not bm.initialize(config):
raise RuntimeError("Impossibile inizializzare BusMonitor1553")
def on_a2(msg):
print("A2 ricevuto:", msg)
bm.register_callback("msg_a2", on_a2)
bm.start_recording()
bm.start_session()
if bm.wait_for_message("msg_a2", timeout=5.0):
print("A2 arrivato")
print(bm.get_status())
bm.stop_recording(save=True)
bm.stop_session()
```
Per un esempio completo e commentato vedere: `examples/artos_integration_example.py`.
## Best practices
- Non bloccare i callback: usare code/worker.
- Verificare `initialize()` e `manager.import_error` per diagnosi di import.
- Usare `wait_for_message` per sincronizzare test automatizzati.
- Usare `start_recording`/`stop_recording` per acquisire eventi di test e riprodurli.
- Per modifiche ai messaggi, consultare le definizioni in `Grifo_E_1553lib.messages`.
## Logging e diagnostica
- Abilitare logging di debug:
```python
import logging
logging.basicConfig(level=logging.DEBUG)
```
- Controllare `bm.get_status()['errors']` e `bm.manager.import_error` per problemi di import/bind.
## Testing e validazione
- Usare `examples/artos_integration_example.py` come caso di test.
- Controlli chiave:
- `initialize()` ritorna `True`.
- `start_session()` attiva `is_running` e `connected`.
- `wait_for_message()` e le callback vengono eseguite entro i timeout previsti.
## Troubleshooting rapido
- ImportError libreria messaggi: controllare `bm.manager.import_error`.
- Nessun messaggio ricevuto: verificare bind IP/porte e che `init_recv_sock()` non fallisca.
- Callback non chiamata: verificare label usata (es. `"msg_a2"` vs `"A2"`) con `get_all_messages()`.
- Invio non funzionante: assicurarsi che il send-loop sia attivo (`start_session()`) e che il wrapper supporti `.send()`.
## Riferimenti nel repository
- Codice core: [pybusmonitor1553/core/bus_monitor_core.py](pybusmonitor1553/core/bus_monitor_core.py)
- Gestione connessioni: [pybusmonitor1553/core/connection_manager.py](pybusmonitor1553/core/connection_manager.py)
- Esempio ARTOS: [examples/artos_integration_example.py](examples/artos_integration_example.py)
- Entrypoint GUI: [pybusmonitor1553/__main__.py](pybusmonitor1553/__main__.py)
---
Se vuoi, posso aggiungere al documento esempi specifici per l'invio di A2/A1 con i nomi dei campi letti dalle definizioni nel pacchetto `Grifo_E_1553lib`.
## Esempi dettagliati: A1/A2 (comandi) e B6/B7 (stato)
Qui sotto trovi esempi pratici completi che mostrano come impostare comandi su A1/A2 e come leggere stati/tellback su B6/B7. Gli snippet assumono che `bm` sia un'istanza inizializzata di `BusMonitorCore` e che la sessione sia avviata (`start_session()`).
### 1) Esempio: inviare comando A1 (impostazioni / parametri)
Questo esempio mostra come ottenere il wrapper A1, modificare un campo e inviare il messaggio (se il wrapper espone `.send()`). Si forniscono fallback per l'accesso ai campi.
```python
from pathlib import Path
from pybusmonitor1553.core.bus_monitor_core import BusMonitorCore
bm = BusMonitorCore()
bm.initialize({'ip': '127.0.0.1'})
bm.start_session()
# Recupera wrapper A1
msg_a1 = bm.get_message('A1')
if msg_a1 is None:
raise RuntimeError('Wrapper A1 non trovato')
# Esempio: impostare un parametro nelle impostazioni operative (campo `settings`)
try:
# API field-specific: usare i metodi esposti da RDROperationalSettings se disponibili
# Qui usiamo il campo `settings` presente in MsgRdrSettingsAndParameters
if hasattr(msg_a1.message.settings, 'set_history_level'):
msg_a1.message.settings.set_history_level(4)
else:
msg_a1.message.settings.raw = 4
except Exception:
raise
# Invio esplicito se disponibile
if hasattr(msg_a1, 'send'):
msg_a1.send()
else:
# Se non c'è send, MajorFrame invierà A1 al prossimo ciclo se configurato
print('Invio A1: il wrapper non espone .send(); MajorFrame invierà il valore al prossimo frame')
print('A1 inviato / aggiornato')
bm.stop_session()
```
### 2) Esempio: inviare comando A2 (modalità / comandi radar)
L'esempio mostra come cambiare la modalità principale (campo `rdr_mode_command`) in A2.
```python
bm = BusMonitorCore()
bm.initialize({})
bm.start_session()
msg_a2 = bm.get_message('A2')
if msg_a2 is None:
raise RuntimeError('Wrapper A2 non trovato')
# Impostare modalità radar (campo `rdr_mode_command` presente in MsgRdrOperationCommand)
try:
if hasattr(msg_a2.message.rdr_mode_command, 'set_master_mode'):
msg_a2.message.rdr_mode_command.set_master_mode(1) # valore ICD di esempio
else:
msg_a2.message.rdr_mode_command.raw = 1
except Exception:
raise
if hasattr(msg_a2, 'send'):
msg_a2.send()
else:
print('A2 aggiornato (MajorFrame invierà il dato)')
bm.stop_session()
```
### 3) Esempio: leggere stato B6 (RdrSettingsAndParametersTellback)
B6 viene usato come tellback per ottenere impostazioni e parametri dal radar. Questo snippet mostra come registrare una callback per lettura asincrona e come leggere il valore al volo.
```python
bm = BusMonitorCore()
bm.initialize({})
bm.start_session()
def on_b6(msg):
try:
# I campi nel tellback usano suffissi _tellback (es. settings_tellback)
settings_tb = getattr(msg.message, 'settings_tellback', None)
freq_tb = getattr(msg.message, 'frequency_tellback', None)
print('B6 ricevuto: settings_tellback=', settings_tb, ' frequency_tellback=', freq_tb)
except Exception as e:
print('Errore nella callback B6:', e)
bm.register_callback('msg_b6', on_b6)
# Alternativa: lettura sincrona via get_message + accesso a campi tellback
msg_b6 = bm.get_message('B6')
if msg_b6:
try:
print('B6 - settings_tellback.raw:', getattr(msg_b6.message, 'settings_tellback').raw)
except Exception:
pass
import time
time.sleep(2)
bm.stop_session()
```
### 4) Esempio: leggere stato B7 (RdrStatusTellback)
B7 contiene lo stato operativo; l'esempio mostra lettura e callback.
```python
bm = BusMonitorCore()
bm.initialize({})
bm.start_session()
def on_b7(msg):
try:
# Il tellback per lo stato usa il campo `rdr_mode_tellback` e param*_tellback
rdr_status = getattr(msg.message, 'rdr_mode_tellback', None)
print('B7 ricevuto: rdr_mode_tellback=', rdr_status)
except Exception as e:
print('Errore nella callback B7:', e)
bm.register_callback('msg_b7', on_b7)
# Lettura diretta
msg_b7 = bm.get_message('B7')
if msg_b7:
try:
print('B7 - rdr_mode_tellback:', getattr(msg_b7.message, 'rdr_mode_tellback'))
except Exception:
pass
time.sleep(2)
bm.stop_session()
```
### Note sui nomi dei campi
- I nomi dei campi usati negli esempi (`settings`, `rdr_mode_command`, `status`, ecc.) sono quelli comunemente esposti dai wrappers interni ma possono variare a seconda della versione delle classi in `Grifo_E_1553lib.messages`.
- Per conoscere i nomi esatti, usare `print(sorted(dir(msg.message)))` sul wrapper ottenuto da `get_message()` oppure consultare i file in `pybusmonitor1553/Grifo_E_1553lib/messages/`.
### Raccomandazioni finali
- Testare gli snippet in un ambiente di laboratorio prima di usarli in produzione.
- Proteggere i percorsi critici (callback, invio) con try/except e logging.