diff --git a/panda b/panda index bf2c007103..1a9a94c519 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit bf2c0071036bae4e0c00c37ae43237fce6063e31 +Subproject commit 1a9a94c519cb74e9e0f57bea4d316a54c27ea8aa diff --git a/selfdrive/boardd/tests/bootstub.panda.bin b/selfdrive/boardd/tests/bootstub.panda.bin new file mode 100755 index 0000000000..43db537061 Binary files /dev/null and b/selfdrive/boardd/tests/bootstub.panda.bin differ diff --git a/selfdrive/boardd/tests/bootstub.panda_h7.bin b/selfdrive/boardd/tests/bootstub.panda_h7.bin new file mode 100755 index 0000000000..5cf2fa4519 Binary files /dev/null and b/selfdrive/boardd/tests/bootstub.panda_h7.bin differ diff --git a/selfdrive/boardd/tests/test_pandad.py b/selfdrive/boardd/tests/test_pandad.py index 50d24f4fe3..fff17523e3 100755 --- a/selfdrive/boardd/tests/test_pandad.py +++ b/selfdrive/boardd/tests/test_pandad.py @@ -1,16 +1,19 @@ #!/usr/bin/env python3 +import os import time import unittest import cereal.messaging as messaging from cereal import log from common.gpio import gpio_set, gpio_init -from panda import Panda +from panda import Panda, PandaDFU from selfdrive.test.helpers import phone_only from selfdrive.manager.process_config import managed_processes from system.hardware import HARDWARE from system.hardware.tici.pins import GPIO +HERE = os.path.dirname(os.path.realpath(__file__)) + class TestPandad(unittest.TestCase): @@ -27,11 +30,13 @@ class TestPandad(unittest.TestCase): if sm['peripheralState'].pandaType == log.PandaState.PandaType.unknown: raise Exception("boardd failed to start") + def _go_to_dfu(self): + HARDWARE.recover_internal_panda() + assert Panda.wait_for_dfu(None, 10) + @phone_only def test_in_dfu(self): HARDWARE.recover_internal_panda() - time.sleep(1) - managed_processes['pandad'].start() self._wait_for_boardd(60) @@ -66,9 +71,25 @@ class TestPandad(unittest.TestCase): managed_processes['pandad'].start() self._wait_for_boardd(8) + @phone_only + def test_release_to_devel_bootstub(self): + if HARDWARE.get_device_type() != 'tici': + self.skipTest("TODO: fix reset timeout") + + # flash release bootstub + self._go_to_dfu() + pd = PandaDFU(None) + fn = os.path.join(HERE, pd.get_mcu_type().config.bootstub_fn) + with open(fn, "rb") as f: + pd.program_bootstub(f.read()) + pd.reset() + + assert Panda.wait_for_panda(None, 20) + with Panda() as p: + assert p.bootstub - #def test_out_of_date_fw(self): - # pass + managed_processes['pandad'].start() + self._wait_for_boardd(60) if __name__ == "__main__": diff --git a/selfdrive/manager/process_config.py b/selfdrive/manager/process_config.py index 57c42aaaaa..ee8d079bdf 100644 --- a/selfdrive/manager/process_config.py +++ b/selfdrive/manager/process_config.py @@ -47,7 +47,7 @@ procs = [ NativeProcess("navmodeld", "selfdrive/modeld", ["./navmodeld"], enabled=False), NativeProcess("sensord", "system/sensord", ["./sensord"], enabled=not PC), NativeProcess("ui", "selfdrive/ui", ["./ui"], offroad=True, watchdog_max_dt=(5 if not PC else None)), - NativeProcess("soundd", "selfdrive/ui/soundd", ["./soundd"], offroad=True), + NativeProcess("soundd", "selfdrive/ui/soundd", ["./soundd"]), NativeProcess("locationd", "selfdrive/locationd", ["./locationd"]), NativeProcess("boardd", "selfdrive/boardd", ["./boardd"], enabled=False), PythonProcess("calibrationd", "selfdrive.locationd.calibrationd"), diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 7ee1d05962..9261aee838 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -6,6 +6,7 @@ import platform from collections import OrderedDict from dataclasses import dataclass, field from typing import Dict, List, Optional, Callable +from tqdm import tqdm import cereal.messaging as messaging from cereal import car @@ -77,9 +78,9 @@ class ReplayContext: def wait_for_recv_called(self): messaging.wait_for_one_event(self.all_recv_called_events) - def wait_for_next_recv(self, end_of_cycle): + def wait_for_next_recv(self, trigger_empty_recv): index = messaging.wait_for_one_event(self.all_recv_called_events) - if self.drained_pub is not None and end_of_cycle: + if self.drained_pub is not None and trigger_empty_recv: self.all_recv_called_events[index].clear() self.all_recv_ready_events[index].set() self.all_recv_called_events[index].wait() @@ -147,6 +148,8 @@ def get_car_params_callback(rc, pm, msgs, fingerprint): sendcan = DummySocket() canmsgs = [msg for msg in msgs if msg.which() == "can"] + assert len(canmsgs) != 0, "CAN messages are required for carParams initialization" + for m in canmsgs[:300]: can.send(m.as_builder().to_bytes()) _, CP = get_car(can, sendcan, Params().get_bool("ExperimentalLongitudinalEnabled")) @@ -256,7 +259,7 @@ CONFIGS = [ subs=["liveCalibration"], ignore=["logMonoTime", "valid"], config_callback=None, - init_callback=get_car_params_callback, + init_callback=None, should_recv_callback=calibration_rcv_callback, ), ProcessConfig( @@ -265,7 +268,7 @@ CONFIGS = [ subs=["driverMonitoringState"], ignore=["logMonoTime", "valid"], config_callback=None, - init_callback=get_car_params_callback, + init_callback=None, should_recv_callback=FrequencyBasedRcvCallback("driverStateV2"), tolerance=NUMPY_TOLERANCE, ), @@ -278,7 +281,7 @@ CONFIGS = [ subs=["liveLocationKalman"], ignore=["logMonoTime", "valid"], config_callback=locationd_config_pubsub_callback, - init_callback=get_car_params_callback, + init_callback=None, should_recv_callback=None, tolerance=NUMPY_TOLERANCE, ), @@ -307,7 +310,7 @@ CONFIGS = [ subs=["gnssMeasurements"], ignore=["logMonoTime"], config_callback=laikad_config_pubsub_callback, - init_callback=get_car_params_callback, + init_callback=None, should_recv_callback=None, tolerance=NUMPY_TOLERANCE, timeout=60*10, # first messages are blocked on internet assistance @@ -338,9 +341,9 @@ def replay_process_with_name(name, lr, *args, **kwargs): return replay_process(cfg, lr, *args, **kwargs) -def replay_process(cfg, lr, fingerprint=None, return_all_logs=False): +def replay_process(cfg, lr, fingerprint=None, return_all_logs=False, disable_progress=False): all_msgs = list(lr) - process_logs = _replay_single_process(cfg, all_msgs, fingerprint) + process_logs = _replay_single_process(cfg, all_msgs, fingerprint, disable_progress) if return_all_logs: keys = set(cfg.subs) @@ -354,7 +357,7 @@ def replay_process(cfg, lr, fingerprint=None, return_all_logs=False): return log_msgs -def _replay_single_process(cfg, lr, fingerprint): +def _replay_single_process(cfg, lr, fingerprint, disable_progress): with OpenpilotPrefix(): controlsState = None initialized = False @@ -402,7 +405,7 @@ def _replay_single_process(cfg, lr, fingerprint): # Do the replay cnt = 0 - for msg in pub_msgs: + for msg in tqdm(pub_msgs, disable=disable_progress): with Timeout(cfg.timeout, error_msg=f"timed out testing process {repr(cfg.proc_name)}, {cnt}/{len(pub_msgs)} msgs done"): resp_sockets, end_of_cycle = cfg.subs, True if cfg.should_recv_callback is not None: @@ -417,12 +420,17 @@ def _replay_single_process(cfg, lr, fingerprint): for s in sockets.values(): messaging.recv_one_or_none(s) + # empty recv on drained pub indicates the end of messages, only do that if there're any + trigger_empty_recv = False + if cfg.drained_pub: + trigger_empty_recv = next((True for m in msg_queue if m.which() == cfg.drained_pub), False) + for m in msg_queue: pm.send(m.which(), m.as_builder()) msg_queue = [] rc.unlock_sockets() - rc.wait_for_next_recv(True) + rc.wait_for_next_recv(trigger_empty_recv) for s in resp_sockets: ms = messaging.drain_sock(sockets[s]) diff --git a/selfdrive/test/process_replay/test_processes.py b/selfdrive/test/process_replay/test_processes.py index 02922c530d..6962e51cb5 100755 --- a/selfdrive/test/process_replay/test_processes.py +++ b/selfdrive/test/process_replay/test_processes.py @@ -99,7 +99,7 @@ def test_process(cfg, lr, segment, ref_log_path, new_log_path, ignore_fields=Non ref_log_msgs = list(LogReader(ref_log_path)) try: - log_msgs = replay_process(cfg, lr) + log_msgs = replay_process(cfg, lr, disable_progress=True) except Exception as e: raise Exception("failed on segment: " + segment) from e diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index 4b2a3d88c8..226416dca9 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -1057,7 +1057,7 @@ Isso pode levar até um minuto. Aggressive - Disputador + Disputa Standard @@ -1069,11 +1069,11 @@ Isso pode levar até um minuto. Driving Personality - Personalidade de Condução + Temperamento de Direção Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. - Neutro é o recomendado. No modo disputador o openpilot seguirá o carro da frente mais de perto e será mais agressivo com a aceleração e frenagem. No modo calmo o openpilot se manterá mais longe do carro da frente. + Neutro é o recomendado. No modo disputa o openpilot seguirá o carro da frente mais de perto e será mais agressivo com a aceleração e frenagem. No modo calmo o openpilot se manterá mais longe do carro da frente. diff --git a/system/hardware/tici/tests/test_power_draw.py b/system/hardware/tici/tests/test_power_draw.py index 1487db0842..7ec31670b1 100755 --- a/system/hardware/tici/tests/test_power_draw.py +++ b/system/hardware/tici/tests/test_power_draw.py @@ -5,29 +5,33 @@ import math import threading from dataclasses import dataclass from tabulate import tabulate +from typing import List import cereal.messaging as messaging +from cereal.services import service_list from system.hardware import HARDWARE, TICI from system.hardware.tici.power_monitor import get_power from selfdrive.manager.process_config import managed_processes from selfdrive.manager.manager import manager_cleanup +SAMPLE_TIME = 8 # seconds to sample power @dataclass class Proc: name: str power: float + msgs: List[str] rtol: float = 0.05 atol: float = 0.12 warmup: float = 6. PROCS = [ - Proc('camerad', 2.1), - Proc('modeld', 0.93, atol=0.2), - Proc('dmonitoringmodeld', 0.4), - Proc('encoderd', 0.23), - Proc('mapsd', 0.05), - Proc('navmodeld', 0.05), + Proc('camerad', 2.1, msgs=['roadCameraState', 'wideRoadCameraState', 'driverCameraState']), + Proc('modeld', 0.93, atol=0.2, msgs=['modelV2']), + Proc('dmonitoringmodeld', 0.4, msgs=['driverStateV2']), + Proc('encoderd', 0.23, msgs=[]), + Proc('mapsd', 0.05, msgs=['mapRenderState']), + Proc('navmodeld', 0.05, msgs=['navModel']), ] def send_llk_msg(done): @@ -39,6 +43,7 @@ def send_llk_msg(done): # Send liveLocationKalman at 20hz while not done.is_set(): + msg.clear_write_flag() pm.send('liveLocationKalman', msg) time.sleep(1/20) @@ -68,25 +73,33 @@ class TestPowerDraw(unittest.TestCase): prev = baseline used = {} + msg_counts = {} for proc in PROCS: + socks = {msg: messaging.sub_sock(msg) for msg in proc.msgs} managed_processes[proc.name].start() time.sleep(proc.warmup) + for sock in socks.values(): + messaging.drain_sock_raw(sock) - now = get_power(8) + now = get_power(SAMPLE_TIME) used[proc.name] = now - prev prev = now + for msg,sock in socks.items(): + msg_counts[msg] = len(messaging.drain_sock_raw(sock)) done.set() manager_cleanup() - tab = [] - tab.append(['process', 'expected (W)', 'measured (W)']) + tab = [['process', 'expected (W)', 'measured (W)', '# msgs expected', '# msgs received']] for proc in PROCS: cur = used[proc.name] expected = proc.power - tab.append([proc.name, round(expected, 2), round(cur, 2)]) + msgs_received = sum(msg_counts[msg] for msg in proc.msgs) + msgs_expected = int(sum(SAMPLE_TIME * service_list[msg].frequency for msg in proc.msgs)) + tab.append([proc.name, round(expected, 2), round(cur, 2), msgs_expected, msgs_received]) with self.subTest(proc=proc.name): self.assertTrue(math.isclose(cur, expected, rel_tol=proc.rtol, abs_tol=proc.atol)) + self.assertTrue(math.isclose(msgs_expected, msgs_received, rel_tol=.02, abs_tol=2)) print(tabulate(tab)) print(f"Baseline {baseline:.2f}W\n")