168 lines
5.1 KiB
Python
Executable file
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()
|