SXXXXXXX_PyMsc/doc/ARTOS_Architecture_Decisions.md

24 KiB

ARTOS: Architecture Decision Record (ADR)

Living Document - Decisioni Architetturali e Rationale

Data inizio: 09 Gennaio 2026
Ultima modifica: 09 Gennaio 2026
Versione: 0.1.0


📋 Indice

  1. Panoramica e Obiettivi
  2. Decisione 1: Architettura a Layer Separati
  3. Decisione 2: GUI Modulare con Docking System
  4. Decisione 3: Dual-Mode Test System
  5. Piano di Implementazione
  6. Domande Aperte e Decisioni Future

Panoramica e Obiettivi

Problema Iniziale

Il sistema PyMSC attuale presenta un'architettura monolitica dove:

  • La GUI è il punto centrale di controllo
  • La logica di test è accoppiata alla GUI (script_manager basato su WIDGET_MAP)
  • Non esiste una chiara separazione tra hardware abstraction, orchestrazione e test
  • Aggiungere nuovi moduli (video, target simulator) è complesso

Obiettivi Strategici

  1. Modularità: Ogni componente HW ha il suo modulo indipendente
  2. Testabilità: Test automatici eseguibili sia da GUI che da CLI
  3. Estensibilità: Facile aggiunta di nuovi moduli e algoritmi
  4. Usabilità: GUI moderna con workspace personalizzabili
  5. Dual-mode: Supportare sia test programmatici (Python) che registrazioni GUI

Decisione 1: Architettura a Layer Separati

Contesto

Ispirandoci a ARTOS Technical Design Specification, vogliamo separare chiaramente i livelli funzionali.

Decisione

Adottiamo un'architettura a 4 livelli:

┌─────────────────────────────────────────────────────────────┐
│  L3: Test Execution Layer                                   │
│  ├── Python Test Scripts (programmatic)                     │
│  └── GUI-Recorded Sequences (user-generated)                │
├─────────────────────────────────────────────────────────────┤
│  L2: Dynamic Algorithm Library                              │
│  ├── Target Validation                                      │
│  ├── Video Analysis                                         │
│  └── Timing Analysis                                        │
├─────────────────────────────────────────────────────────────┤
│  L1: Orchestrator Core (TestContext + Module Manager)       │
│  ├── TestContext: Unified API                               │
│  ├── Module Lifecycle Management                            │
│  └── Event Bus (pub/sub)                                    │
├─────────────────────────────────────────────────────────────┤
│  L0: Hardware Abstraction Layer                             │
│  ├── Bus1553Module                                          │
│  ├── VideoModule (SFP frame capture)                        │
│  └── TargetSimModule                                        │
└─────────────────────────────────────────────────────────────┘

Rationale

  • Separazione delle responsabilità: Ogni layer ha uno scopo chiaro
  • TestContext come pivot: Fornisce API unificata per accesso ai moduli
  • Indipendenza test da GUI: Test possono girare headless via CLI
  • Algoritmi riutilizzabili: Logic di validazione centralizzata in L2

Impatto

  • Facilita testing automatico
  • Permette esecuzione batch senza GUI
  • Semplifica aggiunta di nuovi moduli hardware
  • ⚠️ Richiede refactoring significativo del codice esistente

Decisione 2: GUI Modulare con Docking System

Contesto

La GUI attuale (main_window.py) è un TabWidget monolitico con tutte le pagine hardcoded. Aggiungere nuovi pannelli è invasivo e non c'è flessibilità nel layout.

Decisione

Implementare un sistema di GUI modulare basato su Docking Widgets:

Architettura GUI Proposta

┌──────────────────────────────────────────────────────────────────┐
│  MainWindow (Docking Container)                                  │
│  ┌────────────────┬──────────────────┬──────────────────────┐   │
│  │  MCS Control   │  MFD Display     │  Test Control Panel   │   │
│  │  Dock          │  Dock            │  Dock                 │   │
│  │                │                  │                       │   │
│  │  [1553 Msgs]   │  [Video Frame]   │  [Run Tests]          │   │
│  │  [Commands]    │  [Overlays]      │  [Record Sequence]    │   │
│  │                │                  │  [Test Library]       │   │
│  ├────────────────┴──────────────────┴──────────────────────┤   │
│  │  SAR Image Display Dock                                   │   │
│  │  [SAR Map] [Controls] [Analysis Tools]                    │   │
│  ├───────────────────────────────────────────────────────────┤   │
│  │  Target Simulator Dock          │  Recording Dock         │   │
│  │  [Inject Target] [Scenarios]    │  [Actions] [Playback]   │   │
│  └─────────────────────────────────┴─────────────────────────┘   │
└──────────────────────────────────────────────────────────────────┘

Caratteristiche dei Dock

  1. Floating e Ridimensionabili: Ogni dock può essere spostato/ridimensionato
  2. Ownership Modulare: Ogni modulo HW controlla il proprio dock
  3. Layout Salvabile: L'utente può salvare/caricare configurazioni workspace
  4. Hot-plug: Dock possono essere aggiunti/rimossi a runtime

Struttura Implementativa

artos/
└── gui/
    ├── main_docking_window.py    # ★ Container principale (QMainWindow/tkinter.dnd)
    ├── dock_registry.py           # Registro dock disponibili
    ├── workspace_manager.py       # Save/Load layout utente
    └── docks/
        ├── base_dock.py           # Classe base per tutti i dock
        ├── mcs_control_dock.py    # Pannello MCS (ex main_window tabs)
        ├── mfd_display_dock.py    # Video MFD real-time
        ├── sar_display_dock.py    # SAR image + analysis
        ├── test_control_dock.py   # Test execution panel
        ├── recorder_dock.py       # GUI recording controls
        └── target_sim_dock.py     # Target injection controls

Implementazione Tecnica

Opzione A: Tkinter + ttkbootstrap + Custom Docking SCELTA

Pro:

  • Già usato nel progetto - zero curva apprendimento
  • Stdlib Python - zero dipendenze esterne critiche
  • Libreria ttkbootstrap per temi moderni
  • PanedWindow nativo per split dinamici
  • Performance adeguata per 20-30 FPS video
  • Nessun licensing concern
  • Distribuzione semplificata (PyInstaller friendly)

Contro:

  • ⚠️ Docking system custom (richiede implementazione)
  • ⚠️ Look & feel meno "native" (mitigabile con ttkbootstrap)

Opzione B: PyQt6 con QDockWidget (Scartata)

Pro:

  • Sistema di docking nativo robusto
  • Performance eccellenti per video
  • Integrazione OpenGL per SAR/MFD

Contro:

  • Dipendenza esterna pesante (~50MB)
  • Licensing complesso (GPL o commerciale costoso)
  • Curva apprendimento ripida
  • Debugging più complesso (errori C++)
  • Potenziali problemi compatibilità OS/Python
  • Overengineering per le nostre esigenze

Decisione Finale: Tkinter + Custom Docking System

Data decisione: 09 Gennaio 2026

Motivazione:

  1. Affidabilità: Tkinter è stdlib, sempre disponibile, zero rischi dipendenze
  2. Preservazione codebase: Tutto il lavoro fatto rimane valido
  3. Pragmatismo: Performance sufficienti per i nostri requisiti (30 FPS max)
  4. Manutenibilità: Team già esperto in Tkinter
  5. Distribuzione: Nessun licensing concern, build più semplici

Implementazione:

  • DockFrame: Classe base custom per pannelli dockabili
  • WorkspaceManager: Gestione layout con tk.PanedWindow
  • ttkbootstrap: Temi moderni e widget styled
  • PIL/Pillow: Video frame display (già in uso)

Rationale Architetturale

  • Modularità: Ogni modulo registra il proprio dock all'avvio
  • Flessibilità utente: Layout personalizzabili per diversi scenari di test
  • Scalabilità: Facile aggiunta di nuovi pannelli (es. future telemetry dock)

Impatto

  • GUI più professionale e moderna
  • Ogni sviluppatore può lavorare su un dock indipendente
  • Utenti possono personalizzare workspace per task specifici
  • Zero dipendenze esterne aggiuntive
  • ⚠️ Richiede implementazione custom docking (DockFrame, WorkspaceManager)

Decisione 3: Dual-Mode Test System

Contesto

Vogliamo supportare DUE tipi di test:

  1. Test Programmatici (Python): Script scritti a mano, versionabili, ripetibili
  2. Test Registrati (GUI): Sequenze catturate dall'utente, rapide da creare

Decisione

Implementare un sistema ibrido dove entrambi i modi coesistono:

Architettura Test System

┌────────────────────────────────────────────────────────────────┐
│  Test Execution Engine (L3)                                     │
│  ┌──────────────────────────┬─────────────────────────────┐   │
│  │  Python Test Runner      │  GUI Sequence Player        │   │
│  │  ├── test_loader.py      │  ├── sequence_loader.py     │   │
│  │  ├── test_executor.py    │  ├── sequence_executor.py   │   │
│  │  └── assertion_lib.py    │  └── action_replayer.py     │   │
│  └──────────────────────────┴─────────────────────────────┘   │
│                              ▼                                  │
│            Unified TestContext Interface                        │
│  ┌─────────────────────────────────────────────────────────┐  │
│  │  context.send_command()                                  │  │
│  │  context.get_message()                                   │  │
│  │  context.run_algorithm()                                 │  │
│  │  context.wait_for_condition()                            │  │
│  └─────────────────────────────────────────────────────────┘  │
└────────────────────────────────────────────────────────────────┘

1. Test Programmatici (Python)

Esempio: tests/functional/test_radar_track.py

from artos.test_execution import TestCase, assert_equal

class TestRadarTracking(TestCase):
    """Test radar target tracking capability"""
    
    def test_track_single_target(self, context):
        """Verify radar can track a single injected target"""
        
        # Setup
        context.target_sim.inject_target(id=101, distance=50, azimuth=0)
        context.wait(seconds=2)
        
        # Action
        context.send_command("rdr_mode", "TWS")
        context.wait_for_condition(
            lambda: context.get_message("B6").track_count > 0,
            timeout=5
        )
        
        # Validation
        result = context.run_algorithm("TargetValidator", tolerance=0.5)
        assert_equal(result["status"], "PASS", "Target not detected")
        
        # Cleanup
        context.target_sim.clear_all_targets()

Caratteristiche:

  • Versionabili in git
  • Code review possibile
  • Parametrizzabili con pytest fixtures
  • Eseguibili in CI/CD pipeline

2. Test Registrati (GUI Sequence)

Esempio: sequences/manual/radar_mode_check.json

{
  "name": "Radar Mode Switching Check",
  "description": "Recorded sequence of mode changes",
  "date": "2026-01-09T10:30:00",
  "recorder_version": "2.0",
  "actions": [
    {
      "timestamp": 0.0,
      "dock": "mcs_control",
      "widget": "rdr_mode_combo",
      "action": "select_value",
      "value": "RWS",
      "delay_ms": 1000
    },
    {
      "timestamp": 1.0,
      "dock": "mcs_control",
      "widget": "range_scale_spin",
      "action": "set_value",
      "value": 80,
      "delay_ms": 500
    },
    {
      "timestamp": 1.5,
      "validation": {
        "algorithm": "MessageValidator",
        "params": {"msg_id": "B7", "field": "rdr_mode_tellback"},
        "expected": "RWS"
      }
    }
  ]
}

Caratteristiche:

  • Rapidi da creare (record & play)
  • Ideali per smoke testing
  • Non richiedono programmazione
  • Convertibili in test Python (export feature)

Registrazione dalle Docking Area

Flow di Recording:

┌─────────────────────────────────────────────────────────────┐
│ User interacts with GUI Dock                                 │
│   ↓                                                          │
│ Dock emits action event (Qt Signal / Tkinter callback)      │
│   ↓                                                          │
│ RecorderService intercepts event                            │
│   ↓                                                          │
│ Event serialized to JSON action                             │
│   ↓                                                          │
│ Action appended to current sequence                         │
│   ↓                                                          │
│ Sequence saved to file on Stop Recording                    │
└─────────────────────────────────────────────────────────────┘

Implementazione:

# artos/gui/recording/recorder_service.py
class RecorderService:
    """Intercepts actions from all docks and serializes them"""
    
    def register_dock(self, dock: BaseDock):
        """Connect to dock signals for recording"""
        dock.action_performed.connect(self._on_action)
    
    def _on_action(self, dock_id: str, widget_id: str, action: str, value: Any):
        if self.is_recording:
            self.current_sequence.append({
                "dock": dock_id,
                "widget": widget_id,
                "action": action,
                "value": value,
                "timestamp": time.time() - self.start_time
            })

Conversione Sequence → Python Test

Feature: Export automatico di sequenze GUI in test Python

# artos/test_execution/sequence_to_test.py
def export_sequence_to_python(sequence_file: str, output_file: str):
    """Converts a GUI sequence JSON to a Python test case"""
    
    with open(sequence_file) as f:
        seq = json.load(f)
    
    test_code = f"""
class Test{seq['name'].replace(' ', '')}(TestCase):
    def test_generated(self, context):
        \"\"\"Auto-generated from sequence: {seq['description']}\"\"\"
"""
    
    for action in seq['actions']:
        if action.get('validation'):
            test_code += f"        result = context.run_algorithm('{action['validation']['algorithm']}', ...)\n"
        else:
            test_code += f"        context.send_command('{action['widget']}', {action['value']})\n"
    
    with open(output_file, 'w') as f:
        f.write(test_code)

Rationale

  • Flexibilità: Utenti possono scegliere il metodo più adatto al task
  • Onboarding: Non-programmatori possono registrare test
  • Maintenance: Test complessi meglio gestiti come Python
  • Best of both worlds: Sequence per smoke test, Python per integration

Impatto

  • Abbassa barriera d'ingresso per test creation
  • Permette test rapidi senza programmazione
  • Test Python per scenari complessi
  • ⚠️ Serve infrastruttura di recording più sofisticata
  • ⚠️ Maintenance di due sistemi (ma condividono TestContext)

Piano di Implementazione

Fase 1: Fondamenta (2-3 settimane)

Obiettivo: Creare L1 (Orchestrator Core) senza rompere GUI esistente

Step 1.1: TestContext

  • Creare artos/core/test_context.py
  • Implementare basic API (get_message, send_command)
  • Unit test per TestContext
  • Integrare con bus1553_module esistente

Step 1.2: Module Manager

  • Creare artos/core/module_manager.py
  • Gestione lifecycle moduli (init, start, stop)
  • Registry pattern per moduli

Step 1.3: Backward Compatibility

  • GUI esistente continua a funzionare
  • TestContext disponibile opzionalmente

Deliverable: TestContext funzionante, usabile da script Python esterni


Fase 2: GUI Modulare con Tkinter Docking (3-4 settimane)

Obiettivo: Sistema docking custom con Tkinter

Step 2.1: Infrastruttura Base [IN CORSO]

  • Decisione: Tkinter + custom docking
  • Creare DockFrame base class
  • Creare WorkspaceManager con PanedWindow
  • POC con 1-2 dock esempio
  • Sistema save/load layout (JSON)

Step 2.2: Migrazione MCS Dock [IN CORSO]

  • Creare MainDockingWindow
  • Migrare contenuto main_window.py in MCS Dock
  • Testare funzionalità esistenti (spinbox, combobox, tabs)
  • Validare performance refresh loop

Step 2.3: Dock Placeholder

  • Test Control Dock (vuoto, UI base)
  • Recorder Dock (vuoto, UI base)
  • Video Display Dock (vuoto, canvas placeholder)
  • Target Sim Dock (vuoto, UI base)

Deliverable: GUI con sistema docking funzionante e MCS migrato


Fase 3: Test System Dual-Mode (2-3 settimane)

Obiettivo: Supporto sia Python che GUI sequences

Step 3.1: Python Test Runner

  • Creare artos/test_execution/test_runner.py
  • Test loader per file .py
  • Integrazione con TestContext
  • Esempio test suite in tests/functional/

Step 3.2: Sequence Recording

  • RecorderService per intercettare azioni dock
  • Serializzazione a JSON
  • Sequence player con playback

Step 3.3: Export Feature

  • Tool per convertire sequence → Python
  • CLI per eseguire test headless

Deliverable: Entrambi i sistemi funzionanti e documentati


Fase 4: Algorithm Library (1-2 settimane)

Obiettivo: Creare L2 con algoritmi riutilizzabili

Step 4.1: Base Infrastructure

  • BaseAlgorithm interface
  • Algorithm registry
  • 2-3 algoritmi esempio (MessageValidator, TimingChecker)

Deliverable: Libreria algoritmi funzionale


Fase 5: Pulizia e Documentazione (1 settimana)

  • Deprecare codice legacy
  • Aggiornare README e manuals
  • Video tutorial per nuovo workflow

--- Decisioni Prese

D1: Framework GUI DECISO

Decisione: Tkinter + custom docking system

Rationale:

  • Stdlib affidabile, zero dipendenze critiche
  • Team già esperto
  • Performance adeguate per i nostri requisiti
  • Nessun licensing concern
  • Rischi significativamente ridotti vs PyQt6

Status: Implementazione iniziata (Fase 2 in corso)


🤔 Da Decidere

  • B) Tkinter + custom docking (più semplice ma limitato)
  • C) PySide6 (simile a PyQt6, LGPL)

Prossimo step: POC con PyQt6 per validare performance video


Q2: Event Bus Implementation

Domanda: Serve un event bus per comunicazione cross-modulo?

Scenario: MFD Dock mostra alert quando 1553 rileva warning

Opzioni:

  • A) TestContext funge da mediator (più semplice)
  • B) Event bus separato (Qt Signals, o custom pub/sub)

Decisione temporanea: Posticipare, iniziare con TestContext come mediator


Q3: Video Streaming Architecture

Domanda: Come gestire streaming video real-time verso MFD/SAR dock?

Opzioni:

  • A) VideoModule scrive frames in shared memory, Dock legge
  • B) Qt Signal/Slot per frame updates
  • C) ZeroMQ per IPC video

Prossimo step: Profiling performance con diverse soluzioni


Q4: Test Report Format

Domanda: Formato standard per test reports?

Opzioni:

  • A) JUnit XML (integrazione CI/CD)
  • B) HTML custom (leggibile)
  • C) JSON + template engine

2026-01-09 (Pomeriggio) - Decisione Framework GUI + Inizio Implementazione

  • DECISIONE CRITICA: Scelta Tkinter + custom docking invece di PyQt6
  • Motivazione: Affidabilità stdlib, zero licensing, team expertise, rischi ridotti
  • Aggiornato piano Fase 2: Implementazione custom docking in Tkinter
  • IMPLEMENTAZIONE COMPLETATA - Prima Iterazione:
    • Creata struttura pymsc/gui/docking/ con:
      • DockFrame: base class per pannelli dockabili (minimize, close, title bar)
      • WorkspaceManager: gestione layout con tk.PanedWindow (splits orizzontali/verticali)
      • MCSDock: migrazione completa controlli MCS in dock con tab (Page 01-07)
      • PlaceholderDock: dock generici per moduli futuri
    • Creato MainDockingWindow: finestra principale con:
      • Layout a 3 pannelli: Left (MCS) | Right Top (Nav, IRST) | Right Bottom (Logger)
      • Menu bar con File, View, System, Help
      • Save/Load layout con JSON serialization
      • Theme: ttkbootstrap "darkly"
    • Script launcher: scripts/launch_docking_gui.py
    • Documentazione aggiornata: corretto "Impatto" sezione Decision 2

2026-01-09 - ranea:** JUnit XML per CI + HTML per human


📝 Decisioni da Rivedere

  • Sincrono vs Asincrono TestContext: Iniziare sincrono, valutare async se bottleneck
  • Recording Granularity: Registrare ogni singola azione o solo "meaningful events"?

Log delle Modifiche

2026-01-09 - Inizializzazione ADR

  • Creato documento iniziale
  • Documentate decisioni su architettura layered
  • Proposta GUI modulare con docking
  • Definito dual-mode test system
  • Piano di implementazione a 5 fasi

Prossimi Passi Immediati

  1. Revisione con team: Discutere scelte architetturali (questo documento)
  2. POC PyQt6: Test pratico docking system + video streaming
  3. Implementazione Fase 1: Creare TestContext funzionante
  4. Update documento: Aggiornare ADR con risultati POC

Riferimenti