From 729d3ddcb55a294f244304e0d455424ce85c84a8 Mon Sep 17 00:00:00 2001 From: Mitchell Goff Date: Wed, 7 Jun 2023 14:06:11 -0700 Subject: [PATCH 1/7] Check msg count in test_power_draw (#28444) * Check msg count in test_power_draw. * Use drain_sock * Bug fix * Combine into a single table * Check camera state msgs and no default value for Proc.msgs * Update system/hardware/tici/tests/test_power_draw.py Co-authored-by: Adeeb Shihadeh --------- Co-authored-by: Adeeb Shihadeh --- system/hardware/tici/tests/test_power_draw.py | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) 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") From 02841cede3daed3ac2a8668c9596e0663c889e12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20R=C4=85czy?= Date: Thu, 8 Jun 2023 01:22:48 +0200 Subject: [PATCH 2/7] process_replay: remove get_car_params_callback when it's not needed (#28451) * Add assertion for can msgs in get_car_params_callback * Remove get_car_params_callback from processes that don't need it --- selfdrive/test/process_replay/process_replay.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 7ee1d05962..16c0c0f6a3 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -147,6 +147,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 +258,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 +267,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 +280,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 +309,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 From d932aae327fbaaae0c628ecbb571eef85f88f2af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20R=C4=85czy?= Date: Thu, 8 Jun 2023 01:23:02 +0200 Subject: [PATCH 3/7] process_replay: improved support for drained pubs (#28452) Stop triggering empty update if there're no drained messages in the queue --- selfdrive/test/process_replay/process_replay.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 16c0c0f6a3..5871488fe6 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -77,9 +77,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() @@ -419,12 +419,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]) From a2e75fdea6465bf41edd46875fb7dd241155aca8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20R=C4=85czy?= Date: Thu, 8 Jun 2023 01:23:40 +0200 Subject: [PATCH 4/7] process_replay: progress bar support (#28447) * Add support for tqdm progress bar * Fix argument name --- selfdrive/test/process_replay/process_replay.py | 9 +++++---- selfdrive/test/process_replay/test_processes.py | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 5871488fe6..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 @@ -340,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) @@ -356,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 @@ -404,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: 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 From 3e3c8b7ddf6b3d6f71182aea75db2fa642946175 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 7 Jun 2023 16:24:54 -0700 Subject: [PATCH 5/7] soundd: only run onroad (#28449) --- selfdrive/manager/process_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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"), From 28980cf4c60ca2fb79a7e0bb42be17a9abacf728 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 7 Jun 2023 20:25:15 -0700 Subject: [PATCH 6/7] pandad: test release -> devel bootstub reflash (#28330) * test * release bootstubs * rm that * fixup * skip earlier --------- Co-authored-by: Comma Device --- panda | 2 +- selfdrive/boardd/tests/bootstub.panda.bin | Bin 0 -> 15732 bytes selfdrive/boardd/tests/bootstub.panda_h7.bin | Bin 0 -> 16860 bytes selfdrive/boardd/tests/test_pandad.py | 31 ++++++++++++++++--- 4 files changed, 27 insertions(+), 6 deletions(-) create mode 100755 selfdrive/boardd/tests/bootstub.panda.bin create mode 100755 selfdrive/boardd/tests/bootstub.panda_h7.bin 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 0000000000000000000000000000000000000000..43db5370613b9a3966790c2a14f5e121907a5ade GIT binary patch literal 15732 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 HPRRcNEoX1h literal 0 HcmV?d00001 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__": From a250e1cb2f176b9de808f8ee76b6999562c47463 Mon Sep 17 00:00:00 2001 From: AlexandreSato <66435071+AlexandreSato@users.noreply.github.com> Date: Thu, 8 Jun 2023 16:54:23 -0300 Subject: [PATCH 7/7] Multilang: Fix text overlap in pt-BR translation (#28450) * update pt-BR translations * fix some cutoff texts * update pt-BR translations * Include the new relaxed explanation * fix text overlaps --- selfdrive/ui/translations/main_pt-BR.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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.