FLL/.scripts/wrapper.py
2025-10-26 19:00:36 +01:00

168 lines
5.1 KiB
Python
Executable file

#!/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]<NOMEROBOT>[/cyan] [cyan]<NOMEFILE>[/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()