# 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(1, 0); // A1 addMsg(2, 0); // A2 addMsg(3, 0); // A3 addMsg(4, 0); // A4 addMsg(5, 0); // A5 // Registra messaggi B (RTβ†’BC, tr=1): addMsg(15, 1); // B6 addMsg(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(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('