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

21 KiB

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())

// 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:

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())

// 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 )

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:

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:

// 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):

// 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:

# 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:

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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