SXXXXXXX_PyBusMonitor1553/doc/GrifoScope-Migration-Plan.md
2025-12-17 07:59:30 +01:00

672 lines
21 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Piano di Migrazione: GrifoScope → PyBusMonitor1553
## 📊 Situazione Attuale
### Sistema a 3 Componenti
```
┌─────────────────┐ UDP 1553 ┌─────────────────┐
│ GrifoScope │◄────────────────────────►│ Radar Server │
│ (BusMonitor) │ Port 50553 ← 60553 │ (Target Real) │
│ C++/Qt │ │ │
└─────────────────┘ └─────────────────┘
│ DA SOSTITUIRE CON
┌─────────────────┐ UDP 1553 ┌─────────────────┐
│PyBusMonitor1553 │◄────────────────────────►│ Radar Server │
│ (BusMonitor) │ Port 51553 ← 61553 │ (Target Real) │
│ Python │ │ │
└─────────────────┘ └─────────────────┘
```
**Obiettivo**: Far funzionare PyBusMonitor1553 esattamente come GrifoScope per comunicare con il Radar Server.
---
## 🔍 Analisi del Comportamento di GrifoScope
### 1. **Architettura di Comunicazione** (`qg1553overudp.cpp`)
GrifoScope usa un pattern **Bus Controller (BC)** che:
#### **Inizializzazione** (`Qg1553OverUdp::Implementation::BusGenerator()`)
```cpp
// Bind sulla porta locale 60553 (60000 + 1553)
s.bind(networkInterface(), 60000+1553);
// Indirizzo destinazione: broadcast o IP specifico radar
rtAddress = networkBroadcast(); // Es: 127.0.0.255
// Registra messaggi A (BC→RT, tr=0):
addMsg<msg_rdr_settings_and_parameters_t>(1, 0); // A1
addMsg<msg_rdr_operation_command_t>(2, 0); // A2
addMsg<msg_graphic_setting_t>(3, 0); // A3
addMsg<msg_nav_data_and_cursor_t>(4, 0); // A4
addMsg<msg_inu_high_speed_t>(5, 0); // A5
// Registra messaggi B (RT→BC, tr=1):
addMsg<msg_settings_tellback_t>(15, 1); // B6
addMsg<msg_rdr_status_tellback_t>(16, 1); // B7
```
**CRITICO**: GrifoScope invia **datagrammi multi-messaggio**:
```cpp
struct b1553_datagram_t {
uint16_t number_of_messages;
b1553_raw_message_t msgs[20]; // Fino a 20 messaggi in un frame
};
```
#### **Trasmissione Ciclica** (`doWork()`)
```cpp
// Timer a 10 secondi (10000 µs) per invio frame completo
scheduler->start(10000); // 100 Hz
// Ogni ciclo invia TUTTI i messaggi registrati in UN solo UDP datagram
s.writeDatagram(&msg, sizeof(msg), rtAddress, 50000+1553);
```
**Differenza Critica con PyBusMonitor**:
- GrifoScope → **1 datagram UDP** con **tutti i messaggi A1-A5** + request B6-B7
- PyBusMonitor → **multi datagram** (uno per messaggio) con scheduler separato
---
### 2. **Formato Pacchetto UDP1553** (`udp1553_types.h`, `b1553_udp.cpp`)
#### **Header UDP1553** (64 bytes - già implementato ✅)
```cpp
struct udp_1553_header_t {
uint16_t marker1553; // 0x1553
uint8_t vmajor, vminor; // Version
uint16_t otype; // 0x4342 ("BC") o 0x5452 ("RT")
uint8_t ta; // Terminal Address (0=BC, 20=RT)
uint8_t flags;
uint32_t fcounter; // Frame counter
uint32_t mcounter; // Master counter
// ... altri campi timing/debug
};
```
#### **Blocco Messaggio** (ripetuto N volte)
```
0x3C3C // MARKER_BEGIN
CW // Command Word (RT, TR, SA, WC)
SW // Status Word (solo RT replies)
ERRCODE // Error code
reserved[2] // 8 bytes reserved
DATA[wc] // Payload (wc words, Big Endian)
~CW // Inverted command word
0x3E3E // MARKER_END
```
#### **Terminatore Frame**
```
0x5315 // MARKER_END_1553
```
**⚠️ Problema Rilevato nel Dispatcher**:
Il parser PyBusMonitor ha già logica per multi-messaggio MA ha bug con terminatori (vedi `dispatcher.py` linee 180-220). GrifoScope usa layout fisso.
---
### 3. **Sequenza di Startup Radar** (`g346_a1a2.cpp`)
GrifoScope implementa sequenza di sicurezza **obbligatoria**:
```cpp
void GrifoMsg_A2B7::startProtection() {
dSilence.setFromUser(1); // RF SILENCE = ON
dStby.setFromUser(1); // STANDBY = ON
}
```
**Workflow Startup** (già parzialmente implementato ✅):
| Fase | Durata | STANDBY | SILENCE | Azione |
|------|--------|---------|---------|--------|
| **1 - Protection** | 2-3 cicli | ON | ON | Stato sicuro, radar non risponde |
| **2 - Activation** | 1 ciclo | OFF | OFF | Comando attivazione |
| **3 - Transition** | 6 cicli (~240ms) | OFF | OFF | Radar elabora cambio modo |
| **4 - Operational** | continuo | OFF | OFF | Monitoraggio B7 `transition_status` |
**Implementazione Python**: Vedi `RadarController.apply_startup_sequence()` - **già corretto**
---
### 4. **Gestione Request/Reply** (`b1553_udp.cpp`)
GrifoScope usa pattern **request-response**:
```cpp
// INVIO (BC → RT):
// - A1-A5: Comandi con tr=0, payload pieno
// - B6-B7: Richieste con tr=1, payload vuoto (solo CW)
// RICEZIONE (RT → BC):
// Radar risponde con stesso SA, tr=1, payload pieno
if (cw.str.tr == 1) { // RT→BC reply
// Copia dati nel buffer locale
memcpy(local_buffer[sa], data, wc*2);
}
```
**Ciclo Tipico**:
1. GrifoScope invia frame con A1-A5 (comandi) + B6-B7 (request vuote)
2. Radar risponde con frame separato contenente B6-B7 (dati pieni)
3. GrifoScope aggiorna display con tellback B7
**PyBusMonitor Status**:
- ✅ Invia A-messages (comandi)
- ⚠️ Invia B-requests MA non usa formato corretto (vedi sotto)
---
## 🔧 Differenze Critiche da Risolvere
**Riepilogo**:
-**2 problemi CRITICI** da risolvere (Multi-Message, B-Request)
-**3 aspetti già corretti** (Porte, Endianness, Startup)
---
### ❌ **PROBLEMA 1: Formato Multi-Messaggio**
**GrifoScope**:
```
UDP Header (64 bytes)
0x3C3C | CW_A1 | ... | DATA_A1[32] | ~CW_A1 | 0x3E3E
0x3C3C | CW_A2 | ... | DATA_A2[32] | ~CW_A2 | 0x3E3E
0x3C3C | CW_A3 | ... | DATA_A3[32] | ~CW_A3 | 0x3E3E
0x3C3C | CW_A4 | ... | DATA_A4[32] | ~CW_A4 | 0x3E3E
0x3C3C | CW_A5 | ... | DATA_A5[32] | ~CW_A5 | 0x3E3E
0x3C3C | CW_B6 | ... | [EMPTY] | ~CW_B6 | 0x3E3E ← REQUEST
0x3C3C | CW_B7 | ... | [EMPTY] | ~CW_B7 | 0x3E3E ← REQUEST
0x5315 (END marker)
```
**PyBusMonitor Attuale** (`scheduler.py`):
- Invia messaggi A/B in frame separati
- Usa rate diversi (50Hz, 25Hz, 6.25Hz)
- Non raggruppa i messaggi
**Radar potrebbe aspettarsi frame singolo con tutti i messaggi sincronizzati!**
---
### ❌ **PROBLEMA 2: B-Messages Request Format**
**GrifoScope** per richieste B (RT→BC):
```cpp
// B6/B7 request: tr=1, wc=32, payload=0 bytes (solo header)
addMsg<msg_settings_tellback_t>(15, 1); // SA=15, tr=1, wc=32
```
**PyBusMonitor Attuale**:
```python
# B-messages hanno payload pieno (32 words) anche in TX
self.msg_b7 = msgs.MsgB7() # Crea con _data[32] inizializzato
```
**ERRORE**: B-requests dovrebbero avere:
- `tr = 1` (RT transmit) ✅
- `wc = 32`
- **Payload VUOTO** (0 bytes data) ❌ → **Attualmente 64 bytes**
---
### ✅ **~~PROBLEMA 3: Porte UDP~~ - RISOLTO**
**VERIFICATO**: Le porte sono già correttamente allineate!
**GrifoScope**: 60000+1553 = **61553** (RX), 50000+1553 = **51553** (TX)
**PyBusMonitor**: RX_PORT = **61553**, TARGET_PORT = **51553**
**Nessuna modifica necessaria**
---
### ✅ **~~PROBLEMA 4: Endianness e Bit Numbering~~ - GIÀ CORRETTO**
**VERIFICATO**:
- ✅ Payload: Big Endian (network byte order) - **OK**
- ✅ Headers: Little Endian - **OK**
- ✅ Bit numbering: MSB=0 convention - **OK nei field descriptors**
**CRITICO da verificare**:
- Inverted CW (`~cw`) deve usare **bitwise NOT** su 16-bit
- Alcuni radar potrebbero essere sensibili a byte swap in `~CW`
---
### ⚠️ **PROBLEMA 5: Porte UDP - FALSO ALLARME** ✅
**VERIFICATO**: Le porte sono già allineate correttamente!
**Da verificare con test reale**:
- Inverted CW (`~cw`) usa bitwise NOT su 16-bit (già implementato ✅)
- Layout terminatore: `inv_cw (LE) → MARKER_END (LE)` (già implementato ✅)
~~Discrepanza di 1000 porte~~ → ✅ **IDENTICHE - Nessun problema**
---
### ⚠️ **PROBLEMA 6: Dispatcher Multi-Message Bug**
File `dispatcher.py` linee 180-250 contiene workaround per terminatori:
```python
# Accept if LE matches expected and control marker is correct
if (inv_cw_le == expected_inv_cw) and (ctrl_le == MARKER_END):
accepted = True
# Else try BE, fallback scans, etc...
```
**Issue**: Logica troppo permissiva causa falsi positivi. GrifoScope usa **layout fisso**:
```
DATA[wc] → inv_cw (LE) → MARKER_END (LE)
```
---
## ✅ Cosa Funziona Già (Non Toccare)
1. **Message Definitions** (`lib1553/messages/`) - ✅ Corrette vs C++ ICD
2. **Field Descriptors** (`fields.py`) - ✅ MSB=0 bit numbering
3. **PacketBuilder** (`packet_builder.py`) - ✅ UDP1553 header format
4. **Startup Sequence** (`controller.py`) - ✅ STANDBY/SILENCE phases
5. **Navigation Data** - ✅ Validity flags (inverse logic)
---
## 🎯 Piano d'Azione
### **FASE 1: Verifica Compatibilità Formato** (Diagnostica)
**Obiettivo**: Catturare traffico GrifoScope reale e confrontare con PyBusMonitor
#### Task 1.1: Cattura Pacchetti GrifoScope
```bash
# Usa Wireshark o tcpdump per catturare:
# - GrifoScope → Radar (porta 50553)
# - Radar → GrifoScope (porta 60553)
# Salva PCAP e analizza:
python tools/analyze_wireshark_dump.py grifoscope_capture.pcap
```
**Deliverable**: File con layout esatto frame GrifoScope
#### Task 1.2: Confronto Binario
```python
# tools/compare_grifoscope_packets.py
# Compara:
# - Header fields (fcounter, mcounter, etc.)
# - Message order (A1-A5, B6-B7)
# - Word count per messaggio
# - Payload content (campo per campo)
```
**Deliverable**: Report discrepanze
---
### **FASE 2: Fix Formato B-Request** (Codice)
**Problema**: B-messages inviate con payload pieno invece di vuoto
#### Task 2.1: Modificare PacketBuilder
```python
# pybusmonitor1553/core/packet_builder.py
def build_message_block(self, message, force_empty_payload=False):
"""
Args:
force_empty_payload: Se True, invia solo header senza data
(usato per B-requests)
"""
if force_empty_payload:
# tr=1, wc=32 MA data_bytes=0
return header_bytes + inv_cw + marker_end
else:
# tr=0 o tr=1 con payload
return header_bytes + data_bytes + inv_cw + marker_end
```
#### Task 2.2: Flag nelle Message Class
```python
# lib1553/message_base.py
class MessageBase:
IS_TRANSMIT = False # Existing
REQUEST_MODE = False # NEW: True per B-requests (no payload)
# lib1553/messages/msg_b7.py
class MsgB7(MessageBase):
IS_TRANSMIT = True
REQUEST_MODE = True # ← B-messages sono requests!
```
**Deliverable**: B-requests inviate con 0 data bytes
---
### **FASE 3: Implementare Frame Multi-Messaggio** (Architettura)
**Problema**: Inviare tutti i messaggi in UN frame come GrifoScope
#### Task 3.1: Nuovo Builder Pattern
```python
# pybusmonitor1553/core/packet_builder.py
def build_multi_message_frame(self, messages):
"""
Costruisce frame singolo con N messaggi (stile GrifoScope).
Args:
messages: Lista [MsgA1, MsgA2, ..., MsgB7]
Returns:
bytes: UDP datagram completo
"""
frame = UDP1553Header(...) # 64 bytes
for msg in messages:
# Determina se request (B-msg) o command (A-msg)
is_request = getattr(msg, 'REQUEST_MODE', False)
frame += build_message_block(msg, force_empty_payload=is_request)
frame += MARKER_END_1553
return frame
```
#### Task 3.2: Modificare Scheduler
```python
# pybusmonitor1553/core/scheduler.py
class TrafficScheduler:
def __init__(self, controller, network):
self.mode = 'GRIFOSCOPE' # NEW: 'LEGACY' o 'GRIFOSCOPE'
def _schedule_grifoscope_mode(self):
"""
Invia frame singolo con tutti i messaggi ogni 40ms (25 Hz).
"""
messages = [
self.controller.msg_a1,
self.controller.msg_a2,
self.controller.msg_a3,
self.controller.msg_a4,
self.controller.msg_a5,
self.controller.msg_b6, # Request
self.controller.msg_b7, # Request
]
frame = self.builder.build_multi_message_frame(messages)
self.network.send(frame, self.target_ip, self.target_port)
```
**Deliverable**: Modalità compatibile GrifoScope attivabile via flag
---
### **FASE 4: Fix Dispatcher Multi-Message** (Parser)
**Problema**: Parser troppo permissivo con terminatori
#### Task 4.1: Semplificare Logica
```python
# pybusmonitor1553/core/dispatcher.py
def parse_packet(self, raw_data):
# REMOVE: Fallback scans, BE/LE alternation
# USE: Strict layout matching GrifoScope
# Expected: DATA → inv_cw (LE) → MARKER_END (LE)
inv_cw, ctrl_marker = struct.unpack_from('<HH', raw_data, offset)
if inv_cw != expected_inv_cw:
raise ValueError(f"Invalid ~CW at offset {offset}")
if ctrl_marker != MARKER_END:
raise ValueError(f"Invalid MARKER_END at offset {offset}")
```Network Configuration** (Config) - OPZIONALE
#### Task 5.1: ~~Porte UDP~~ - GIÀ CORRETTE ✅
Le porte sono identiche a GrifoScope. Nessuna modifica necessaria.
#### Task 5.2: Broadcast vs Unicast (Opzionale)
```python
# Verificare con test reale se radar richiede broadcast
# GrifoScope usa: rtAddress = networkBroadcast()
# Può essere 127.0.0.255 (broadcast) o 127.0.0.1 (unicast)
BROADCAST_MODE = os.getenv('PYBM_BROADCAST', 'false').lower() == 'true'
if BROADCAST_MODE:
TARGET_IP = "127.0.0.255" # Broadcast locale
else:
TARGET_IP = "127.0.0.1" # Unicast (default attuale)
```
**Deliverable**: Test con radar reale determinerà necessità
TARGET_IP = "127.0.0.255" # Broadcast locale
else:
TARGET_IP = "127.0.0.1"
```
**Deliverable**: Flag ambiente per compatibilità totale
---
### **FASE 6: Testing con Radar Reale** (Validazione)
#### Task 6.1: Test di Connessione
```bash
# 1. Stop GrifoScope
# 2. Avvia PyBusMonitor in GRIFOSCOPE_MODE
export PYBM_GRIFOSCOPE_MODE=true
python -m pybusmonitor1553
# 3. Verifica:
# - Radar risponde con B-messages?
# - Transition status in B7 arriva a OPERATIONAL?
# - Display radar mostra parametri corretti?
```
#### Task 6.2: Confronto Comportamentale
| Metrica | GrifoScope | PyBusMonitor | Status |
|---------|-----------|--------------|--------|
| Radar risponde | ✅ SI | ❓ DA VERIFICARE | |
| B7 transition complete | ✅ 6 cicli | ❓ DA VERIFICARE | |
| Parametri radar aggiornati | ✅ SI | ❓ DA VERIFICARE | |
| Frame rate | 100 Hz | 25 Hz → 100 Hz | TODO |
**Deliverable**: Report test con radar vivo
---
## 📁 Struttura Modifiche al Codice
```
pybusmonitor1553/
├── core/
│ ├── packet_builder.py # ✏️ MODIFY: Multi-message frame
│ ├── scheduler.py # ✏️ MODIFY: GRIFOSCOPE mode
│ ├── dispatcher.py # ✏️ FIX: Strict terminator parsing
│ └── controller.py # ✅ OK (startup sequence già corretto)
├── lib1553/
│ ├── message_base.py # ✏️ ADD: REQUEST_MODE flag
│ └── messages/
│ ├── msg_b*.py # ✏️ SET: REQUEST_MODE = True
└── __main__.py # ✏️ ADD: GRIFOSCOPE_MODE env var
tools/
├── compare_grifoscope_packets.py # 🆕 NEW: Binary comparison
├── analyze_wireshark_dump.py # 🆕 NEW: PCAP parser
└── simple_rt_responder.py # ✅ OK (test tool)
doc/
└── GrifoScope-Migration-Plan.md # 📄 THIS FILE
```
---
## 🚦 Roadmap Implementazione
### **Sprint 1 (Settimana 1): Diagnostica**
- [ ] Cattura traffico GrifoScope con Wireshark
- [ ] Analizza formato esatto frame
- [ ] Identifica differenze vs PyBusMonitor
- [ ] Scrivi tool `compare_grifoscope_packets.py`
### **Sprint 2 (Settimana 2): Fix Formato**
- [ ] Implementa `REQUEST_MODE` flag
- [ ] Modifica `PacketBuilder.build_message_block()`
- [ ] Test con `simple_rt_responder.py`
- [ ] Verifica B-requests inviate senza payload
### **Sprint 3 (Settimana 3): Multi-Message**
- [ ] Implementa `build_multi_message_frame()`
- [ ] Aggiungi `GRIFOSCOPE` mode a scheduler
- [ ] Test frame rate (25 Hz vs 100 Hz)
- [ ] Benchmark performance
### **Sprint 4 (Settimana 4): Integration**
- [ ] Fix dispatcher strict parsing
- [ ] Allinea porte (60553/50553)
- [ ] Test con radar simulato
- [ ]**TEST CON RADAR REALE**
### **Sprint 5 (Settimana 5): Hardening**
- [ ] Handle edge cases (timeout, packet loss)
- [ ] Logging migliorato per debug
- [ ] Documentazione operativa
- [ ] Training team su modalità GrifoScope
---
## ⚠️ Rischi e Mitigazioni
| Rischio | Probabilità | Impatto | Mitigazione |
|---------|-------------|---------|-------------|
| Radar hardcoded su porte GrifoScope | Alta | Alto | Usare stesse porte in GRIFOSCOPE_MODE |
| Frame format incompatibile | Media | Alto | Cattura PCAP prima di modifiche |
| ~~Porte UDP diverse~~ | ✅ RISOLTO | - | Porte già allineate (61553/51553) |
| Frame format incompatibile | Media | Alto | Cattura PCAP prima di modifiche |
| Timing critico (100 Hz) | Media | Medio | Benchmark Python threading performance |
| Radar richiede broadcast IP | Bassa | Medio | Test con 127.0.0.255 vs 127.0.0.1 |
| Radar richiede checksum/CRC | Bassa | Alto | Analizza header `errcode` field
## 📊 Metriche di Successo
### **Criteri di Accettazione**
**MUST HAVE**:
1. Radar risponde ai comandi A-messages
2. Radar invia B-messages di tellback
3. Transition status raggiunge OPERATIONAL
4. Zero crash o errori di parsing
**SHOULD HAVE**:
5. Frame rate = 100 Hz (come GrifoScope)
6. Latenza < 10ms per ciclo
7. Compatibilità backward con modalità legacy
**NICE TO HAVE**:
8. GUI mostra differenze GrifoScope vs PyBusMonitor
9. Tool di diagnostica automatico
10. Documentazione completa workflow
---
## 🔗 Riferimenti
### **File Chiave C++**
- `cpp/GrifoScope/GrifoScope/QgExt/qg1553overudp.cpp` - Logica trasmissione
- `cpp/GrifoScope/GrifoSdkEif/dev/Portable/ext/b1553_udp.cpp` - Parser radar-side
- `cpp/GrifoScope/GrifoMCS/GADS/MCS/MCS_G346/g346/g346_a1a2.cpp` - Startup sequence
### **File Chiave Python**
- `pybusmonitor1553/core/dispatcher.py` - Parser UDP1553
- `pybusmonitor1553/core/packet_builder.py` - Builder frame
- `pybusmonitor1553/core/scheduler.py` - Timing messaggi
- `pybusmonitor1553/core/controller.py` - Startup radar
### **Documentazione**
- `doc/ICD-Implementation-Notes.md` - Discrepanze ICD vs C++
- `doc/Technical-Architecture.md` - Architettura sistema
- `.github/copilot-instructions.md` - Regole sviluppo
---
## 🎓 Note per il Team
### **Approccio Incrementale**
1. **Non stravolgere l'architettura esistente** - Aggiungi modalità compatibilità
2. **Mantieni backward compatibility** - Flag `GRIFOSCOPE_MODE` opzionale
3. **Test continuo** - Ogni modifica deve passare pytest + test manuale
4. **Cattura prima, modifica dopo** - PCAP è la fonte di verità
### **Debug Tips**
```bash
# Abilita logging verbose
export PYBM_LOG_LEVEL=DEBUG
# Cattura pacchetti in tempo reale
python tools/network_sniffer.py --source python --debug
# Confronta con GrifoScope
diff captured_a_messages_commands_grifoscope.txt \
captured_a_messages_commands_python.txt
```
### **Quando Chiamare Aiuto**
- Radar non risponde dopo 3 cicli di debug
- Crash sistematico su parse multi-message
- Performance < 25 Hz su hardware target
- Discrepanze ICD non documentate nel C++
---
## ✨ Conclusioni
**Status Attuale**: PyBusMonitor ha ~80% delle funzionalità necessarie.
**Gap Principali**:
1. Frame multi-messaggio (vs singoli datagram)
2. B-requests con payload vuoto
3. Porte UDP diverse
**Effort Stimato**:
- Fase 1-2: 1 settimana (diagnostica **85%** delle funzionalità necessarie.
**Gap Principali** (solo 2!):
1. Frame multi-messaggio (vs singoli datagram)
2. B-requests con payload vuoto
**Già Corretti** :
- Porte UDP (61553/51553 come GrifoScope)
- Endianness (Big Endian payload, Little Endian header)
- Startup sequence (STANDBY/SILENCE)
- Field descriptors (MSB=0 bit numbering)
**Effort Stimato** (rivisto):
- Fase 1-2: 1 settimana (diagnostica + B-request fix)
- Fase 3-4: 1.5 settimane (multi-message + dispatcher)
- Fase 5-6: 0.5 settimane (test radar + validazione)
**Total**: ~**3 settimane** per compatibilità completa
**Priorità**:
1. **CRITICA** - B-request format (blocca risposte radar)
2. **ALTA** - Multi-message frame (timing/formato)
3. **MEDIA** - Dispatcher cleanup (funziona ma migliorabile)
4. **BASSA** - Broadcast IP (test opziona
**Domande?** Apri issue su GitHub o discuti con team lead.
---
*Documento creato: 2025-12-15*
*Versione: 1.0*
*Autore: AI Assistant + Team PyBusMonitor*