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
- Panoramica e Obiettivi
- Decisione 1: Architettura a Layer Separati
- Decisione 2: GUI Modulare con Docking System
- Decisione 3: Dual-Mode Test System
- Piano di Implementazione
- 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
- Modularità: Ogni componente HW ha il suo modulo indipendente
- Testabilità: Test automatici eseguibili sia da GUI che da CLI
- Estensibilità: Facile aggiunta di nuovi moduli e algoritmi
- Usabilità: GUI moderna con workspace personalizzabili
- 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
- Floating e Ridimensionabili: Ogni dock può essere spostato/ridimensionato
- Ownership Modulare: Ogni modulo HW controlla il proprio dock
- Layout Salvabile: L'utente può salvare/caricare configurazioni workspace
- 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
ttkbootstrapper temi moderni - ✅
PanedWindownativo 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:
- Affidabilità: Tkinter è stdlib, sempre disponibile, zero rischi dipendenze
- Preservazione codebase: Tutto il lavoro fatto rimane valido
- Pragmatismo: Performance sufficienti per i nostri requisiti (30 FPS max)
- Manutenibilità: Team già esperto in Tkinter
- Distribuzione: Nessun licensing concern, build più semplici
Implementazione:
DockFrame: Classe base custom per pannelli dockabiliWorkspaceManager: Gestione layout contk.PanedWindowttkbootstrap: Temi moderni e widget styledPIL/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:
- Test Programmatici (Python): Script scritti a mano, versionabili, ripetibili
- 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
DockFramebase class - Creare
WorkspaceManagercon 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 contk.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
- ✅ Creata struttura
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
- Revisione con team: Discutere scelte architetturali (questo documento)
- POC PyQt6: Test pratico docking system + video streaming
- Implementazione Fase 1: Creare TestContext funzionante
- Update documento: Aggiornare ADR con risultati POC