#!/usr/bin/env python3 import ast import shutil import subprocess import sys from pathlib import Path from rich.console import Console from rich.table import Table console = Console() def info(msg: str) -> None: console.print(f"{msg}") def ok(msg: str) -> None: console.print(f"[green]✔ {msg}[/]") def warn(msg: str) -> None: console.print(f"[yellow]▲ {msg}[/]") def die(msg: str, code: int = 1) -> None: console.print(f"[bold red]✖ {msg}[/]") sys.exit(code) def usage() -> None: prog = Path(sys.argv[0]).name console.print( f"[blue]Uso:[/blue] {prog} [cyan][/cyan] [cyan][/cyan]\n" ) # console.print( # " NOMEROBOT Variabile in robots.py (es. cbrobot) oppure nome hub BLE (es. cbhub)" # ) # console.print(" NOMEFILE File .py da inviare al hub con pybricksdev\n") # console.print( # f" Esempi:\n {prog} cbrobot guidaavantieindietro.py\n {prog} cbhub menu.py" # ) def ensure_project_layout() -> None: missing = [] if not Path("robot_class.py").is_file(): missing.append("robot_class.py") if not Path(".scripts").is_dir(): missing.append(".scripts/") if missing: die("Non sei nella cartella giusta: mancano " + ", ".join(missing), code=9) def parse_robots_py(robots_py: Path) -> dict[str, str]: """Ritorna {variabile: hub_name} da assegnazioni tipo: X = LazyRobot(..., name='hub', ...).""" if not robots_py.exists(): die(f"File non trovato: {robots_py}", code=3) try: tree = ast.parse(robots_py.read_text(encoding="utf-8"), filename=str(robots_py)) except SyntaxError as e: die(f"robots.py non parsabile: {e}", code=3) mapping: dict[str, str] = {} for node in tree.body: if not isinstance(node, ast.Assign): continue if len(node.targets) != 1 or not isinstance(node.targets[0], ast.Name): continue var = node.targets[0].id call = node.value if not isinstance(call, ast.Call): continue func = call.func if not (isinstance(func, ast.Name) and func.id == "LazyRobot"): continue hub_name = None for kw in call.keywords: if ( kw.arg == "name" and isinstance(kw.value, ast.Constant) and isinstance(kw.value.value, str) ): hub_name = kw.value.value break if hub_name: mapping[var] = hub_name if not mapping: die("Nessun robot trovato in robots.py (LazyRobot(..., name=...)).", code=3) return mapping def choose_robot(mapping: dict[str, str], ident: str) -> tuple[str, str]: """Accetta variabile o hub. Ritorna (var_name, hub_name).""" ident_lc = ident.strip().lower() for var, hub in mapping.items(): if var.lower() == ident_lc: return var, hub for var, hub in mapping.items(): if hub.lower() == ident_lc: return var, hub warn(f"Robot '{ident}' non trovato in robots.py.") table = Table(title="Robot conosciuti (da robots.py)") table.add_column("Variabile", style="cyan", no_wrap=True) table.add_column("Nome hub (BLE)", style="green") for var, hub in mapping.items(): table.add_row(var, hub) console.print(table) sys.exit(4) def write_robot_local(robot_local: Path, var_name: str) -> None: """Crea/sovrascrive robot_local.py.""" header = ( "# robot_local.py — generato automaticamente da pbd.py\n" "# Non commitare questo file: deve essere nel .gitignore.\n" ) body = f"from robots import {var_name} as robot\n" try: robot_local.write_text(header + body, encoding="utf-8") except Exception as e: die(f"Impossibile scrivere {robot_local}: {e}") def run_pybricksdev(file_path: Path, hub_name: str) -> int: if not file_path.exists(): die(f"File target da inviare non trovato: {file_path}", code=6) exe = shutil.which("pybricksdev") if exe is None: die("Comando 'pybricksdev' non trovato nel PATH. Installalo e riprova.", code=7) cmd = [exe, "run", "ble", str(file_path), "--name", hub_name] return subprocess.run(cmd, check=False).returncode def main() -> None: # argv: 0=prog, 1=nomerobot, 2=nomefile if len(sys.argv) < 3 or len(sys.argv) > 3: usage() die("Parametri errati: servono esattamente NOMEROBOT e NOMEFILE.", code=2) nomerobot = sys.argv[1] nomefile = sys.argv[2] ensure_project_layout() mapping = parse_robots_py(Path("robots.py")) var, hub = choose_robot(mapping, nomerobot) info(f"HUB: [green]{hub}[/] ROBOT: [cyan]{var}[/]") # crea/scrive robot_local.py (se non esiste, lo crea) write_robot_local(Path("robot_local.py"), var) # ok(f"Aggiornato robot_local.py con 'from robots import {var} as robot'") # info(f"Eseguo: pybricksdev run ble {nomefile} --name {hub}") rc = run_pybricksdev(Path(nomefile), hub) if rc == 0: ok("programma terminato.") else: die(f"pybricksdev terminato con exit code {rc}.", code=rc) if __name__ == "__main__": main()