Add multiple pandas selector (#2015)

* multiple Panda cli helper

* Don't flash all pandas by default. Use --all arg to flash all.
This commit is contained in:
Radek 2024-09-24 16:37:23 -07:00 committed by GitHub
parent 2037a2ead7
commit 38cab7d501
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 80 additions and 17 deletions

4
Jenkinsfile vendored
View File

@ -106,7 +106,7 @@ pipeline {
phone_steps("panda-tres", [ phone_steps("panda-tres", [
["build", "scons -j4"], ["build", "scons -j4"],
["flash", "cd tests/ && ./reflash_internal_panda.py"], ["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"], ["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", [ phone_steps("panda-dos", [
["build", "scons -j4"], ["build", "scons -j4"],
["flash", "cd tests/ && ./reflash_internal_panda.py"], ["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'"], ["test", "cd tests/hitl && HW_TYPES=6 pytest -n0 --durations=0 [2-9]*.py -k 'not test_send_recv'"],
]) ])
} }

View File

@ -1,18 +1,27 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os import os
import subprocess import subprocess
import argparse
from panda import Panda from panda import Panda
board_path = os.path.dirname(os.path.realpath(__file__)) board_path = os.path.dirname(os.path.realpath(__file__))
if __name__ == "__main__": 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) subprocess.check_call(f"scons -C {board_path}/.. -j$(nproc) {board_path}", shell=True)
if args.all:
serials = Panda.list() serials = Panda.list()
print(f"found {len(serials)} panda(s) - {serials}") print(f"found {len(serials)} panda(s) - {serials}")
else:
serials = [None]
for s in serials: for s in serials:
print("flashing", s)
with Panda(serial=s) as p: with Panda(serial=s) as p:
print("flashing", p.get_usb_serial())
p.flash() p.flash()
exit(1 if len(serials) == 0 else 0) exit(1 if len(serials) == 0 else 0)

View File

@ -1,19 +1,27 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os import os
import subprocess import subprocess
import argparse
from panda import PandaJungle from panda import PandaJungle
board_path = os.path.dirname(os.path.realpath(__file__)) board_path = os.path.dirname(os.path.realpath(__file__))
if __name__ == "__main__": 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) subprocess.check_call(f"scons -C {board_path}/.. -u -j$(nproc) {board_path}", shell=True)
if args.all:
serials = PandaJungle.list() serials = PandaJungle.list()
print(f"found {len(serials)} panda jungle(s) - {serials}") print(f"found {len(serials)} panda jungles(s) - {serials}")
for s in serials: else:
print("flashing", s) serials = [None]
with PandaJungle(serial=s) as p:
p.flash()
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) exit(1 if len(serials) == 0 else 0)

View File

@ -2,17 +2,23 @@
import os import os
import time import time
import subprocess import subprocess
import argparse
from panda import PandaJungle, PandaJungleDFU from panda import PandaJungle, PandaJungleDFU
board_path = os.path.dirname(os.path.realpath(__file__)) board_path = os.path.dirname(os.path.realpath(__file__))
if __name__ == "__main__": 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) subprocess.check_call(f"scons -C {board_path}/.. -u -j$(nproc) {board_path}", shell=True)
for s in PandaJungle.list(): serials = PandaJungle.list() if args.all else [None]
print("putting", s, "in DFU mode") for s in serials:
with PandaJungle(serial=s) as p: with PandaJungle(serial=s) as p:
print(f"putting {p.get_usb_serial()} in DFU mode")
p.reset(enter_bootstub=True) p.reset(enter_bootstub=True)
p.reset(enter_bootloader=True) p.reset(enter_bootloader=True)

View File

@ -2,17 +2,23 @@
import os import os
import time import time
import subprocess import subprocess
import argparse
from panda import Panda, PandaDFU from panda import Panda, PandaDFU
board_path = os.path.dirname(os.path.realpath(__file__)) board_path = os.path.dirname(os.path.realpath(__file__))
if __name__ == "__main__": 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) subprocess.check_call(f"scons -C {board_path}/.. -j$(nproc) {board_path}", shell=True)
for s in Panda.list(): serials = Panda.list() if args.all else [None]
print("putting", s, "in DFU mode") for s in serials:
with Panda(serial=s) as p: with Panda(serial=s) as p:
print(f"putting {p.get_usb_serial()} in DFU mode")
p.reset(enter_bootstub=True) p.reset(enter_bootstub=True)
p.reset(enter_bootloader=True) p.reset(enter_bootloader=True)

View File

@ -224,8 +224,7 @@ class Panda:
FLAG_FORD_LONG_CONTROL = 1 FLAG_FORD_LONG_CONTROL = 1
FLAG_FORD_CANFD = 2 FLAG_FORD_CANFD = 2
def __init__(self, serial: str | None = None, claim: bool = True, disable_checks: bool = True, can_speed_kbps: int = 500): def __init__(self, serial: str | None = None, claim: bool = True, disable_checks: bool = True, can_speed_kbps: int = 500, cli: bool = True):
self._connect_serial = serial
self._disable_checks = disable_checks self._disable_checks = disable_checks
self._handle: BaseHandle self._handle: BaseHandle
@ -233,9 +232,38 @@ class Panda:
self.can_rx_overflow_buffer = b'' self.can_rx_overflow_buffer = b''
self._can_speed_kbps = can_speed_kbps 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 # connect and set mcu type
self.connect(claim) 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): def __enter__(self):
return self return self
@ -391,6 +419,12 @@ class Panda:
return context, usb_handle, usb_serial, bootstub, bcd 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 @classmethod
def list(cls): def list(cls):
ret = cls.usb_list() ret = cls.usb_list()