# Capitolo 3: Script Principale GRIFO_M_PBIT.py

[← Architettura Software](02_Architettura_Software_e_Moduli.md) | [Torna all'Indice](00_INDEX.md) | [Prossimo: Workflow Test →](04_Sistema_Test_Workflow_Logica.md)

---

## 3.1 Overview dello Script

**GRIFO_M_PBIT.py** è lo script principale che orchestra l'intero processo di test automatizzato. Con **~2500 righe di codice**, è responsabile di:

1. **Gestione ciclo di vita test**: Power cycling, timing, ripetizioni
2. **Orchestrazione scenari**: Configurazione failure simulati
3. **Monitoring multi-canale**: 1553, seriale, power control
4. **Aggregazione statistiche**: B6/B8/Serial/Target metrics

## 3.2.2 Versione Script e gestione KNOWN_FAILURES

Lo script espone ora una variabile di versione `TEST_VERSION` che viene
inserita in tutti gli output (log, CSV, JSON e PDF). Questa stringa è utile
per tracciare quale versione del codice ha generato un report.

Esempio in codice:

```python
# Script version propagated to outputs
TEST_VERSION = "0.0.1"
```

KNOWN_FAILURES: il comportamento della lista `KNOWN_FAILURES` è stato
esteso rispetto alle versioni precedenti. Ora gli elementi configurati in
questa lista sono usati per classificare come "Known" anche le failure
emerse durante il drill-down di B8. In pratica:

- Se un campo B6 è in fallimento ma il relativo test B8 corrispondente è
    segnato come "known" nella configurazione, quello specifico fallimento
    NON rende la run fallita (viene registrato come `known_fail`).
- Se un campo B8 fallisce e non è classificato come known, viene marcato
    come real failure e la run viene considerata FAILED.

Implementazione helper (semplificata):

```python
def is_known_failure(field_name: str) -> bool:
        """Restituisce True se `field_name` corrisponde a una voce di KNOWN_FAILURES.

        Matching: case-insensitive substring match (config semplice, ma efficace).
        """
        for k in KNOWN_FAILURES:
                if k.lower() in field_name.lower():
                        return True
        return False
```

Questa estensione permette di mantenere la stessa logica operativa (known
failures ignorate per il giudizio di PASS/FAIL) anche quando il dettaglio
diagnostico è ottenuto con il drill-down B8.
5. **Generazione report**: PDF, CSV, JSON output

### 3.1.1 Caratteristiche Chiave

| Caratteristica | Descrizione |
|----------------|-------------|
| **LOC** | ~2500 linee |
| **Funzioni principali** | 15+ funzioni di test |
| **Scenari supportati** | 6 (Random, Recycle, Perfect, TgtGen, ecc.) |
| **Output formati** | PDF, CSV, JSON |
| **Modalità esecuzione** | Hardware (`--target`) / Simulazione (`--simulate`) |
| **GUI integration** | Thread-safe real-time monitoring |

## 3.2 Struttura del File

### 3.2.1 Organizzazione Sezioni

```python
# ============================================================
# SEZIONE 1: HEADER E DOCUMENTAZIONE (righe 1-40)
# ============================================================
"""
Docstring completo con:
- Test flow description
- Configuration options
- Timing constants
- Author & version info
"""

# ============================================================
# SEZIONE 2: IMPORT E DIPENDENZE (righe 41-65)
# ============================================================
import __init__  # Path bootstrap
from leo_grifo_common import *
from test_common_function import *
from leo_grifo_1553 import theGrifo1553
from leo_grifo_test_report import testReport
# ... altri import

# ============================================================
# SEZIONE 3: CONFIGURAZIONE E COSTANTI (righe 66-150)
# ============================================================
NUMBER_OF_REPETITIONS = 10
PBIT_SEC_TIME = 182
COMM_LOSS_THRESHOLD = 20
KNOWN_FAILURES = [...]
TARGET_DETECTION_TIMEOUT_SEC = 20.0
# ... altre configurazioni

# ============================================================
# SEZIONE 4: SCENARI DI TEST (righe 151-300)
# ============================================================
SCENARIOS = {
    'random': {...},
    'recycle': {...},
    'perfect': {...},
    # ... altri scenari
}

# ============================================================
# SEZIONE 5: FUNZIONI HELPER (righe 301-700)
# ============================================================
def radar_setup():
    """Configura messaggi 1553 iniziali"""
    
def enter_pbit():
    """Invia comando PBIT al radar"""
    
def wait_pbit_completion():
    """Attende completamento PBIT con timeout"""
    
def check_comm_loss():
    """Verifica perdita comunicazione 1553"""

# ============================================================
# SEZIONE 6: TEST B6/B8 (righe 701-1500)
# ============================================================
def verify_b6_status():
    """Verifica 12 campi B6 LRU status"""
    
def drill_down_b8():
    """Drill-down 185 campi diagnostici B8"""

# ============================================================
# SEZIONE 7: TEST TARGET (righe 1501-1900)
# ============================================================
def tgt_gen(scenario_config):
    """Test rilevamento target (opzionale)"""

# ============================================================
# SEZIONE 8: MAIN TEST LOOP (righe 1901-2400)
# ============================================================
def main_test():
    """Loop principale test con power cycling"""
    for repetition in range(NUMBER_OF_REPETITIONS):
        # Power cycle
        # Execute PBIT
        # Verify B6/B8
        # Check serial
        # Target test (if enabled)
        # Update GUI
        # Aggregate statistics

# ============================================================
# SEZIONE 9: REPORT E STATISTICHE (righe 2401-2494)
# ============================================================
def generate_final_statistics_report():
    """Aggrega statistiche e genera report finale"""
    
def export_statistics_to_csv():
    """Esporta statistiche in CSV"""

if __name__ == '__main__':
    main_test()
```

## 3.3 Flusso di Esecuzione Principale

### 3.3.1 Diagramma di Flusso Completo

```
┌─────────────────────────────────────────────────────┐
│                  START PROGRAM                       │
└────────────┬────────────────────────────────────────┘
             │
             ↓
┌─────────────────────────────────────────────────────┐
│  Parse Command Line Arguments                        │
│  - --simulate / --target                             │
│  - --runs N                                          │
│  - --scenario <name>                                 │
└────────────┬────────────────────────────────────────┘
             │
             ↓
┌─────────────────────────────────────────────────────┐
│  Load Scenario Configuration                         │
│  - Select from SCENARIOS dict                        │
│  - Apply mock replacements if --simulate             │
└────────────┬────────────────────────────────────────┘
             │
             ↓
┌─────────────────────────────────────────────────────┐
│  Initialize Components                               │
│  - testReport (PDF generator)                        │
│  - gui_monitor (if not disabled)                     │
│  - Serial terminal connection                        │
│  - Statistics collectors                             │
└────────────┬────────────────────────────────────────┘
             │
             ↓
┌─────────────────────────────────────────────────────┐
│         MAIN TEST LOOP                               │
│  for repetition in range(NUMBER_OF_REPETITIONS):    │
└────────────┬────────────────────────────────────────┘
             │
             ↓
     ┌───────────────┐
     │ Run N/TOTAL   │
     └───────┬───────┘
             │
             ↓
┌─────────────────────────────────────────────────────┐
│  PHASE 1: POWER CYCLING                              │
│  ├─ power_grifo_off(wait_before=1s, wait_after=4s)  │
│  ├─ power_grifo_on(wait_after=1.5s)                 │
│  └─ Wait radar initialization                        │
└────────────┬────────────────────────────────────────┘
             │
             ↓
┌─────────────────────────────────────────────────────┐
│  PHASE 2: RADAR SETUP                                │
│  ├─ radar_setup()                                    │
│  │   ├─ Set A1 intensity = 127                      │
│  │   ├─ Set A2 mode = PBIT                          │
│  │   ├─ Set A4 nav data (heading, position)         │
│  │   └─ Set A5 INU mode word                        │
│  └─ Enable B6/B8/B9 notifications                    │
└────────────┬────────────────────────────────────────┘
             │
             ↓
┌─────────────────────────────────────────────────────┐
│  PHASE 3: PBIT EXECUTION                             │
│  ├─ enter_pbit() → Send PBIT command                │
│  ├─ wait_pbit_completion(timeout=182s)              │
│  │   ├─ Poll B6 message                             │
│  │   ├─ Check bit_available flag                    │
│  │   └─ Monitor comm_loss                           │
│  └─ Record PBIT time                                 │
└────────────┬────────────────────────────────────────┘
             │
             ↓
┌─────────────────────────────────────────────────────┐
│  PHASE 4: B6 VERIFICATION                            │
│  ├─ verify_b6_status()                               │
│  │   ├─ Check 12 LRU status fields                  │
│  │   ├─ Classify: PASS / FAIL / KNOWN               │
│  │   └─ Track: b6_pass, b6_fail, b6_known_fail      │
│  └─ Update statistics                                │
└────────────┬────────────────────────────────────────┘
             │
             ↓
┌─────────────────────────────────────────────────────┐
│  PHASE 5: B8 DRILL-DOWN (conditional)                │
│  ├─ IF real_failures > 0:                            │
│  │   └─ drill_down_b8()                              │
│  │       ├─ Check 185 diagnostic fields              │
│  │       ├─ Track: b8_pass, b8_fail, b8_checked     │
│  │       └─ Record failure details                   │
│  └─ ELSE: Skip B8 (known failures only)             │
└────────────┬────────────────────────────────────────┘
             │
             ↓
┌─────────────────────────────────────────────────────┐
│  PHASE 6: SERIAL MONITORING                          │
│  ├─ Parse serial buffer                              │
│  ├─ Count: errors (%%E), fatal (%%F), recycle       │
│  └─ Update: serial_errors, serial_fatal, recycles    │
└────────────┬────────────────────────────────────────┘
             │
             ↓
┌─────────────────────────────────────────────────────┐
│  PHASE 7: TARGET TEST (optional)                     │
│  ├─ IF scenario has target_gen:                      │
│  │   └─ tgt_gen(scenario_config)                     │
│  │       ├─ Set A2 mode = OPERATE                    │
│  │       ├─ Inject A4/A5 target parameters           │
│  │       ├─ Monitor B9 for target detection          │
│  │       ├─ Verify range within tolerance            │
│  │       └─ Record: target_detected, range, cycle    │
│  └─ Update: target_simulated in run_stats           │
└────────────┬────────────────────────────────────────┘
             │
             ↓
┌─────────────────────────────────────────────────────┐
│  PHASE 8: RUN FINALIZATION                           │
│  ├─ Aggregate run_stats:                             │
│  │   ├─ success (PASS/FAIL)                         │
│  │   ├─ pbit_time                                    │
│  │   ├─ b6_pass/fail/known                          │
│  │   ├─ b8_pass/fail/checked                        │
│  │   ├─ serial_errors/fatal/recycles                │
│  │   ├─ target_detected/range/iterations            │
│  │   └─ failures list                                │
│  ├─ Update GUI (gui_monitor.update_run())            │
│  └─ Append to test_statistics[]                      │
└────────────┬────────────────────────────────────────┘
             │
             ↓
     ┌───────────────┐
     │ More runs?    │
     └───────┬───────┘
             │ Yes
             ↓ (loop)
             │ No
             ↓
┌─────────────────────────────────────────────────────┐
│  FINAL REPORT GENERATION                             │
│  ├─ generate_final_statistics_report()               │
│  │   ├─ Aggregate all run_stats                     │
│  │   ├─ Calculate averages/percentages               │
│  │   └─ Format per-run summary table                │
│  ├─ testReport.pdf_generate()                        │
│  │   ├─ Create PDF with tables/charts               │
│  │   └─ Save to pdf_reports/                        │
│  ├─ export_statistics_to_csv()                       │
│  │   └─ Save CSV to pdf_reports/                    │
│  └─ Export JSON statistics                           │
└────────────┬────────────────────────────────────────┘
             │
             ↓
┌─────────────────────────────────────────────────────┐
│  CLEANUP                                             │
│  ├─ power_grifo_off()                                │
│  ├─ terminal.disconnect()                            │
│  ├─ gui_monitor.show_results()                       │
│  └─ EXIT                                             │
└─────────────────────────────────────────────────────┘
```

### 3.3.2 Stati del Test

Durante l'esecuzione, il test attraversa diversi stati:

```python
class TestState(Enum):
    INIT = "Initialization"
    POWER_CYCLING = "Power Cycling"
    RADAR_SETUP = "Radar Setup"
    PBIT_EXECUTION = "PBIT Execution"
    B6_VERIFICATION = "B6 Verification"
    B8_DRILLDOWN = "B8 Drill-Down"
    SERIAL_CHECK = "Serial Check"
    TARGET_TEST = "Target Test"
    RUN_FINALIZATION = "Run Finalization"
    REPORT_GENERATION = "Report Generation"
    CLEANUP = "Cleanup"
```

## 3.4 Funzioni Principali

### 3.4.1 `main_test()` - Entry Point

```python
def main_test():
    """
    Main test orchestration function.
    
    Workflow:
        1. Parse command line arguments
        2. Load scenario configuration
        3. Initialize components (report, GUI, serial)
        4. Execute test loop (N repetitions)
        5. Generate final report (PDF/CSV/JSON)
        6. Cleanup and exit
    
    Returns:
        bool: True if all runs passed, False otherwise
    """
```

**Responsabilità**:
- Parsing argomenti CLI (`--simulate`, `--runs`, `--scenario`)
- Inizializzazione singleton (testReport, gui_monitor)
- Gestione eccezioni globali e interrupt (Ctrl-C)
- Coordinamento fasi test

**Gestione Interrupt**:
```python
def signal_handler(sig, frame):
    """Handle Ctrl-C gracefully"""
    global interruptRequest
    interruptRequest = True
    logging.warning("Interrupt signal received - will stop after current run")

signal.signal(signal.SIGINT, signal_handler)
```

### 3.4.2 `radar_setup()` - Configurazione 1553

```python
def radar_setup():
    """
    Configure radar via 1553 messages before PBIT.
    
    Sets:
        - A1: Intensity = 127 (maximum)
        - A2: Mode = STANDBY initially
        - A4: Navigation data (heading, position)
        - A5: INU mode word
    
    Returns:
        bool: True if setup successful
    """
```

**Messaggi Configurati**:
| Messaggio | Campo | Valore | Scopo |
|-----------|-------|--------|-------|
| A1 | `a1_display_intensity` | 127 | Intensità display massima |
| A2 | `a2_radarMode` | 1 (STBY) | Modalità iniziale STANDBY |
| A4 | `a4_heading` | 1060 | Heading corrente (ICD units) |
| A4 | `a4_x_pos`, `a4_y_pos` | 2500, 2500 | Posizione piattaforma |
| A5 | `a5_inu_mode_word` | 0xAA54 | INU mode word valido |

### 3.4.3 `enter_pbit()` - Comando PBIT

```python
def enter_pbit():
    """
    Send PBIT command to radar via A2 message.
    
    Sequence:
        1. Set a2_radarMode = 2 (OPERATE)
        2. Set a2_radarModeInitiation = 1 (PBIT)
        3. Wait 100ms for radar to register command
    
    Returns:
        bool: True if command sent successfully
    """
```

**Implementazione**:
```python
# Set OPERATE mode
setValue(theGrifo1553, 2, "A2_MsgRdrOperationCommand", 
         "a2_radarMode", commitChanges=True)

# Trigger PBIT
setValue(theGrifo1553, 1, "A2_MsgRdrOperationCommand",
         "a2_radarModeInitiation", commitChanges=True)

time.sleep(0.1)  # Allow radar to process command
```

### 3.4.4 `wait_pbit_completion()` - Attesa PBIT

```python
def wait_pbit_completion(timeout_sec: float = PBIT_SEC_TIME):
    """
    Wait for PBIT to complete with timeout.
    
    Monitors:
        - B6 bit_available flag (True when PBIT done)
        - 1553 message counter (comm loss detection)
    
    Args:
        timeout_sec: Maximum wait time (default 182s)
    
    Returns:
        Tuple[bool, float, str]:
            - success: True if PBIT completed
            - elapsed_time: Time taken for PBIT
            - status: 'completed' / 'timeout' / 'comm_loss'
    """
```

**Logica Polling**:
```python
start_time = time.time()
last_msg_count = 0
no_change_iterations = 0

while (time.time() - start_time) < timeout_sec:
    # Check bit_available flag
    bit_available = getValue(theGrifo1553, "B6_MsgRdrSettingsAndParametersTellback",
                             "b6_t1_bit_available")
    
    if bit_available == "true":
        elapsed = time.time() - start_time
        return (True, elapsed, 'completed')
    
    # Check comm loss (message counter not incrementing)
    current_msg_count = theGrifo1553.get_message_count("B6")
    if current_msg_count == last_msg_count:
        no_change_iterations += 1
        if no_change_iterations >= COMM_LOSS_THRESHOLD:
            return (False, time.time() - start_time, 'comm_loss')
    else:
        no_change_iterations = 0
        last_msg_count = current_msg_count
    
    time.sleep(0.1)  # Poll every 100ms

# Timeout
return (False, timeout_sec, 'timeout')
```

### 3.4.5 `verify_b6_status()` - Verifica B6

```python
def verify_b6_status(known_failures: List[str]):
    """
    Verify all 12 B6 LRU status fields.
    
    Fields Checked:
        - b6_t1_rdr_fail_status
        - b6_t1_processor_1_status
        - b6_t1_processor_2_status
        - b6_t1_receiver_status
        - b6_t1_exciter_status
        - b6_t1_transmitter_status
        - b6_t1_antenna_drive_unit_status
        - b6_t1_power_supply_status
        - b6_t1_auxilliary_processor_status
        - b6_t1_waveguide_pressurization_status
        - b6_t1_auxilliary_siu_status
        - b6_t1_receiver_transmitter_status
    
    Args:
        known_failures: List of expected failures (HW setup limitations)
    
    Returns:
        Dict:
            {
                'b6_pass': int,
                'b6_fail': int,
                'b6_known_fail': int,
                'failures': List[Tuple[str, str]]  # (field, value)
            }
    """
```

**Classificazione Failure**:
```python
for field in B6_FIELDS:
    ret, err = check(theGrifo1553, "false", "B6_MsgRdrSettingsAndParametersTellback", field)
    
    if ret:  # Field = "false" (no failure)
        b6_pass += 1
    else:    # Field = "true" (failure detected)
        if field in known_failures:
            b6_known_fail += 1  # Expected failure
        else:
            b6_fail += 1  # Unexpected failure
            failures.append((field, "true"))
```

### 3.4.6 `drill_down_b8()` - Drill-Down B8

```python
def drill_down_b8():
    """
    Perform detailed B8 diagnostic drill-down.
    
    Checks 185 diagnostic fields for detailed failure analysis.
    Only executed when real (non-known) failures detected.
    
    Returns:
        Dict:
            {
                'b8_pass': int,
                'b8_fail': int,
                'b8_checked': int,
                'b8_failures': List[Tuple[str, str]]
            }
    """
```

**Performance Note**: B8 drill-down può richiedere 5-10 secondi per 185 field checks.

### 3.4.7 `tgt_gen()` - Test Target Generation

```python
def tgt_gen(scenario_config: Dict) -> Dict:
    """
    Execute target detection test (optional).
    
    Workflow:
        1. Set A2 mode = OPERATE
        2. Configure A4/A5 for target scenario:
           - a4_heading: Target bearing
           - a4_x_pos, a4_y_pos: Platform position
           - a5_inu_mode_word: INU valid
        3. Monitor B9 message for target reports
        4. Verify range within tolerance
        5. Count successful detections
    
    Args:
        scenario_config: Scenario dict with target_gen configuration
    
    Returns:
        Dict:
            {
                'target_detected': bool,
                'target_range': int (meters),
                'target_iterations': int (cycle found),
                'target_test_time': float (seconds)
            }
    """
```

**Esempio Configurazione Target**:
```python
scenario_config = {
    'target_gen': {
        'distance': 2000,  # meters
        'appeared_after_cycles': 3,  # Visible dopo 3 cicli B9
        'heading': 1060,
        'x_pos': 2500,
        'y_pos': 2500
    }
}
```

### 3.4.8 `generate_final_statistics_report()` - Report Finale

```python
def generate_final_statistics_report(report, test_statistics: List[Dict]):
    """
    Aggregate statistics from all runs and format for report.
    
    Calculates:
        - Total runs: N
        - Pass rate: X% (runs with success=True)
        - Fail rate: Y%
        - Average PBIT time: Xs
        - B6/B8/Serial aggregates
        - Per-run summary table
    
    Args:
        report: TestReport instance
        test_statistics: List of run_stats dicts
    
    Returns:
        Dict: Formatted statistics for PDF/CSV/JSON export
    """
```

## 3.5 Gestione Scenari

### 3.5.1 Struttura Scenario

Ogni scenario definisce:
- **Nome e descrizione**
- **Failure da iniettare** (per simulazione)
- **Configurazione target** (opzionale)

```python
SCENARIOS = {
    'random': {
        'name': 'Random Component Failures',
        'description': 'Random selection of component failures (30% proc, 20% rx, 15% rx)',
        'expected_failures': [
            'processor_status',
            'transmitter_status',
            'receiver_status'
        ],
        'failure_probability': {
            'processor_status': 0.30,
            'transmitter_status': 0.20,
            'receiver_status': 0.15
        },
        'target_gen': None  # No target test
    },
    
    'tgtgen': {
        'name': 'Target Generation Test',
        'description': 'PBIT + Target detection test at 2000m',
        'expected_failures': [],
        'target_gen': {
            'distance': 2000,
            'appeared_after_cycles': 3,
            'heading': 1060,
            'x_pos': 2500,
            'y_pos': 2500
        }
    },
    
    'perfect': {
        'name': 'Perfect Run (No Failures)',
        'description': 'All tests pass - ideal scenario',
        'expected_failures': [],
        'target_gen': None
    }
}
```

### 3.5.2 Selezione Scenario

```python
# Command line
python GRIFO_M_PBIT.py --simulate --scenario random

# In code
selected_scenario = SCENARIOS.get(args.scenario, SCENARIOS['random'])
```

## 3.6 Integrazione GUI

### 3.6.1 Inizializzazione GUI

```python
if not args.no_gui:
    from GRIFO_M_PBIT_gui import TestMonitorGUI
    gui_monitor = TestMonitorGUI()
    gui_monitor.start()  # Lancia GUI su thread separato
else:
    gui_monitor = None
```

### 3.6.2 Aggiornamenti GUI Thread-Safe

```python
# Durante test (worker thread)
gui_monitor.update_status(
    run_current=repetition+1,
    run_total=NUMBER_OF_REPETITIONS,
    pbit_time=pbit_time,
    pbit_available=True,
    power_on=True
)

gui_monitor.update_run(
    run=repetition+1,
    result='PASS' if success else 'FAIL',
    pbit=pbit_time,
    b6_pass=b6_pass,
    b6_fail=b6_fail,
    # ... altri parametri
)

# GUI thread processa queue e aggiorna widgets
```

## 3.7 Output e Naming Convention

### 3.7.1 Formato Nome File

Tutti gli output seguono il pattern:
```
{TEST_ID}_{YYYYMMDD}_{HHMMSS}_{suffix}.{ext}
```

**Esempio**:
```
GRIFO_M_PBIT_20260204_143052_report.pdf
GRIFO_M_PBIT_20260204_143052_statistic.csv
GRIFO_M_PBIT_20260204_143052_statistic.json
```

**Vantaggi**:
- Ordinamento alfabetico = ordinamento cronologico
- ID univoco tramite timestamp con secondi
- Suffix identifica tipo file (`report` / `statistic`)

### 3.7.2 Contenuto Output

**PDF Report**:
- Tabella per-run summary (8 colonne: Run, Status, PBIT Time, B6, B8, Serial, Target, Target Test)
- Statistiche aggregate (totali, medie, percentuali)
- Grafici (opzionale)

**CSV Statistic**:
- Header con nomi colonne
- Riga per ogni run
- Campi separati da virgola

**JSON Statistic**:
- Struttura gerarchica completa
- Include metadata (scenario, timestamp, ecc.)
- Array `runs[]` con dati di ogni run

## 3.8 Error Handling e Robustezza

### 3.8.1 Gestione Eccezioni

```python
try:
    # Test execution
    success = execute_run()
except KeyboardInterrupt:
    # Ctrl-C: graceful shutdown
    logging.warning("Test interrupted by user")
    break
except Exception as e:
    # Unexpected error: log and continue
    logging.error(f"Run {repetition+1} failed with exception: {e}")
    logging.error(traceback.format_exc())
    run_stats['success'] = False
    run_stats['exception'] = str(e)
finally:
    # Always cleanup (power off, disconnect serial)
    cleanup()
```

### 3.8.2 Timeout Protection

Tutti i wait hanno timeout:
```python
# PBIT completion
success, time, status = wait_pbit_completion(timeout_sec=182)

# Target detection
target_found = wait_target_detection(timeout_sec=20)

# Serial read
line = serial_port.readline(timeout=1.0)
```

### 3.8.3 Comm Loss Detection

```python
# Rileva perdita comunicazione 1553 (radar spento/crashed)
if no_new_messages_for(iterations=20):
    logging.error("1553 comm loss detected - aborting run")
    return False
```

---

[← Architettura Software](02_Architettura_Software_e_Moduli.md) | [Torna all'Indice](00_INDEX.md) | [Prossimo: Workflow Test →](04_Sistema_Test_Workflow_Logica.md)
