# Capitolo 8: Configurazione e Personalizzazione

[← Output e Report](07_Output_Report.md) | [Torna all'Indice](00_INDEX.md) | [Prossimo: Integrazione Hardware →](09_Integrazione_Hardware.md)

---

## 8.1 Overview Configurazione

Il sistema è progettato per essere **altamente configurabile** senza modificare il codice core. La configurazione avviene tramite:

| Meccanismo | File/Variabile | Scopo |
|------------|---------------|-------|
| **CLI Arguments** | `--target`, `--simulate`, `--runs` | Modalità esecuzione e parametri base |
| **SCENARIOS Dict** | `GRIFO_M_PBIT.py` (riga ~130-250) | Definizione scenari test e failure injection |
| **KNOWN_FAILURES List** | `GRIFO_M_PBIT.py` (riga ~115-125) | Failure attesi da ignorare |
| **Constants** | Top of modules | Timeout, retry, threshold |
| **Mock Config** | `GRIFO_M_PBIT_mock.py` | Comportamento simulazione |

## 8.2 Parametri CLI

### 8.2.1 Sintassi Completa

```bash
python GRIFO_M_PBIT.py [--target | --simulate] [--runs N] [--gui] [--no-gui] [--fast-mode]
```

### 8.2.2 Parametri Disponibili

| Parametro | Tipo | Default | Descrizione |
|-----------|------|---------|-------------|
| `--target` | Flag | False | Esegue su hardware reale (MIL-STD-1553) |
| `--simulate` | Flag | False | Esegue in simulation mode (mock objects) |
| `--runs` | Integer | 10 | Numero di cicli test da eseguire |
| `--gui` | Flag | True | Mostra GUI real-time |
| `--no-gui` | Flag | False | Disabilita GUI (headless) |
| `--fast-mode` | Flag | False | Salta verification delays (simulation only) |

**Nota**: `--target` e `--simulate` sono **mutually exclusive**. Se nessuno dei due è specificato, default = `--simulate`.

### 8.2.3 Esempi Uso CLI

```bash
# Simulation standard (10 runs, GUI enabled)
python GRIFO_M_PBIT.py --simulate

# Simulation veloce (skip delays)
python GRIFO_M_PBIT.py --simulate --fast-mode

# Simulation 50 runs senza GUI (per CI/CD)
python GRIFO_M_PBIT.py --simulate --runs 50 --no-gui

# Hardware test (3 runs, GUI enabled)
python GRIFO_M_PBIT.py --target --runs 3

# Hardware test senza GUI (automation)
python GRIFO_M_PBIT.py --target --runs 1 --no-gui
```

### 8.2.4 Parsing Argomenti

```python
def parse_arguments():
    """Parse CLI arguments for test configuration"""
    import argparse
    
    parser = argparse.ArgumentParser(
        description='GRIFO M-PBIT Automated Test',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  python GRIFO_M_PBIT.py --simulate                # Simulation mode
  python GRIFO_M_PBIT.py --target --runs 5         # Hardware 5 runs
  python GRIFO_M_PBIT.py --simulate --no-gui       # Headless simulation
        """
    )
    
    # Execution mode (mutually exclusive)
    mode_group = parser.add_mutually_exclusive_group()
    mode_group.add_argument('--target', action='store_true',
                           help='Run on hardware (MIL-STD-1553)')
    mode_group.add_argument('--simulate', action='store_true',
                           help='Run in simulation mode (default)')
    
    # Test parameters
    parser.add_argument('--runs', type=int, default=10,
                       help='Number of test cycles (default: 10)')
    
    # GUI control
    gui_group = parser.add_mutually_exclusive_group()
    gui_group.add_argument('--gui', action='store_true', default=True,
                          help='Enable GUI (default)')
    gui_group.add_argument('--no-gui', action='store_true',
                          help='Disable GUI (headless mode)')
    
    # Performance
    parser.add_argument('--fast-mode', action='store_true',
                       help='Skip verification delays (simulation only)')
    
    args = parser.parse_args()
    
    # Set defaults
    if not args.target and not args.simulate:
        args.simulate = True  # Default to simulation
    
    if args.no_gui:
        args.gui = False
    
    return args
```

## 8.3 Sistema Scenari

### 8.3.1 Concetto Scenari

Gli **scenari** definiscono **configurazioni di failure injection** per testare comportamenti diversi:

- **Scenario 1**: Baseline (tutto nominale)
- **Scenario 2-5**: Failure specifici (processor, transmitter, antenna, ecc.)
- **Scenario 6**: Random failures (30% probability)

**Rotazione**: Test cicla attraverso scenari in ordine sequenziale:
```
Run 1  → Scenario 1
Run 2  → Scenario 1
Run 3  → Scenario 2
Run 4  → Scenario 3
Run 5  → Scenario 4
Run 6  → Scenario 5
Run 7  → Scenario 6
Run 8  → Scenario 1  (ricomincia)
...
```

### 8.3.2 Definizione Scenari (SCENARIOS Dict)

```python
# File: GRIFO_M_PBIT.py (~line 130)

SCENARIOS = {
    1: {
        'name': 'Scenario 1: All Systems Nominal',
        'description': 'Baseline test with all components functioning correctly',
        'b6_overrides': {},  # No failures injected
        'target_config': {
            'enabled': True,
            'distance': 2000,  # meters
            'appear_after_cycles': 3
        }
    },
    
    2: {
        'name': 'Scenario 2: Processor Failure',
        'description': 'Simulate processor subsystem failure',
        'b6_overrides': {
            'processor_status': 0  # Force fail
        },
        'target_config': {
            'enabled': False  # No target test if critical failure
        }
    },
    
    3: {
        'name': 'Scenario 3: Transmitter Failure',
        'description': 'Simulate transmitter offline',
        'b6_overrides': {
            'transmitter_status': 0
        },
        'target_config': {
            'enabled': False
        }
    },
    
    4: {
        'name': 'Scenario 4: Antenna Servo Failure',
        'description': 'Simulate antenna positioning failure',
        'b6_overrides': {
            'antenna_servo_status': 0
        },
        'target_config': {
            'enabled': True,  # Antenna failure doesn't prevent target test
            'distance': 1500,
            'appear_after_cycles': 5
        }
    },
    
    5: {
        'name': 'Scenario 5: Multiple Component Failures',
        'description': 'Combine processor + transmitter failures',
        'b6_overrides': {
            'processor_status': 0,
            'transmitter_status': 0,
            'cooling_status': 0
        },
        'target_config': {
            'enabled': False
        }
    },
    
    6: {
        'name': 'Scenario 6: Random Component Failures',
        'description': 'Random selection of failures (30% probability each)',
        'b6_overrides': 'random',  # Special keyword
        'random_failure_probability': 0.30,
        'target_config': {
            'enabled': True,
            'distance': 'random',  # 1000-3000m
            'appear_after_cycles': 'random'  # 2-8 cycles
        }
    }
}
```

### 8.3.3 Applicazione Scenario

```python
def apply_scenario(run_number: int) -> Dict:
    """
    Select and apply scenario based on run number.
    
    Args:
        run_number: Current test run (1-based)
    
    Returns:
        Scenario configuration dictionary
    """
    import random
    
    # Rotate through scenarios
    num_scenarios = len(SCENARIOS)
    scenario_id = ((run_number - 1) % num_scenarios) + 1
    
    scenario = SCENARIOS[scenario_id].copy()
    
    # Handle random scenario
    if scenario.get('b6_overrides') == 'random':
        b6_fields = [
            'processor_status', 'transmitter_status', 'receiver_status',
            'antenna_servo_status', 'cooling_status', 'power_supply_status'
        ]
        
        overrides = {}
        prob = scenario.get('random_failure_probability', 0.3)
        
        for field in b6_fields:
            if random.random() < prob:
                overrides[field] = 0  # Inject failure
        
        scenario['b6_overrides'] = overrides
    
    # Handle random target config
    target_cfg = scenario.get('target_config', {})
    if target_cfg.get('distance') == 'random':
        target_cfg['distance'] = random.randint(1000, 3000)
    if target_cfg.get('appear_after_cycles') == 'random':
        target_cfg['appear_after_cycles'] = random.randint(2, 8)
    
    logging.info(f"Run {run_number}: {scenario['name']}")
    if scenario['b6_overrides']:
        logging.info(f"  B6 overrides: {scenario['b6_overrides']}")
    
    return scenario


def configure_mock_with_scenario(scenario: Dict):
    """
    Apply scenario configuration to mock objects.
    Only works in simulation mode.
    """
    if not hasattr(theGrifo1553, 'set_b6_overrides'):
        logging.warning("Not in simulation mode, scenario config ignored")
        return
    
    # Apply B6 overrides to mock
    b6_overrides = scenario.get('b6_overrides', {})
    theGrifo1553.set_b6_overrides(b6_overrides)
    
    # Apply target configuration
    target_cfg = scenario.get('target_config', {})
    if hasattr(theGrifo1553, 'configure_target'):
        theGrifo1553.configure_target(target_cfg)
    
    logging.debug(f"Mock configured with scenario: {scenario['name']}")
```

## 8.4 Known Failures List

### 8.4.1 Concetto Known Failures

Alcuni failure sono **attesi e accettabili** (es. ICD mismatch, known bugs). Il sistema li traccia separatamente e **non causano FAIL del run**.

**Esempio**: Campo `bit_test_2_status` sempre fallisce per bug noto nel firmware → aggiungi a `KNOWN_FAILURES`.

### 8.4.2 Definizione Known Failures

```python
# File: GRIFO_M_PBIT.py (~line 115)

KNOWN_FAILURES = [
    'bit_test_2_status',  # Known ICD mismatch: always returns 0
    # Add more fields here as needed
]
```

### 8.4.3 Check con Known Failures

```python
def check_b6_field(field_name: str, expected: int, actual: int) -> str:
    """
    Check B6 field with known failures handling.
    
    Returns:
        'PASS', 'FAIL', or 'KNOWN_FAIL'
    """
    if actual == expected:
        return 'PASS'
    
    # Check if in known failures list
    if field_name in KNOWN_FAILURES:
        logging.warning(f"B6 {field_name}: Known failure (expected={expected}, actual={actual})")
        return 'KNOWN_FAIL'
    
    # Unexpected failure
    logging.error(f"B6 {field_name}: FAIL (expected={expected}, actual={actual})")
    return 'FAIL'


def evaluate_run_result(b6_pass: int, b6_fail: int, b6_known: int,
                       b8_pass: int, b8_fail: int,
                       serial_fatal: int) -> str:
    """
    Determine final run result.
    Known failures do NOT cause FAIL.
    
    Returns:
        'PASS' or 'FAIL'
    """
    # Serial fatal errors always fail
    if serial_fatal > 0:
        return 'FAIL'
    
    # B6 unexpected failures
    if b6_fail > 0:
        return 'FAIL'
    
    # B8 failures
    if b8_fail > 0:
        return 'FAIL'
    
    # Known failures are OK
    return 'PASS'
```

### 8.4.4 Reporting Known Failures

Nel report PDF/CSV, i known failures sono tracciati separatamente:

**PDF**:
```
Run 1/10 - PASS
├─ B6 Status: 11/0/1 (P/F/K)
│  ├─ 11 PASS
│  ├─ 0 FAIL
│  └─ 1 KNOWN_FAIL: bit_test_2_status
```

**CSV**:
```csv
run_id,result,b6_pass,b6_fail,b6_known_fail
1,PASS,11,0,1
```

## 8.5 Costanti e Timeout

### 8.5.1 Costanti Principali

```python
# File: GRIFO_M_PBIT.py (top of file)

# Test configuration
DEFAULT_NUM_RUNS = 10
MAX_RUNS = 100  # Safety limit

# Timeouts (seconds)
PBIT_TIMEOUT = 120.0  # Max time to wait for PBIT completion
B6_AVAILABLE_TIMEOUT = 10.0  # Max time for BIT data availability
B8_AVAILABLE_TIMEOUT = 10.0
TARGET_TEST_TIMEOUT = 300.0  # 5 minutes for target detection

# Power control
POWER_ON_DELAY = 1.0  # Delay after power ON
POWER_OFF_DELAY = 0.5  # Delay after power OFF

# Serial terminal
SERIAL_BUFFER_SIZE = 4096
SERIAL_TIMEOUT = 0.1  # Read timeout

# Target generation
TARGET_DEFAULT_DISTANCE = 2000  # meters
TARGET_MAX_CYCLES = 20  # Max iterations before timeout
TARGET_CYCLE_DURATION = 6.0  # seconds per radar scan cycle

# Verification delays (can be skipped with --fast-mode)
TELLBACK_VERIFICATION_DELAY = 0.5
STANDBY_VERIFICATION_DELAY = 0.5
```

### 8.5.2 Timeout Configuration Best Practices

**Hardware Mode** (real hardware, slow):
```python
if args.target:
    PBIT_TIMEOUT = 120.0  # Hardware PBIT can take up to 2 minutes
    TARGET_TEST_TIMEOUT = 600.0  # Real radar needs more time
```

**Simulation Mode** (fast, deterministic):
```python
if args.simulate:
    PBIT_TIMEOUT = 10.0  # Mock PBIT completes in ~1s
    TARGET_TEST_TIMEOUT = 30.0  # Mock target appears quickly
```

**Fast Mode** (skip delays):
```python
if args.fast_mode:
    TELLBACK_VERIFICATION_DELAY = 0.0
    STANDBY_VERIFICATION_DELAY = 0.0
    POWER_ON_DELAY = 0.1
```

## 8.6 Configurazione Mock (Simulation)

### 8.6.1 Mock Configuration Constants

```python
# File: GRIFO_M_PBIT_mock.py (top section)

# PBIT timing
MOCK_PBIT_DURATION_MIN = 0.5  # seconds
MOCK_PBIT_DURATION_MAX = 3.0
MOCK_PBIT_AVAILABLE_DELAY = 0.0  # BIT data immediately available

# B6 default values (all nominal)
MOCK_B6_DEFAULTS = {
    'processor_status': 1,
    'transmitter_status': 1,
    'receiver_status': 1,
    'antenna_servo_status': 1,
    'pbit_complete': 1,
    'bit_test_1_status': 1,
    'bit_test_2_status': 0,  # Known failure (always 0)
    'cooling_status': 1,
    'power_supply_status': 1,
    'memory_status': 1,
    'software_version': 123,
    'hardware_revision': 456
}

# B8 configuration
MOCK_B8_TOTAL_FIELDS = 185
MOCK_B8_FAILURE_PROBABILITY = 0.0  # 0% = all pass (can override per scenario)

# Serial terminal messages
MOCK_SERIAL_ERROR_PROBABILITY = 0.05  # 5% chance of %%E per message
MOCK_SERIAL_FATAL_PROBABILITY = 0.01  # 1% chance of %%F
MOCK_SERIAL_MESSAGE_RATE = 2  # Messages per second

# Target simulation
MOCK_TARGET_ALWAYS_DETECT = True  # If False, use probability
MOCK_TARGET_DETECTION_PROBABILITY = 0.9  # 90% success rate
MOCK_TARGET_MIN_CYCLES = 2
MOCK_TARGET_MAX_CYCLES = 8
```

### 8.6.2 Runtime Configuration (durante test)

```python
class MockGrifo1553Interface:
    """Mock 1553 interface with runtime configuration"""
    
    def __init__(self):
        self.b6_values = MOCK_B6_DEFAULTS.copy()
        self.b6_overrides = {}
        self.target_config = {
            'enabled': True,
            'distance': 2000,
            'appear_after_cycles': 3
        }
    
    def set_b6_overrides(self, overrides: Dict[str, int]):
        """
        Override B6 field values (for scenario injection).
        
        Args:
            overrides: Dict of {field_name: value}
        """
        self.b6_overrides = overrides.copy()
        logging.debug(f"Mock B6 overrides set: {overrides}")
    
    def configure_target(self, config: Dict):
        """
        Configure target simulation behavior.
        
        Args:
            config: Dict with keys 'enabled', 'distance', 'appear_after_cycles'
        """
        self.target_config.update(config)
        logging.debug(f"Mock target config: {self.target_config}")
    
    def get(self, address: str) -> int:
        """Get B6 field value (with overrides applied)"""
        field_name = self._address_to_field_name(address)
        
        # Apply override if present
        if field_name in self.b6_overrides:
            return self.b6_overrides[field_name]
        
        # Return default
        return self.b6_values.get(field_name, 0)
```

## 8.7 Personalizzazione Avanzata

### 8.7.1 Aggiungere Nuovo Scenario

**Step 1**: Aggiungi a `SCENARIOS` dict:
```python
SCENARIOS = {
    # ... scenari esistenti ...
    
    7: {
        'name': 'Scenario 7: Partial Receiver Failure',
        'description': 'Receiver degraded but operational',
        'b6_overrides': {
            'receiver_status': 0  # Force receiver fail
        },
        'b8_overrides': {
            # Specific B8 fields affected by receiver failure
            'receiver_sensitivity': 50,  # Degraded (expected 100)
            'signal_quality': 2  # Poor (expected 5)
        },
        'target_config': {
            'enabled': True,
            'distance': 1000,  # Shorter distance due to degraded receiver
            'appear_after_cycles': 10  # Takes longer to detect
        }
    }
}
```

**Step 2**: Se serve, estendi mock per supportare `b8_overrides`:
```python
class MockGrifo1553Interface:
    def __init__(self):
        # ... existing ...
        self.b8_overrides = {}
    
    def set_b8_overrides(self, overrides: Dict[str, int]):
        """Override specific B8 field values"""
        self.b8_overrides = overrides.copy()
```

**Step 3**: Applica scenario nel loop principale:
```python
scenario = apply_scenario(current_run)
configure_mock_with_scenario(scenario)

# Now test will use scenario configuration
```

### 8.7.2 Aggiungere Known Failure

**Step 1**: Identifica campo che fallisce in modo accettabile
```python
# Esempio: nuovo campo B6 "secondary_power_status" ha bug noto
```

**Step 2**: Aggiungi a `KNOWN_FAILURES`:
```python
KNOWN_FAILURES = [
    'bit_test_2_status',
    'secondary_power_status',  # New: always returns incorrect value
]
```

**Step 3**: Verifica report (dovrebbe mostrare "K" per Known):
```
B6 Status: 11/0/2 (P/F/K)
            ↑     ↑
            Pass  Known Failures
```

### 8.7.3 Modificare Timeout per Hardware Lento

Se hardware impiega >2 minuti per PBIT:

```python
# File: GRIFO_M_PBIT.py

# OLD
PBIT_TIMEOUT = 120.0

# NEW
PBIT_TIMEOUT = 300.0  # 5 minutes for slow hardware
```

Oppure, rendilo configurabile via CLI:
```python
parser.add_argument('--pbit-timeout', type=float, default=120.0,
                   help='PBIT completion timeout (seconds)')

args = parse_arguments()
PBIT_TIMEOUT = args.pbit_timeout
```

### 8.7.4 Custom Target Distances

Per testare detection a diverse range:

```python
# Modifica SCENARIOS target_config
SCENARIOS[1]['target_config']['distance'] = 500   # Close range
SCENARIOS[2]['target_config']['distance'] = 3000  # Long range
SCENARIOS[3]['target_config']['distance'] = 1500  # Medium range
```

Oppure, aggiungi lista di distance da testare:
```python
TARGET_TEST_DISTANCES = [500, 1000, 1500, 2000, 2500, 3000]

def get_target_distance_for_run(run_number: int) -> int:
    """Rotate through test distances"""
    idx = (run_number - 1) % len(TARGET_TEST_DISTANCES)
    return TARGET_TEST_DISTANCES[idx]
```

## 8.8 File di Configurazione Esterni (Opzionale)

### 8.8.1 YAML Configuration (Future Enhancement)

Per configurazione più flessibile, considera file YAML:

```yaml
# grifo_test_config.yaml

test:
  default_runs: 10
  enable_gui: true
  fast_mode: false

timeouts:
  pbit: 120.0
  target: 300.0
  b6_available: 10.0

scenarios:
  - id: 1
    name: "All Systems Nominal"
    description: "Baseline test"
    b6_overrides: {}
    target:
      enabled: true
      distance: 2000
      cycles: 3
  
  - id: 2
    name: "Processor Failure"
    description: "Processor offline"
    b6_overrides:
      processor_status: 0
    target:
      enabled: false

known_failures:
  - bit_test_2_status
  - secondary_power_status
```

**Loading YAML**:
```python
import yaml

def load_config(config_file: str = 'grifo_test_config.yaml') -> Dict:
    """Load test configuration from YAML file"""
    with open(config_file, 'r') as f:
        config = yaml.safe_load(f)
    return config

# Usage
config = load_config()
DEFAULT_NUM_RUNS = config['test']['default_runs']
PBIT_TIMEOUT = config['timeouts']['pbit']
SCENARIOS = {s['id']: s for s in config['scenarios']}
KNOWN_FAILURES = config['known_failures']
```

### 8.8.2 Environment Variables

Per deployment automation:

```python
import os

# Override defaults from environment
DEFAULT_NUM_RUNS = int(os.getenv('GRIFO_TEST_RUNS', '10'))
PBIT_TIMEOUT = float(os.getenv('GRIFO_PBIT_TIMEOUT', '120.0'))
ENABLE_GUI = os.getenv('GRIFO_ENABLE_GUI', 'true').lower() == 'true'
```

**Usage**:
```bash
# Windows
set GRIFO_TEST_RUNS=50
set GRIFO_ENABLE_GUI=false
python GRIFO_M_PBIT.py --simulate

# Linux/Mac
export GRIFO_TEST_RUNS=50
export GRIFO_ENABLE_GUI=false
python GRIFO_M_PBIT.py --simulate
```

## 8.9 Checklist Personalizzazione

✅ **Prima di modificare**:
- [ ] Backup del file originale
- [ ] Identifica sezione esatta da modificare
- [ ] Verifica impatto su altri componenti

✅ **Modifiche Comuni** (safe):
- [ ] Aggiungi scenario a `SCENARIOS` dict
- [ ] Aggiungi campo a `KNOWN_FAILURES` list
- [ ] Modifica timeout constants
- [ ] Cambia `DEFAULT_NUM_RUNS`

✅ **Modifiche Avanzate** (test required):
- [ ] Nuovi parametri CLI
- [ ] Custom B8 check logic
- [ ] Nuovi campi B6/B8 (richiede ICD update)
- [ ] Modifiche a mock behavior

✅ **Dopo modifiche**:
- [ ] Test in simulation mode
- [ ] Verifica report generati correttamente
- [ ] Test con diversi scenari
- [ ] Documenta modifiche

---

[← Output e Report](07_Output_Report.md) | [Torna all'Indice](00_INDEX.md) | [Prossimo: Integrazione Hardware →](09_Integrazione_Hardware.md)
