From 38cab7d5017d3d4bdafd4ebd14f42ba427df4fbc Mon Sep 17 00:00:00 2001 From: Radek Date: Tue, 24 Sep 2024 16:37:23 -0700 Subject: [PATCH] Add multiple pandas selector (#2015) * multiple Panda cli helper * Don't flash all pandas by default. Use --all arg to flash all. --- Jenkinsfile | 4 ++-- board/flash.py | 15 ++++++++++++--- board/jungle/flash.py | 20 ++++++++++++++------ board/jungle/recover.py | 10 ++++++++-- board/recover.py | 10 ++++++++-- python/__init__.py | 38 ++++++++++++++++++++++++++++++++++++-- 6 files changed, 80 insertions(+), 17 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 2ccf6be11..3e6b888e1 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -106,7 +106,7 @@ pipeline { phone_steps("panda-tres", [ ["build", "scons -j4"], ["flash", "cd tests/ && ./reflash_internal_panda.py"], - ["flash jungle", "cd board/jungle && ./flash.py"], + ["flash jungle", "cd board/jungle && ./flash.py --all"], ["test", "cd tests/hitl && HW_TYPES=9 pytest -n0 --durations=0 2*.py [5-9]*.py"], ]) } @@ -118,7 +118,7 @@ pipeline { phone_steps("panda-dos", [ ["build", "scons -j4"], ["flash", "cd tests/ && ./reflash_internal_panda.py"], - ["flash jungle", "cd board/jungle && ./flash.py"], + ["flash jungle", "cd board/jungle && ./flash.py --all"], ["test", "cd tests/hitl && HW_TYPES=6 pytest -n0 --durations=0 [2-9]*.py -k 'not test_send_recv'"], ]) } diff --git a/board/flash.py b/board/flash.py index 903a6a645..394381462 100755 --- a/board/flash.py +++ b/board/flash.py @@ -1,18 +1,27 @@ #!/usr/bin/env python3 import os import subprocess +import argparse from panda import Panda board_path = os.path.dirname(os.path.realpath(__file__)) if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--all", action="store_true", help="Recover all Panda devices") + args = parser.parse_args() + subprocess.check_call(f"scons -C {board_path}/.. -j$(nproc) {board_path}", shell=True) - serials = Panda.list() - print(f"found {len(serials)} panda(s) - {serials}") + if args.all: + serials = Panda.list() + print(f"found {len(serials)} panda(s) - {serials}") + else: + serials = [None] + for s in serials: - print("flashing", s) with Panda(serial=s) as p: + print("flashing", p.get_usb_serial()) p.flash() exit(1 if len(serials) == 0 else 0) diff --git a/board/jungle/flash.py b/board/jungle/flash.py index 5b6c6e904..8378169e4 100755 --- a/board/jungle/flash.py +++ b/board/jungle/flash.py @@ -1,19 +1,27 @@ #!/usr/bin/env python3 import os import subprocess +import argparse from panda import PandaJungle board_path = os.path.dirname(os.path.realpath(__file__)) if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--all", action="store_true", help="Recover all panda jungle devices") + args = parser.parse_args() + subprocess.check_call(f"scons -C {board_path}/.. -u -j$(nproc) {board_path}", shell=True) - serials = PandaJungle.list() - print(f"found {len(serials)} panda jungle(s) - {serials}") - for s in serials: - print("flashing", s) - with PandaJungle(serial=s) as p: - p.flash() + if args.all: + serials = PandaJungle.list() + print(f"found {len(serials)} panda jungles(s) - {serials}") + else: + serials = [None] + for s in serials: + with PandaJungle(serial=s) as p: + print("flashing", p.get_usb_serial()) + p.flash() exit(1 if len(serials) == 0 else 0) diff --git a/board/jungle/recover.py b/board/jungle/recover.py index 19666c3ed..34c88e26e 100755 --- a/board/jungle/recover.py +++ b/board/jungle/recover.py @@ -2,17 +2,23 @@ import os import time import subprocess +import argparse from panda import PandaJungle, PandaJungleDFU board_path = os.path.dirname(os.path.realpath(__file__)) if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--all", action="store_true", help="Recover all panda jungle devices") + args = parser.parse_args() + subprocess.check_call(f"scons -C {board_path}/.. -u -j$(nproc) {board_path}", shell=True) - for s in PandaJungle.list(): - print("putting", s, "in DFU mode") + serials = PandaJungle.list() if args.all else [None] + for s in serials: with PandaJungle(serial=s) as p: + print(f"putting {p.get_usb_serial()} in DFU mode") p.reset(enter_bootstub=True) p.reset(enter_bootloader=True) diff --git a/board/recover.py b/board/recover.py index 0a248525a..c149e948d 100755 --- a/board/recover.py +++ b/board/recover.py @@ -2,17 +2,23 @@ import os import time import subprocess +import argparse from panda import Panda, PandaDFU board_path = os.path.dirname(os.path.realpath(__file__)) if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--all", action="store_true", help="Recover all Panda devices") + args = parser.parse_args() + subprocess.check_call(f"scons -C {board_path}/.. -j$(nproc) {board_path}", shell=True) - for s in Panda.list(): - print("putting", s, "in DFU mode") + serials = Panda.list() if args.all else [None] + for s in serials: with Panda(serial=s) as p: + print(f"putting {p.get_usb_serial()} in DFU mode") p.reset(enter_bootstub=True) p.reset(enter_bootloader=True) diff --git a/python/__init__.py b/python/__init__.py index 0e531dfc7..d2bbb2fe4 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -224,8 +224,7 @@ class Panda: FLAG_FORD_LONG_CONTROL = 1 FLAG_FORD_CANFD = 2 - def __init__(self, serial: str | None = None, claim: bool = True, disable_checks: bool = True, can_speed_kbps: int = 500): - self._connect_serial = serial + def __init__(self, serial: str | None = None, claim: bool = True, disable_checks: bool = True, can_speed_kbps: int = 500, cli: bool = True): self._disable_checks = disable_checks self._handle: BaseHandle @@ -233,9 +232,38 @@ class Panda: self.can_rx_overflow_buffer = b'' self._can_speed_kbps = can_speed_kbps + if cli and serial is None: + self._connect_serial = self._cli_select_panda() + else: + self._connect_serial = serial + # connect and set mcu type self.connect(claim) + def _cli_select_panda(self): + dfu_pandas = PandaDFU.list() + if len(dfu_pandas) > 0: + print("INFO: some attached pandas are in DFU mode.") + + pandas = self.list() + if len(pandas) == 0: + print("INFO: panda not available") + return None + if len(pandas) == 1: + print(f"INFO: connecting to panda {pandas[0]}") + time.sleep(1) + return pandas[0] + while True: + print("Multiple pandas available:") + pandas.sort() + for idx, serial in enumerate(pandas): + print(f"{[idx]}: {serial}") + try: + choice = int(input("Choose serial [0]:") or "0") + return pandas[choice] + except (ValueError, IndexError): + print("Enter a valid index.") + def __enter__(self): return self @@ -391,6 +419,12 @@ class Panda: return context, usb_handle, usb_serial, bootstub, bcd + def is_connected_spi(self): + return isinstance(self._handle, PandaSpiHandle) + + def is_connected_usb(self): + return isinstance(self._handle, PandaUsbHandle) + @classmethod def list(cls): ret = cls.usb_list()