From 3d6dfc864dd8778fbdac7b3a09bbfff8ab86c78f Mon Sep 17 00:00:00 2001 From: Jimmy <9859727+Quantizr@users.noreply.github.com> Date: Wed, 13 Aug 2025 11:43:35 -0700 Subject: [PATCH 01/64] clip: terminate processes in clip() instead of in main() (#35984) * terminate processes in clip() instead of in main() * context manager for proc --- common/run.py | 15 ++++++++ tools/clip/run.py | 87 ++++++++++++++++++----------------------------- 2 files changed, 49 insertions(+), 53 deletions(-) diff --git a/common/run.py b/common/run.py index 06deb6388d..75395ead1f 100644 --- a/common/run.py +++ b/common/run.py @@ -1,4 +1,6 @@ import subprocess +from contextlib import contextmanager +from subprocess import Popen, PIPE, TimeoutExpired def run_cmd(cmd: list[str], cwd=None, env=None) -> str: @@ -11,3 +13,16 @@ def run_cmd_default(cmd: list[str], default: str = "", cwd=None, env=None) -> st except subprocess.CalledProcessError: return default + +@contextmanager +def managed_proc(cmd: list[str], env: dict[str, str]): + proc = Popen(cmd, env=env, stdout=PIPE, stderr=PIPE) + try: + yield proc + finally: + if proc.poll() is None: + proc.terminate() + try: + proc.wait(timeout=5) + except TimeoutExpired: + proc.kill() diff --git a/tools/clip/run.py b/tools/clip/run.py index 7920751447..8fa0e8eda3 100755 --- a/tools/clip/run.py +++ b/tools/clip/run.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 -import atexit import logging import os import platform @@ -11,13 +10,14 @@ from argparse import ArgumentParser, ArgumentTypeError from collections.abc import Sequence from pathlib import Path from random import randint -from subprocess import Popen, PIPE +from subprocess import Popen from typing import Literal from cereal.messaging import SubMaster from openpilot.common.basedir import BASEDIR from openpilot.common.params import Params, UnknownKeyName from openpilot.common.prefix import OpenpilotPrefix +from openpilot.common.run import managed_proc from openpilot.tools.lib.route import Route from openpilot.tools.lib.logreader import LogReader @@ -38,22 +38,23 @@ UI = str(Path(BASEDIR, 'selfdrive/ui/ui').resolve()) logger = logging.getLogger('clip.py') -def check_for_failure(proc: Popen): - exit_code = proc.poll() - if exit_code is not None and exit_code != 0: - cmd = str(proc.args) - if isinstance(proc.args, str): - cmd = proc.args - elif isinstance(proc.args, Sequence): - cmd = str(proc.args[0]) - msg = f'{cmd} failed, exit code {exit_code}' - logger.error(msg) - stdout, stderr = proc.communicate() - if stdout: - logger.error(stdout.decode()) - if stderr: - logger.error(stderr.decode()) - raise ChildProcessError(msg) +def check_for_failure(procs: list[Popen]): + for proc in procs: + exit_code = proc.poll() + if exit_code is not None and exit_code != 0: + cmd = str(proc.args) + if isinstance(proc.args, str): + cmd = proc.args + elif isinstance(proc.args, Sequence): + cmd = str(proc.args[0]) + msg = f'{cmd} failed, exit code {exit_code}' + logger.error(msg) + stdout, stderr = proc.communicate() + if stdout: + logger.error(stdout.decode()) + if stderr: + logger.error(stderr.decode()) + raise ChildProcessError(msg) def escape_ffmpeg_text(value: str): @@ -137,10 +138,6 @@ def populate_car_params(lr: LogReader): logger.debug('persisted CarParams') -def start_proc(args: list[str], env: dict[str, str]): - return Popen(args, env=env, stdout=PIPE, stderr=PIPE) - - def validate_env(parser: ArgumentParser): if platform.system() not in ['Linux']: parser.exit(1, f'clip.py: error: {platform.system()} is not a supported operating system\n') @@ -176,8 +173,7 @@ def wait_for_frames(procs: list[Popen]): while no_frames_drawn: sm.update() no_frames_drawn = sm['uiDebug'].drawTimeMillis == 0. - for proc in procs: - check_for_failure(proc) + check_for_failure(procs) def clip( @@ -253,35 +249,22 @@ def clip( with OpenpilotPrefix(prefix, shared_download_cache=True): populate_car_params(lr) - env = os.environ.copy() env['DISPLAY'] = display - xvfb_proc = start_proc(xvfb_cmd, env) - atexit.register(lambda: xvfb_proc.terminate()) - ui_proc = start_proc(ui_cmd, env) - atexit.register(lambda: ui_proc.terminate()) - replay_proc = start_proc(replay_cmd, env) - atexit.register(lambda: replay_proc.terminate()) - procs = [replay_proc, ui_proc, xvfb_proc] - - logger.info('waiting for replay to begin (loading segments, may take a while)...') - wait_for_frames(procs) - - logger.debug(f'letting UI warm up ({SECONDS_TO_WARM}s)...') - time.sleep(SECONDS_TO_WARM) - for proc in procs: - check_for_failure(proc) - - ffmpeg_proc = start_proc(ffmpeg_cmd, env) - procs.append(ffmpeg_proc) - atexit.register(lambda: ffmpeg_proc.terminate()) - - logger.info(f'recording in progress ({duration}s)...') - ffmpeg_proc.wait(duration + PROC_WAIT_SECONDS) - for proc in procs: - check_for_failure(proc) - logger.info(f'recording complete: {Path(out).resolve()}') + with managed_proc(xvfb_cmd, env) as xvfb_proc, managed_proc(ui_cmd, env) as ui_proc, managed_proc(replay_cmd, env) as replay_proc: + procs = [xvfb_proc, ui_proc, replay_proc] + logger.info('waiting for replay to begin (loading segments, may take a while)...') + wait_for_frames(procs) + logger.debug(f'letting UI warm up ({SECONDS_TO_WARM}s)...') + time.sleep(SECONDS_TO_WARM) + check_for_failure(procs) + with managed_proc(ffmpeg_cmd, env) as ffmpeg_proc: + procs.append(ffmpeg_proc) + logger.info(f'recording in progress ({duration}s)...') + ffmpeg_proc.wait(duration + PROC_WAIT_SECONDS) + check_for_failure(procs) + logger.info(f'recording complete: {Path(out).resolve()}') def main(): @@ -319,9 +302,7 @@ def main(): logger.exception('interrupted by user', exc_info=e) except Exception as e: logger.exception('encountered error', exc_info=e) - finally: - atexit._run_exitfuncs() - sys.exit(exit_code) + sys.exit(exit_code) if __name__ == '__main__': From be934b3881ba136dbb32a4a9114cc92dfa5d0359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Wed, 13 Aug 2025 11:43:50 -0700 Subject: [PATCH 02/64] fancontroller: remove weird minus (#35983) * fancontroller: remove weird minus * another minus --- system/hardware/fan_controller.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/system/hardware/fan_controller.py b/system/hardware/fan_controller.py index 7d5bec0509..4c7adc0a3e 100755 --- a/system/hardware/fan_controller.py +++ b/system/hardware/fan_controller.py @@ -21,16 +21,16 @@ class TiciFanController(BaseFanController): self.controller = PIDController(k_p=0, k_i=4e-3, k_f=1, rate=(1 / DT_HW)) def update(self, cur_temp: float, ignition: bool) -> int: - self.controller.neg_limit = -(100 if ignition else 30) - self.controller.pos_limit = -(30 if ignition else 0) + self.controller.pos_limit = 100 if ignition else 30 + self.controller.neg_limit = 30 if ignition else 0 if ignition != self.last_ignition: self.controller.reset() - error = 75 - cur_temp - fan_pwr_out = -int(self.controller.update( + error = cur_temp - 75 + fan_pwr_out = int(self.controller.update( error=error, - feedforward=np.interp(cur_temp, [60.0, 100.0], [0, -100]) + feedforward=np.interp(cur_temp, [60.0, 100.0], [0, 100]) )) self.last_ignition = ignition From a2a385336e2e8e2d9ea468e9fd075ea94b1826d2 Mon Sep 17 00:00:00 2001 From: Alexandre Nobuharu Sato <66435071+AlexandreSato@users.noreply.github.com> Date: Wed, 13 Aug 2025 19:20:58 -0300 Subject: [PATCH 03/64] Multilang: update pt-BR translation (#35971) Multilang: update pt-BR translations --- selfdrive/ui/translations/main_pt-BR.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index e16872c758..403faf226f 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -504,7 +504,7 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. - + openpilot detectou atuação excessiva de %1 na sua última condução. Entre em contato com o suporte em https://comma.ai/support e compartilhe o Dongle ID do seu dispositivo para solução de problemas. @@ -1145,13 +1145,15 @@ Se quiser continuar, use https://flash.comma.ai para restaurar seu dispositivo a Record Audio Feedback with LKAS button - + Gravar feedback de áudio com o botão LKAS Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. Note that this feature is only compatible with select cars. - + Pressione o botão LKAS para gravar e compartilhar feedback de direção com a equipe do openpilot. Quando esta opção estiver desativada, o botão funcionará como um botão de marcador. O evento será destacado no comma connect e o segmento será preservado no armazenamento do seu dispositivo. + +Observe que este recurso é compatível apenas com alguns modelos de carros. From 3f830827b2e69fb25054018a5592b5c270ddba08 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Wed, 13 Aug 2025 16:07:12 -0700 Subject: [PATCH 04/64] setup: new flow (#35960) * start * remove * path * fix * prepare * url * format * better * better * consist * check * not real * ref * simpler * fix * fix * more * more * path * clean * line * progress * fast * no * ori * flag * remove * install * line * wait time * wait install * Revert "wait time" This reverts commit 14f750971c3d19b93e4609e9344cb3a8ce9175f4. * move * fix * install * universal service resources * fix * safer * this is stupid * time * cleaner * comment --- selfdrive/ui/installer/installer.cc | 46 ++++++++++++++------ system/ui/setup.py | 66 +++++++++++++++++++++++------ 2 files changed, 85 insertions(+), 27 deletions(-) diff --git a/selfdrive/ui/installer/installer.cc b/selfdrive/ui/installer/installer.cc index 7326e089ab..a9b84b5c06 100644 --- a/selfdrive/ui/installer/installer.cc +++ b/selfdrive/ui/installer/installer.cc @@ -24,11 +24,13 @@ const std::string BRANCH_STR = get_str(BRANCH "? #define GIT_SSH_URL "git@github.com:commaai/openpilot.git" #define CONTINUE_PATH "/data/continue.sh" -const std::string CACHE_PATH = "/data/openpilot.cache"; +const std::string INSTALL_PATH = "/data/openpilot"; +const std::string VALID_CACHE_PATH = "/data/.openpilot_cache"; -#define INSTALL_PATH "/data/openpilot" #define TMP_INSTALL_PATH "/data/tmppilot" +const int FONT_SIZE = 120; + extern const uint8_t str_continue[] asm("_binary_selfdrive_ui_installer_continue_openpilot_sh_start"); extern const uint8_t str_continue_end[] asm("_binary_selfdrive_ui_installer_continue_openpilot_sh_end"); extern const uint8_t inter_ttf[] asm("_binary_selfdrive_ui_installer_inter_ascii_ttf_start"); @@ -41,6 +43,16 @@ void run(const char* cmd) { assert(err == 0); } +void finishInstall() { + BeginDrawing(); + ClearBackground(BLACK); + const char *m = "Finishing install..."; + int text_width = MeasureText(m, FONT_SIZE); + DrawTextEx(font, m, (Vector2){(float)(GetScreenWidth() - text_width)/2 + FONT_SIZE, (float)(GetScreenHeight() - FONT_SIZE)/2}, FONT_SIZE, 0, WHITE); + EndDrawing(); + util::sleep_for(60 * 1000); +} + void renderProgress(int progress) { BeginDrawing(); ClearBackground(BLACK); @@ -62,11 +74,11 @@ int doInstall() { } // cleanup previous install attempts - run("rm -rf " TMP_INSTALL_PATH " " INSTALL_PATH); + run("rm -rf " TMP_INSTALL_PATH); // do the install - if (util::file_exists(CACHE_PATH)) { - return cachedFetch(CACHE_PATH); + if (util::file_exists(INSTALL_PATH) && util::file_exists(VALID_CACHE_PATH)) { + return cachedFetch(INSTALL_PATH); } else { return freshClone(); } @@ -135,7 +147,9 @@ void cloneFinished(int exitCode) { run("git submodule update --init"); // move into place - run("mv " TMP_INSTALL_PATH " " INSTALL_PATH); + run(("rm -f " + VALID_CACHE_PATH).c_str()); + run(("rm -rf " + INSTALL_PATH).c_str()); + run(util::string_format("mv %s %s", TMP_INSTALL_PATH, INSTALL_PATH.c_str()).c_str()); #ifdef INTERNAL run("mkdir -p /data/params/d/"); @@ -153,9 +167,9 @@ void cloneFinished(int exitCode) { param << value; param.close(); } - run("cd " INSTALL_PATH " && " + run(("cd " + INSTALL_PATH + " && " "git remote set-url origin --push " GIT_SSH_URL " && " - "git config --replace-all remote.origin.fetch \"+refs/heads/*:refs/remotes/origin/*\""); + "git config --replace-all remote.origin.fetch \"+refs/heads/*:refs/remotes/origin/*\"").c_str()); #endif // write continue.sh @@ -171,16 +185,22 @@ void cloneFinished(int exitCode) { run("mv /data/continue.sh.new " CONTINUE_PATH); // wait for the installed software's UI to take over - util::sleep_for(60 * 1000); + finishInstall(); } int main(int argc, char *argv[]) { InitWindow(2160, 1080, "Installer"); - font = LoadFontFromMemory(".ttf", inter_ttf, inter_ttf_end - inter_ttf, 120, NULL, 0); + font = LoadFontFromMemory(".ttf", inter_ttf, inter_ttf_end - inter_ttf, FONT_SIZE, NULL, 0); SetTextureFilter(font.texture, TEXTURE_FILTER_BILINEAR); - renderProgress(0); - int result = doInstall(); - cloneFinished(result); + + if (util::file_exists(CONTINUE_PATH)) { + finishInstall(); + } else { + renderProgress(0); + int result = doInstall(); + cloneFinished(result); + } + CloseWindow(); UnloadFont(font); return 0; diff --git a/system/ui/setup.py b/system/ui/setup.py index d675e868ff..72718b5d6d 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -6,9 +6,12 @@ import time import urllib.request from urllib.parse import urlparse from enum import IntEnum +import shutil + import pyray as rl from cereal import log +from openpilot.common.run import run_cmd from openpilot.system.hardware import HARDWARE from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.widgets import Widget @@ -30,6 +33,19 @@ BUTTON_SPACING = 50 OPENPILOT_URL = "https://openpilot.comma.ai" USER_AGENT = f"AGNOSSetup-{HARDWARE.get_os_version()}" +CONTINUE_PATH = "/data/continue.sh" +TMP_CONTINUE_PATH = "/data/continue.sh.new" +INSTALL_PATH = "/data/openpilot" +VALID_CACHE_PATH = "/data/.openpilot_cache" +INSTALLER_SOURCE_PATH = "/usr/comma/installer" +INSTALLER_DESTINATION_PATH = "/tmp/installer" +INSTALLER_URL_PATH = "/tmp/installer_url" + +CONTINUE = """#!/usr/bin/env bash + +cd /data/openpilot +exec ./launch_openpilot.sh +""" class SetupState(IntEnum): LOW_VOLTAGE = 0 @@ -136,21 +152,19 @@ class Setup(Widget): self.state = SetupState.SOFTWARE_SELECTION def _custom_software_warning_continue_button_callback(self): - self.state = SetupState.CUSTOM_SOFTWARE + self.state = SetupState.NETWORK_SETUP + self.stop_network_check_thread.clear() + self.start_network_check() def _getting_started_button_callback(self): - self.state = SetupState.NETWORK_SETUP - self.stop_network_check_thread.clear() - self.start_network_check() + self.state = SetupState.SOFTWARE_SELECTION def _software_selection_back_button_callback(self): - self.state = SetupState.NETWORK_SETUP - self.stop_network_check_thread.clear() - self.start_network_check() + self.state = SetupState.GETTING_STARTED def _software_selection_continue_button_callback(self): if self._software_selection_openpilot_button.selected: - self.download(OPENPILOT_URL) + self.use_openpilot() else: self.state = SetupState.CUSTOM_SOFTWARE_WARNING @@ -158,11 +172,14 @@ class Setup(Widget): self.state = SetupState.GETTING_STARTED def _network_setup_back_button_callback(self): - self.state = SetupState.GETTING_STARTED + self.state = SetupState.SOFTWARE_SELECTION def _network_setup_continue_button_callback(self): - self.state = SetupState.SOFTWARE_SELECTION self.stop_network_check_thread.set() + if self._software_selection_openpilot_button.selected: + self.download(OPENPILOT_URL) + else: + self.state = SetupState.CUSTOM_SOFTWARE def render_low_voltage(self, rect: rl.Rectangle): rl.draw_texture(self.warning, int(rect.x + 150), int(rect.y + 110), rl.WHITE) @@ -299,6 +316,23 @@ class Setup(Widget): self.keyboard.set_title("Enter URL", "for Custom Software") gui_app.set_modal_overlay(self.keyboard, callback=handle_keyboard_result) + def use_openpilot(self): + if os.path.isdir(INSTALL_PATH) and os.path.isfile(VALID_CACHE_PATH): + os.remove(VALID_CACHE_PATH) + with open(TMP_CONTINUE_PATH, "w") as f: + f.write(CONTINUE) + run_cmd(["chmod", "+x", TMP_CONTINUE_PATH]) + shutil.move(TMP_CONTINUE_PATH, CONTINUE_PATH) + shutil.copyfile(INSTALLER_SOURCE_PATH, INSTALLER_DESTINATION_PATH) + + # give time for installer UI to take over + time.sleep(1) + gui_app.request_close() + else: + self.state = SetupState.NETWORK_SETUP + self.stop_network_check_thread.clear() + self.start_network_check() + def download(self, url: str): # autocomplete incomplete URLs if re.match("^([^/.]+)/([^/]+)$", url): @@ -316,7 +350,7 @@ class Setup(Widget): try: import tempfile - _, tmpfile = tempfile.mkstemp(prefix="installer_") + fd, tmpfile = tempfile.mkstemp(prefix="installer_") headers = {"User-Agent": USER_AGENT, "X-openpilot-serial": HARDWARE.get_serial()} req = urllib.request.Request(self.download_url, headers=headers) @@ -346,12 +380,16 @@ class Setup(Widget): self.download_failed(self.download_url, "No custom software found at this URL.") return - os.rename(tmpfile, "/tmp/installer") - os.chmod("/tmp/installer", 0o755) + # AGNOS might try to execute the installer before this process exits. + # Therefore, important to close the fd before renaming the installer. + os.close(fd) + os.rename(tmpfile, INSTALLER_DESTINATION_PATH) - with open("/tmp/installer_url", "w") as f: + with open(INSTALLER_URL_PATH, "w") as f: f.write(self.download_url) + # give time for installer UI to take over + time.sleep(5) gui_app.request_close() except Exception: From 56a89eb4fb39aed92ecec771b0771d685ff9d42c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 13 Aug 2025 18:32:10 -0700 Subject: [PATCH 05/64] [bot] Update translations (#35975) Update translations Co-authored-by: Vehicle Researcher --- selfdrive/ui/translations/main_ar.ts | 105 ---------------------- selfdrive/ui/translations/main_de.ts | 105 ---------------------- selfdrive/ui/translations/main_es.ts | 107 ----------------------- selfdrive/ui/translations/main_fr.ts | 105 ---------------------- selfdrive/ui/translations/main_ja.ts | 107 ----------------------- selfdrive/ui/translations/main_ko.ts | 107 ----------------------- selfdrive/ui/translations/main_pt-BR.ts | 107 ----------------------- selfdrive/ui/translations/main_th.ts | 105 ---------------------- selfdrive/ui/translations/main_tr.ts | 105 ---------------------- selfdrive/ui/translations/main_zh-CHS.ts | 107 ----------------------- selfdrive/ui/translations/main_zh-CHT.ts | 107 ----------------------- 11 files changed, 1167 deletions(-) diff --git a/selfdrive/ui/translations/main_ar.ts b/selfdrive/ui/translations/main_ar.ts index eac3b561f7..ff2e7ab05c 100644 --- a/selfdrive/ui/translations/main_ar.ts +++ b/selfdrive/ui/translations/main_ar.ts @@ -700,111 +700,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp خرطوم الحريق - - Setup - - WARNING: Low Voltage - تحذير: الجهد منخفض - - - Power your device in a car with a harness or proceed at your own risk. - شغل جهازك في السيارة عن طريق شرطان التوصيل، أو تابع على مسؤوليتك. - - - Power off - إيقاف التشغيل - - - Continue - متابعة - - - Getting Started - البدء - - - Before we get on the road, let’s finish installation and cover some details. - قبل أن ننطلق في الطريق، دعنا ننتهي من التثبيت ونغطي بعض التفاصيل. - - - Connect to Wi-Fi - الاتصال بشبكة الواي فاي - - - Back - السابق - - - Continue without Wi-Fi - المتابعة بدون شبكة الواي فاي - - - Waiting for internet - بانتظار الاتصال بالإنترنت - - - Enter URL - أدخل رابط URL - - - for Custom Software - للبرامج المخصصة - - - Downloading... - يتم الآن التنزيل... - - - Download Failed - فشل التنزيل - - - Ensure the entered URL is valid, and the device’s internet connection is good. - تأكد من أن رابط URL الذي أدخلته صالح، وأن اتصال الجهاز بالإنترنت جيد. - - - Reboot device - إعادة التشغيل - - - Start over - البدء من جديد - - - Something went wrong. Reboot the device. - حدث خطأ ما. أعد التشغيل الجهاز. - - - No custom software found at this URL. - لم يتم العثور على برنامج خاص لعنوان URL ها. - - - Select a language - اختر لغة - - - Choose Software to Install - اختر البرنامج للتثبيت - - - openpilot - openpilot - - - Custom Software - البرمجيات المخصصة - - - WARNING: Custom Software - - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - - - SetupWidget diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index b91c0e23ce..52fe55720d 100644 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -680,111 +680,6 @@ Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximiere Firehose - - Setup - - WARNING: Low Voltage - Warnung: Batteriespannung niedrig - - - Power your device in a car with a harness or proceed at your own risk. - Versorge dein Gerät über einen Kabelbaum im Auto mit Strom, oder fahre auf eigene Gefahr fort. - - - Power off - Ausschalten - - - Continue - Fortsetzen - - - Getting Started - Loslegen - - - Before we get on the road, let’s finish installation and cover some details. - Bevor wir uns auf die Straße begeben, lass uns die Installation fertigstellen und einige Details prüfen. - - - Connect to Wi-Fi - Mit WLAN verbinden - - - Back - Zurück - - - Continue without Wi-Fi - Ohne WLAN fortsetzen - - - Waiting for internet - Auf Internet warten - - - Enter URL - URL eingeben - - - for Custom Software - für spezifische Software - - - Downloading... - Herunterladen... - - - Download Failed - Herunterladen fehlgeschlagen - - - Ensure the entered URL is valid, and the device’s internet connection is good. - Stelle sicher, dass die eingegebene URL korrekt ist und dein Gerät eine stabile Internetverbindung hat. - - - Reboot device - Gerät neustarten - - - Start over - Von neuem beginnen - - - No custom software found at this URL. - Keine benutzerdefinierte Software unter dieser URL gefunden. - - - Something went wrong. Reboot the device. - Etwas ist schiefgelaufen. Starte das Gerät neu. - - - Select a language - Sprache wählen - - - Choose Software to Install - Wähle die zu installierende Software - - - openpilot - openpilot - - - Custom Software - Benutzerdefinierte Software - - - WARNING: Custom Software - - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - - - SetupWidget diff --git a/selfdrive/ui/translations/main_es.ts b/selfdrive/ui/translations/main_es.ts index 79b10f316f..145eb7ae67 100644 --- a/selfdrive/ui/translations/main_es.ts +++ b/selfdrive/ui/translations/main_es.ts @@ -684,113 +684,6 @@ El Modo Firehose te permite maximizar las subidas de datos de entrenamiento para Firehose - - Setup - - Something went wrong. Reboot the device. - Algo ha ido mal. Reinicie el dispositivo. - - - Ensure the entered URL is valid, and the device’s internet connection is good. - Asegúrese de que la URL insertada es válida y que el dispositivo tiene buena conexión. - - - No custom software found at this URL. - No encontramos software personalizado en esta URL. - - - WARNING: Low Voltage - ALERTA: Voltaje bajo - - - Power your device in a car with a harness or proceed at your own risk. - Encienda su dispositivo en un auto con el arnés o proceda bajo su propio riesgo. - - - Power off - Apagar - - - Continue - Continuar - - - Getting Started - Comenzando - - - Before we get on the road, let’s finish installation and cover some details. - Antes de ponernos en marcha, terminemos la instalación y cubramos algunos detalles. - - - Connect to Wi-Fi - Conectarse al Wi-Fi - - - Back - Volver - - - Continue without Wi-Fi - Continuar sin Wi-Fi - - - Waiting for internet - Esperando conexión a internet - - - Choose Software to Install - Elija el software a instalar - - - openpilot - openpilot - - - Custom Software - Software personalizado - - - Enter URL - Insertar URL - - - for Custom Software - para Software personalizado - - - Downloading... - Descargando... - - - Download Failed - Descarga fallida - - - Reboot device - Reiniciar Dispositivo - - - Start over - Comenzar de nuevo - - - Select a language - Seleccione un idioma - - - WARNING: Custom Software - ADVERTENCIA: Software personalizado - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - Tenga cuidado al instalar software de terceros. El software de terceros no ha sido probado por comma y puede causar daños a su dispositivo y/o vehículo. - -Si desea continuar, utilice https://flash.comma.ai para restaurar su dispositivo a un estado de fábrica más tarde. - - SetupWidget diff --git a/selfdrive/ui/translations/main_fr.ts b/selfdrive/ui/translations/main_fr.ts index a4effb2a45..297d936139 100644 --- a/selfdrive/ui/translations/main_fr.ts +++ b/selfdrive/ui/translations/main_fr.ts @@ -678,111 +678,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp - - Setup - - Something went wrong. Reboot the device. - Un problème est survenu. Redémarrez l'appareil. - - - Ensure the entered URL is valid, and the device’s internet connection is good. - Assurez-vous que l'URL saisie est valide et que la connexion internet de l'appareil est bonne. - - - No custom software found at this URL. - Aucun logiciel personnalisé trouvé à cette URL. - - - WARNING: Low Voltage - ATTENTION : Tension faible - - - Power your device in a car with a harness or proceed at your own risk. - Alimentez votre appareil dans une voiture avec un harness ou continuez à vos risques et périls. - - - Power off - Éteindre - - - Continue - Continuer - - - Getting Started - Commencer - - - Before we get on the road, let’s finish installation and cover some details. - Avant de prendre la route, terminons l'installation et passons en revue quelques détails. - - - Connect to Wi-Fi - Se connecter au Wi-Fi - - - Back - Retour - - - Enter URL - Entrer l'URL - - - for Custom Software - pour logiciel personnalisé - - - Continue without Wi-Fi - Continuer sans Wi-Fi - - - Waiting for internet - En attente d'internet - - - Downloading... - Téléchargement... - - - Download Failed - Échec du téléchargement - - - Reboot device - Redémarrer l'appareil - - - Start over - Recommencer - - - Select a language - Choisir une langue - - - Choose Software to Install - Choisir le logiciel à installer - - - openpilot - openpilot - - - Custom Software - Logiciel personnalisé - - - WARNING: Custom Software - - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - - - SetupWidget diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index 8d0b2cb75b..7bb5c5364f 100644 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -679,113 +679,6 @@ Firehoseモードを有効にすると学習データを最大限アップロー データ学習 - - Setup - - WARNING: Low Voltage - 警告:電圧低下 - - - Power your device in a car with a harness or proceed at your own risk. - ハーネスを使って車でデバイスに電源を供給するか、自己責任でこのまま継続して下さい。 - - - Power off - 電源を切る - - - Continue - 続ける - - - Getting Started - はじめに - - - Before we get on the road, let’s finish installation and cover some details. - 出発する前に、インストールを完了させて少し詳細を確認しましょう。 - - - Connect to Wi-Fi - Wi-Fiに接続 - - - Back - 戻る - - - Continue without Wi-Fi - Wi-Fiに接続せずに続行 - - - Waiting for internet - インターネット接続を待機中 - - - Enter URL - URLの入力 - - - for Custom Software - カスタムソフトウェア - - - Downloading... - ダウンロード中... - - - Download Failed - ダウンロードに失敗しました - - - Ensure the entered URL is valid, and the device’s internet connection is good. - 入力されたURLが正しいかどうか、インターネットに正常に接続できているかを確認してください。 - - - Reboot device - デバイスを再起動 - - - Start over - やり直す - - - No custom software found at this URL. - このURLはカスタムソフトウェアではありません。 - - - Something went wrong. Reboot the device. - 何かの問題が発生しました。デバイスを再起動してください。 - - - Select a language - 言語を選択 - - - Choose Software to Install - インストールするソフトウェアを選択してください。 - - - openpilot - openpilot - - - Custom Software - カスタムソフトウェア - - - WARNING: Custom Software - 警告: カスタムソフトウェア - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - サードパーティ製ソフトウェアをインストールする際は注意してください。サードパーティ製ソフトウェアはcommaによってテストされておらず、あなたのデバイスや車両に損害を与える可能性があります。 - -続行したい場合は、後でデバイスを工場出荷時の状態に戻すために https://flash.comma.ai を使用してください。 - - SetupWidget diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index 3cffab10b2..807996f182 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -679,113 +679,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp 파이어호스 - - Setup - - WARNING: Low Voltage - 경고: 전압이 낮습니다 - - - Power your device in a car with a harness or proceed at your own risk. - 장치를 하네스를 통해 차량 전원에 연결하세요. USB 전원에서는 예상치 못한 문제가 생길 수 있습니다. - - - Power off - 전원 끄기 - - - Continue - 계속 - - - Getting Started - 시작하기 - - - Before we get on the road, let’s finish installation and cover some details. - 출발 전 설정을 완료하고 세부 사항을 살펴봅니다. - - - Connect to Wi-Fi - Wi-Fi 연결 - - - Back - 뒤로 - - - Continue without Wi-Fi - Wi-Fi 연결 없이 진행 - - - Waiting for internet - 인터넷 연결 대기 중 - - - Enter URL - URL 입력 - - - for Custom Software - 커스텀 소프트웨어 - - - Downloading... - 다운로드 중... - - - Download Failed - 다운로드 실패 - - - Ensure the entered URL is valid, and the device’s internet connection is good. - 입력된 URL이 유효하고 인터넷 연결이 원활한지 확인하세요. - - - Reboot device - 장치 재부팅 - - - Start over - 다시 시작 - - - Something went wrong. Reboot the device. - 문제가 발생했습니다. 장치를 재부팅하세요. - - - No custom software found at this URL. - 이 URL에서 커스텀 소프트웨어를 찾을 수 없습니다. - - - Select a language - 언어를 선택하세요 - - - Choose Software to Install - 설치할 소프트웨어 선택 - - - openpilot - 오픈파일럿 - - - Custom Software - 커스텀 소프트웨어 - - - WARNING: Custom Software - 경고: 커스텀 소프트웨어 - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - 타사 소프트웨어를 설치할 때는 주의하십시오. 타사 소프트웨어는 comma에 의해 테스트되지 않았으며 장치나 차량에 손상을 줄 수 있습니다. - -진행하려면 https://flash.comma.ai를 사용하여 나중에 장치를 공장 초기화하세요. - - SetupWidget diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index 403faf226f..7f41588e8f 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -684,113 +684,6 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar Firehose - - Setup - - WARNING: Low Voltage - ALERTA: Baixa Voltagem - - - Power your device in a car with a harness or proceed at your own risk. - Ligue seu dispositivo em um carro com um chicote ou prossiga por sua conta e risco. - - - Power off - Desligar - - - Continue - Continuar - - - Getting Started - Começando - - - Before we get on the road, let’s finish installation and cover some details. - Antes de pegarmos a estrada, vamos terminar a instalação e cobrir alguns detalhes. - - - Connect to Wi-Fi - Conectar ao Wi-Fi - - - Back - Voltar - - - Continue without Wi-Fi - Continuar sem Wi-Fi - - - Waiting for internet - Esperando pela internet - - - Enter URL - Preencher URL - - - for Custom Software - para o Software Customizado - - - Downloading... - Baixando... - - - Download Failed - Download Falhou - - - Ensure the entered URL is valid, and the device’s internet connection is good. - Garanta que a URL inserida é valida, e uma boa conexão à internet. - - - Reboot device - Reiniciar Dispositivo - - - Start over - Inicializar - - - No custom software found at this URL. - Não há software personalizado nesta URL. - - - Something went wrong. Reboot the device. - Algo deu errado. Reinicie o dispositivo. - - - Select a language - Selecione o Idioma - - - Choose Software to Install - Escolha o Software a ser Instalado - - - openpilot - openpilot - - - Custom Software - Software Customizado - - - WARNING: Custom Software - AVISO: Software Personalizado - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - Tenha cuidado ao instalar software de terceiros. Softwares de terceiros não foram testados pela comma e podem causar danos ao seu dispositivo e/ou veículo. - -Se quiser continuar, use https://flash.comma.ai para restaurar seu dispositivo ao estado de fábrica mais tarde. - - SetupWidget diff --git a/selfdrive/ui/translations/main_th.ts b/selfdrive/ui/translations/main_th.ts index 87cb8bc2e2..34824c2680 100644 --- a/selfdrive/ui/translations/main_th.ts +++ b/selfdrive/ui/translations/main_th.ts @@ -675,111 +675,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp สายยางดับเพลิง - - Setup - - WARNING: Low Voltage - คำเตือน: แรงดันแบตเตอรี่ต่ำ - - - Power your device in a car with a harness or proceed at your own risk. - โปรดต่ออุปกรณ์ของคุณเข้ากับสายควบคุมในรถยนต์ หรือดำเนินการด้วยความเสี่ยงของคุณเอง - - - Power off - ปิดเครื่อง - - - Continue - ดำเนินการต่อ - - - Getting Started - เริ่มกันเลย - - - Before we get on the road, let’s finish installation and cover some details. - ก่อนออกเดินทาง เรามาทำการติดตั้งซอฟต์แวร์ และตรวจสอบการตั้งค่า - - - Connect to Wi-Fi - เชื่อมต่อ Wi-Fi - - - Back - ย้อนกลับ - - - Continue without Wi-Fi - ดำเนินการต่อโดยไม่ใช้ Wi-Fi - - - Waiting for internet - กำลังรอสัญญาณอินเตอร์เน็ต - - - Enter URL - ป้อน URL - - - for Custom Software - สำหรับซอฟต์แวร์ที่กำหนดเอง - - - Downloading... - กำลังดาวน์โหลด... - - - Download Failed - ดาวน์โหลดล้มเหลว - - - Ensure the entered URL is valid, and the device’s internet connection is good. - ตรวจสอบให้แน่ใจว่า URL ที่ป้อนนั้นถูกต้อง และอุปกรณ์เชื่อมต่ออินเทอร์เน็ตอยู่ - - - Reboot device - รีบูตอุปกรณ์ - - - Start over - เริ่มต้นใหม่ - - - Something went wrong. Reboot the device. - มีบางอย่างผิดพลาด รีบูตอุปกรณ์ - - - No custom software found at this URL. - ไม่พบซอฟต์แวร์ที่กำหนดเองที่ URL นี้ - - - Select a language - เลือกภาษา - - - Choose Software to Install - เลือกซอฟต์แวร์ที่จะติดตั้ง - - - openpilot - openpilot - - - Custom Software - ซอฟต์แวร์ที่กำหนดเอง - - - WARNING: Custom Software - - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - - - SetupWidget diff --git a/selfdrive/ui/translations/main_tr.ts b/selfdrive/ui/translations/main_tr.ts index 6112698e31..db6c4283a8 100644 --- a/selfdrive/ui/translations/main_tr.ts +++ b/selfdrive/ui/translations/main_tr.ts @@ -672,111 +672,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp - - Setup - - WARNING: Low Voltage - UYARI: Düşük voltaj - - - Power your device in a car with a harness or proceed at your own risk. - Cihazınızı emniyet kemeri olan bir arabada çalıştırın veya riski kabul ederek devam edin. - - - Power off - Sistemi kapat - - - Continue - Devam et - - - Getting Started - Başlarken - - - Before we get on the road, let’s finish installation and cover some details. - Yola çıkmadan önce kurulumu bitirin ve bazı detayları gözden geçirin.. - - - Connect to Wi-Fi - Wi-Fi ile bağlan - - - Back - Geri - - - Continue without Wi-Fi - Wi-Fi bağlantısı olmadan devam edin - - - Waiting for internet - İnternet bağlantısı bekleniyor. - - - Enter URL - URL girin - - - for Custom Software - özel yazılım için - - - Downloading... - İndiriliyor... - - - Download Failed - İndirme başarısız. - - - Ensure the entered URL is valid, and the device’s internet connection is good. - Girilen URL nin geçerli olduğundan ve cihazın internet bağlantısının olduğunu kontrol edin - - - Reboot device - Cihazı yeniden başlat - - - Start over - Zacznij od początku - - - Something went wrong. Reboot the device. - - - - No custom software found at this URL. - - - - Select a language - Dil seçin - - - Choose Software to Install - - - - openpilot - openpilot - - - Custom Software - - - - WARNING: Custom Software - - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - - - SetupWidget diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index a1d1602373..fc4247d6a7 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -679,113 +679,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp Firehose - - Setup - - WARNING: Low Voltage - 警告:低电压 - - - Power your device in a car with a harness or proceed at your own risk. - 请使用car harness线束为您的设备供电,或自行承担风险。 - - - Power off - 关机 - - - Continue - 继续 - - - Getting Started - 开始设置 - - - Before we get on the road, let’s finish installation and cover some details. - 开始旅程之前,让我们完成安装并介绍一些细节。 - - - Connect to Wi-Fi - 连接到WiFi - - - Back - 返回 - - - Continue without Wi-Fi - 不连接WiFi并继续 - - - Waiting for internet - 等待网络连接 - - - Enter URL - 输入网址 - - - for Custom Software - 以下载自定义软件 - - - Downloading... - 正在下载…… - - - Download Failed - 下载失败 - - - Ensure the entered URL is valid, and the device’s internet connection is good. - 请确保互联网连接良好且输入的URL有效。 - - - Reboot device - 重启设备 - - - Start over - 重来 - - - No custom software found at this URL. - 在此网址找不到自定义软件。 - - - Something went wrong. Reboot the device. - 发生了一些错误。请重新启动您的设备。 - - - Select a language - 选择语言 - - - Choose Software to Install - 选择要安装的软件 - - - openpilot - openpilot - - - Custom Software - 定制软件 - - - WARNING: Custom Software - 警告:自定义软件 - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - 请谨慎安装第三方软件。第三方软件未经 comma 测试,可能会损害您的设备和车辆。 - -如果您决定继续,后续可通过 https://flash.comma.ai 将设备恢复到出厂状态。 - - SetupWidget diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index a094dd4182..bf1a739313 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -679,113 +679,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp Firehose - - Setup - - WARNING: Low Voltage - 警告:電壓過低 - - - Power your device in a car with a harness or proceed at your own risk. - 請使用車上 harness 提供的電源,若繼續的話您需要自擔風險。 - - - Power off - 關機 - - - Continue - 繼續 - - - Getting Started - 入門 - - - Before we get on the road, let’s finish installation and cover some details. - 在我們上路之前,讓我們完成安裝並介紹一些細節。 - - - Connect to Wi-Fi - 連接到無線網路 - - - Back - 回上頁 - - - Continue without Wi-Fi - 在沒有 Wi-Fi 的情況下繼續 - - - Waiting for internet - 連接至網路中 - - - Enter URL - 輸入網址 - - - for Custom Software - 訂製的軟體 - - - Downloading... - 下載中… - - - Download Failed - 下載失敗 - - - Ensure the entered URL is valid, and the device’s internet connection is good. - 請確定您輸入的是有效的安裝網址,並且確定裝置的網路連線狀態良好。 - - - Reboot device - 重新啟動 - - - Start over - 重新開始 - - - No custom software found at this URL. - 在此網址找不到自訂軟體。 - - - Something went wrong. Reboot the device. - 發生了一些錯誤。請重新啟動您的裝置。 - - - Select a language - 選擇語言 - - - Choose Software to Install - 選擇要安裝的軟體 - - - openpilot - openpilot - - - Custom Software - 自訂軟體 - - - WARNING: Custom Software - 警告:自訂軟體 - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - 請謹慎安裝第三方軟體。第三方軟體未經 comma 測試,可能會損壞您的裝置及車輛。 - -若您仍要繼續,日後可使用 https://flash.comma.ai 將您的裝置恢復至出廠狀態。 - - SetupWidget From 741ea44abaaa0824982e9fe522ac207c4c742483 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Wed, 13 Aug 2025 23:25:09 -0700 Subject: [PATCH 06/64] AGNOS 12.7 (#35988) * agnos12.7 * prod --- launch_env.sh | 2 +- system/hardware/tici/agnos.json | 12 ++++---- system/hardware/tici/all-partitions.json | 36 ++++++++++++------------ 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/launch_env.sh b/launch_env.sh index 0ed1395b37..e1a0da9b67 100755 --- a/launch_env.sh +++ b/launch_env.sh @@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1 export VECLIB_MAXIMUM_THREADS=1 if [ -z "$AGNOS_VERSION" ]; then - export AGNOS_VERSION="12.6" + export AGNOS_VERSION="12.7" fi export STAGING_ROOT="/data/safe_staging" diff --git a/system/hardware/tici/agnos.json b/system/hardware/tici/agnos.json index 7d99793a59..0900c51d10 100644 --- a/system/hardware/tici/agnos.json +++ b/system/hardware/tici/agnos.json @@ -67,17 +67,17 @@ }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643.img.xz", - "hash": "49faee0e9b084abf0ea46f87722e3366bbd0435fb6b25cce189295c1ff368da1", - "hash_raw": "18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643", + "url": "https://commadist.azureedge.net/agnosupdate/system-6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63.img.xz", + "hash": "e6fbc73b6ef9551f57f123791f94f2f72db8ce59e9fba8ccd44c30685582368b", + "hash_raw": "6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63", "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "db07761be0130e35a9d3ea6bec8df231260d3e767ae770850f18f10e14d0ab3f", + "ondevice_hash": "af2a42284ecfddc9d8aa50fde0e2093ba18cf1dd2242a7a3fbe05f78f6ec0228", "alt": { - "hash": "18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643", - "url": "https://commadist.azureedge.net/agnosupdate/system-18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643.img", + "hash": "6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63", + "url": "https://commadist.azureedge.net/agnosupdate/system-6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63.img", "size": 5368709120 } } diff --git a/system/hardware/tici/all-partitions.json b/system/hardware/tici/all-partitions.json index 8648544991..5d1bcc65a7 100644 --- a/system/hardware/tici/all-partitions.json +++ b/system/hardware/tici/all-partitions.json @@ -350,51 +350,51 @@ }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643.img.xz", - "hash": "49faee0e9b084abf0ea46f87722e3366bbd0435fb6b25cce189295c1ff368da1", - "hash_raw": "18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643", + "url": "https://commadist.azureedge.net/agnosupdate/system-6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63.img.xz", + "hash": "e6fbc73b6ef9551f57f123791f94f2f72db8ce59e9fba8ccd44c30685582368b", + "hash_raw": "6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63", "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "db07761be0130e35a9d3ea6bec8df231260d3e767ae770850f18f10e14d0ab3f", + "ondevice_hash": "af2a42284ecfddc9d8aa50fde0e2093ba18cf1dd2242a7a3fbe05f78f6ec0228", "alt": { - "hash": "18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643", - "url": "https://commadist.azureedge.net/agnosupdate/system-18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643.img", + "hash": "6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63", + "url": "https://commadist.azureedge.net/agnosupdate/system-6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63.img", "size": 5368709120 } }, { "name": "userdata_90", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-02f7abb4b667c04043c0c6950145aaebd704851261f32256d0f7e84a52059dda.img.xz", - "hash": "1eda66d4e31222fc2e792a62ae8e7d322fc643f0b23785e7527bb51a9fee97c7", - "hash_raw": "02f7abb4b667c04043c0c6950145aaebd704851261f32256d0f7e84a52059dda", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-ad38266b508280f22d02c3749322291eb083e08580ff8b7014add6989d290b12.img.xz", + "hash": "59e1bb2606b65293721dd89bcdc8d0c5cae47718c213eb40f235149aa5a408ae", + "hash_raw": "ad38266b508280f22d02c3749322291eb083e08580ff8b7014add6989d290b12", "size": 96636764160, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "679b650ee04b7b1ef610b63fde9b43569fded39ceacf88789b564de99c221ea1" + "ondevice_hash": "40b5e666ec137a863178b51a7947e3bb76fe9259584d84e6a66361fabced3da5" }, { "name": "userdata_89", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-bab8399bbe3968f3c496f7bc83c2541b33acc1f47814c4ad95801bf5cb7e7588.img.xz", - "hash": "e63d3277285aae1f04fd7f4f48429ce35010f4843ab755f10d360c3aa788e484", - "hash_raw": "bab8399bbe3968f3c496f7bc83c2541b33acc1f47814c4ad95801bf5cb7e7588", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-2f78f798861e0a3dd55af87e7a0c1982502a9293340d3eee6f3b8c9c88878e33.img.xz", + "hash": "e555a29c3ccb547ed840085c052cd1c6126c32d5c6dafe1712f593ae631be190", + "hash_raw": "2f78f798861e0a3dd55af87e7a0c1982502a9293340d3eee6f3b8c9c88878e33", "size": 95563022336, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "2947374fc5980ffe3c5b94b61cc1c81bc55214f494153ed234164801731f5dc0" + "ondevice_hash": "f5bc368dbe52ac7800634f74b1fd764fc3b6e68337984f40fd59222b1276d9f2" }, { "name": "userdata_30", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-22c874b4b66bbc000f3219abede8d62cb307f5786fd526a8473c61422765dea0.img.xz", - "hash": "12d9245711e8c49c51ff2c7b82d7301f2fcb1911edcddb35a105a80911859113", - "hash_raw": "22c874b4b66bbc000f3219abede8d62cb307f5786fd526a8473c61422765dea0", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-6367d73ba6a32823035c9941168ca3af3704c783f6bcd912f70afe1dbdfdd14b.img.xz", + "hash": "f5dfbe1dcba25b9a1920259bc89f52b5e406519539ff0112d5469fff3f1b6dba", + "hash_raw": "6367d73ba6a32823035c9941168ca3af3704c783f6bcd912f70afe1dbdfdd14b", "size": 32212254720, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "03c8b65c945207f887ed6c52d38b53d53d71c8597dcb0b63dfbb11f7cfff8d2b" + "ondevice_hash": "b8f447f0ea40faae7292d3a0dc380d194caab6ec3f5007eda5661f2aa4d2f4ab" } ] \ No newline at end of file From 349c0ec66237dad81176532788558217397d231e Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Thu, 14 Aug 2025 10:30:42 -0400 Subject: [PATCH 07/64] Honda: Add several new cars to release (#35989) * bump opendbc * regen CARS.md * update RELEASES.md --- RELEASES.md | 4 ++++ docs/CARS.md | 9 +++++++-- opendbc_repo | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 388ddae28b..2fadae9238 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -13,6 +13,10 @@ Version 0.10.0 (2025-08-05) * Enable live-learned steering actuation delay * Record driving feedback using LKAS button * Opt-in audio recording for dashcam video +* Acura MDX 2025 support thanks to vanillagorillaa and MVL! +* Honda Accord 2023-25 support thanks to vanillagorillaa and MVL! +* Honda CR-V 2023-25 support thanks to vanillagorillaa and MVL! +* Honda Pilot 2023-25 support thanks to vanillagorillaa and MVL! Version 0.9.9 (2025-05-23) ======================== diff --git a/docs/CARS.md b/docs/CARS.md index a0a4fd6bd7..fcb87236c8 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,12 +4,13 @@ A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified. -# 314 Supported Cars +# 319 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Hardware Needed
 |Video|Setup Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| |Acura|ILX 2016-18|Technology Plus Package or AcuraWatch Plus|openpilot|26 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Acura|ILX 2019|All|openpilot|26 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Acura|MDX 2025|All except Type S|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Acura|RDX 2016-18|AcuraWatch Plus or Advance Package|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Acura|RDX 2019-21|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| @@ -72,8 +73,9 @@ A supported vehicle is one that just works when you install a comma device. All |Genesis|GV80 2023[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai M connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |GMC|Sierra 1500 2020-21|Driver Alert Package II|openpilot available[1](#footnotes)|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Accord 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| -|Honda|Accord 2023|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Accord 2023-25|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Accord Hybrid 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Accord Hybrid 2023-25|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Civic 2019-21|All|openpilot available[1](#footnotes)|0 mph|2 mph[5](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Civic 2022-24|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| @@ -84,7 +86,9 @@ A supported vehicle is one that just works when you install a comma device. All |Honda|Civic Hybrid 2025|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|CR-V 2015-16|Touring Trim|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|CR-V 2017-22|Honda Sensing|openpilot available[1](#footnotes)|0 mph|15 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|CR-V 2023-25|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|CR-V Hybrid 2017-22|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|CR-V Hybrid 2023-25|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|e 2020|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Fit 2018-20|Honda Sensing|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Freed 2020|Honda Sensing|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| @@ -95,6 +99,7 @@ A supported vehicle is one that just works when you install a comma device. All |Honda|Odyssey 2018-20|Honda Sensing|openpilot|26 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Passport 2019-25|All|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Pilot 2016-22|Honda Sensing|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Pilot 2023-25|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch C connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Ridgeline 2017-25|Honda Sensing|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Hyundai|Azera 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Hyundai|Azera Hybrid 2019|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| diff --git a/opendbc_repo b/opendbc_repo index 040c6123fe..fa210c3fd8 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 040c6123fe524f138ae5490b93e229dd5de5e151 +Subproject commit fa210c3fd81273a06691b2c355814560804a072e From f2c806f8a0e420ba9cee95c0671cf7d78637d64a Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Thu, 14 Aug 2025 11:39:02 -0400 Subject: [PATCH 08/64] bump opendbc for fingerprint updates (#35990) bump opendbc --- opendbc_repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendbc_repo b/opendbc_repo index fa210c3fd8..3024c6fccb 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit fa210c3fd81273a06691b2c355814560804a072e +Subproject commit 3024c6fccbaea8f7b4760a3c295e7c8fb8e8b4a6 From a6d0a88b1ebb321fef8c5c6e96290123f60d9d4a Mon Sep 17 00:00:00 2001 From: eFini Date: Fri, 15 Aug 2025 01:27:44 +0800 Subject: [PATCH 09/64] Multilang: update chs/cht translations (#35981) --- selfdrive/ui/translations/main_zh-CHS.ts | 12 +++++++----- selfdrive/ui/translations/main_zh-CHT.ts | 12 +++++++----- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index fc4247d6a7..667d81ddd4 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -494,15 +494,15 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
Acknowledge Excessive Actuation - + 确认过度作动 Snooze Update - 暂停更新 + 推迟更新 openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. - + openpilot 在您上一次驾驶中,检测到过度的 %1 作动。请访问 https://comma.ai/support 联系客服,并提供您设备的 Dongle ID 以便进行故障排查。
@@ -1033,13 +1033,15 @@ Firehose Mode allows you to maximize your training data uploads to improve openp Record Audio Feedback with LKAS button - + 使用“车道保持”按钮录制音频反馈 Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. Note that this feature is only compatible with select cars. - + 按下“车道保持”按钮,即可录制并分享驾驶反馈给 openpilot 团队。当此开关禁用时,该按钮将用作书签按钮。该事件将在 comma connect 中高亮显示,且对应的视频片段将被保留在您的设备存储空间中。 + +请注意,此功能仅兼容部分车型。 diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index bf1a739313..a0f1997a00 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -494,15 +494,15 @@ Firehose Mode allows you to maximize your training data uploads to improve openp Acknowledge Excessive Actuation - + 確認過度作動 Snooze Update - 暫停更新 + 延後更新 openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. - + openpilot 在您上次的駕駛中,偵測到過度的 %1 作動。請至 https://comma.ai/support 聯絡客服,並提供您裝置的 Dongle ID 以進行故障排除。 @@ -1033,13 +1033,15 @@ Firehose Mode allows you to maximize your training data uploads to improve openp Record Audio Feedback with LKAS button - + 使用「車道維持」按鈕錄製音訊回饋 Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. Note that this feature is only compatible with select cars. - + 按下「車道維持」按鈕,即可錄製並分享駕駛回饋給 openpilot 團隊。當此開關停用時,該按鈕的功能將轉為書籤按鈕。該事件將會在 comma connect 中被標註,且對應的路段影像將保留在您的裝置儲存空間中。 + +請注意,此功能僅與特定車款相容。 From aa91a02db8278bb52eba64b47390ed5c341363db Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 14 Aug 2025 18:26:19 -0700 Subject: [PATCH 10/64] LogReader sourcing: check comma API source before CI source (#35992) sort --- tools/lib/logreader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index cc84c8b52e..6b0d9fa527 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -298,7 +298,7 @@ class LogReader: def __init__(self, identifier: str | list[str], default_mode: ReadMode = ReadMode.RLOG, sources: list[Source] = None, sort_by_time=False, only_union_types=False): if sources is None: - sources = [internal_source, openpilotci_source, comma_api_source, comma_car_segments_source] + sources = [internal_source, comma_api_source, openpilotci_source, comma_car_segments_source] self.default_mode = default_mode self.sources = sources From daef43f620edb431428fd8498eaf99be824a0dd1 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 14 Aug 2025 18:54:56 -0700 Subject: [PATCH 11/64] ci: show all tests durations (#35995) show --- .github/workflows/selfdrive_tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index fb082ae407..2d94a39260 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -27,7 +27,7 @@ env: RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c - PYTEST: pytest --continue-on-collection-errors --durations=0 --durations-min=5 -n logical + PYTEST: pytest --continue-on-collection-errors --durations=0 -n logical jobs: build_release: From 1eef956cadd30aa4fd5aca154ec7074f16a849df Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 14 Aug 2025 19:19:37 -0700 Subject: [PATCH 12/64] LogReader sourcing: return dict (#35994) * new return type * fix test * why not --- tools/lib/logreader.py | 33 ++++++++++++++++--------------- tools/lib/tests/test_logreader.py | 6 +++--- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index 6b0d9fa527..d15e473c41 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -141,7 +141,7 @@ class ReadMode(enum.StrEnum): LogPath = str | None LogFileName = tuple[str, ...] -Source = Callable[[SegmentRange, LogFileName], list[LogPath]] +Source = Callable[[SegmentRange, LogFileName], dict[int, LogPath]] InternalUnavailableException = Exception("Internal source not available") @@ -150,52 +150,53 @@ class LogsUnavailable(Exception): pass -def comma_api_source(sr: SegmentRange, fns: LogFileName) -> list[LogPath]: +def comma_api_source(sr: SegmentRange, fns: LogFileName) -> dict[int, LogPath]: route = Route(sr.route_name) # comma api will have already checked if the file exists if fns == FileName.RLOG: - return [route.log_paths()[seg] for seg in sr.seg_idxs] + return {seg: route.log_paths()[seg] for seg in sr.seg_idxs} else: - return [route.qlog_paths()[seg] for seg in sr.seg_idxs] + return {seg: route.qlog_paths()[seg] for seg in sr.seg_idxs} -def internal_source(sr: SegmentRange, fns: LogFileName, endpoint_url: str = DATA_ENDPOINT) -> list[LogPath]: +def internal_source(sr: SegmentRange, fns: LogFileName, endpoint_url: str = DATA_ENDPOINT) -> dict[int, LogPath]: if not internal_source_available(endpoint_url): raise InternalUnavailableException def get_internal_url(sr: SegmentRange, seg, file): return f"{endpoint_url.rstrip('/')}/{sr.dongle_id}/{sr.log_id}/{seg}/{file}" - return eval_source([[get_internal_url(sr, seg, fn) for fn in fns] for seg in sr.seg_idxs]) + return eval_source({seg: [get_internal_url(sr, seg, fn) for fn in fns] for seg in sr.seg_idxs}) -def openpilotci_source(sr: SegmentRange, fns: LogFileName) -> list[LogPath]: - return eval_source([[get_url(sr.route_name, seg, fn) for fn in fns] for seg in sr.seg_idxs]) +def openpilotci_source(sr: SegmentRange, fns: LogFileName) -> dict[int, LogPath]: + return eval_source({seg: [get_url(sr.route_name, seg, fn) for fn in fns] for seg in sr.seg_idxs}) -def comma_car_segments_source(sr: SegmentRange, fns: LogFileName) -> list[LogPath]: - return eval_source([get_comma_segments_url(sr.route_name, seg) for seg in sr.seg_idxs]) +def comma_car_segments_source(sr: SegmentRange, fns: LogFileName) -> dict[int, LogPath]: + return eval_source({seg: get_comma_segments_url(sr.route_name, seg) for seg in sr.seg_idxs}) def direct_source(file_or_url: str) -> list[str]: return [file_or_url] -def eval_source(files: list[list[str] | str]) -> list[LogPath]: +def eval_source(files: dict[int, list[str] | str]) -> dict[int, LogPath]: # Returns valid file URLs given a list of possible file URLs for each segment (e.g. rlog.bz2, rlog.zst) - valid_files: list[LogPath] = [] + valid_files: dict[int, LogPath] = {} - for urls in files: + for seg_idx, urls in files.items(): if isinstance(urls, str): urls = [urls] + # Add first valid file URL or None for url in urls: if file_exists(url): - valid_files.append(url) + valid_files[seg_idx] = url break else: - valid_files.append(None) + valid_files[seg_idx] = None return valid_files @@ -227,7 +228,7 @@ def auto_source(identifier: str, sources: list[Source], default_mode: ReadMode) assert len(files) == len(valid_files) or len(valid_files) == 0, f"Source {source.__name__} returned unexpected number of files" # Build a dict of valid files - for idx, f in enumerate(files): + for idx, f in files.items(): if valid_files.get(idx) is None: valid_files[idx] = f diff --git a/tools/lib/tests/test_logreader.py b/tools/lib/tests/test_logreader.py index 11bdd33ccf..7a70ad6aab 100644 --- a/tools/lib/tests/test_logreader.py +++ b/tools/lib/tests/test_logreader.py @@ -36,12 +36,12 @@ def setup_source_scenario(mocker, is_internal=False): comma_api_source_mock.__name__ = comma_api_source_mock._mock_name if is_internal: - internal_source_mock.return_value = [QLOG_FILE] + internal_source_mock.return_value = {3: QLOG_FILE} else: internal_source_mock.side_effect = InternalUnavailableException - openpilotci_source_mock.return_value = [None] - comma_api_source_mock.return_value = [QLOG_FILE] + openpilotci_source_mock.return_value = {3: None} + comma_api_source_mock.return_value = {3: QLOG_FILE} yield From 8ec61991ee35bb0cda07c02a518c469e6502a31c Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 14 Aug 2025 19:28:37 -0700 Subject: [PATCH 13/64] LogReader sourcing: remove redundant file existence checks (#35991) * speed up sourcing but avoiding checking for existence of collected files already from previous sources * clean up * been meaning to make them return dicts * no longer true * no longer true * clean up * more * more * revert --- tools/lib/logreader.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index d15e473c41..bedafb8566 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -141,7 +141,7 @@ class ReadMode(enum.StrEnum): LogPath = str | None LogFileName = tuple[str, ...] -Source = Callable[[SegmentRange, LogFileName], dict[int, LogPath]] +Source = Callable[[SegmentRange, list[int], LogFileName], dict[int, LogPath]] InternalUnavailableException = Exception("Internal source not available") @@ -150,32 +150,32 @@ class LogsUnavailable(Exception): pass -def comma_api_source(sr: SegmentRange, fns: LogFileName) -> dict[int, LogPath]: +def comma_api_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, LogPath]: route = Route(sr.route_name) # comma api will have already checked if the file exists if fns == FileName.RLOG: - return {seg: route.log_paths()[seg] for seg in sr.seg_idxs} + return {seg: route.log_paths()[seg] for seg in seg_idxs} else: - return {seg: route.qlog_paths()[seg] for seg in sr.seg_idxs} + return {seg: route.qlog_paths()[seg] for seg in seg_idxs} -def internal_source(sr: SegmentRange, fns: LogFileName, endpoint_url: str = DATA_ENDPOINT) -> dict[int, LogPath]: +def internal_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName, endpoint_url: str = DATA_ENDPOINT) -> dict[int, LogPath]: if not internal_source_available(endpoint_url): raise InternalUnavailableException def get_internal_url(sr: SegmentRange, seg, file): return f"{endpoint_url.rstrip('/')}/{sr.dongle_id}/{sr.log_id}/{seg}/{file}" - return eval_source({seg: [get_internal_url(sr, seg, fn) for fn in fns] for seg in sr.seg_idxs}) + return eval_source({seg: [get_internal_url(sr, seg, fn) for fn in fns] for seg in seg_idxs}) -def openpilotci_source(sr: SegmentRange, fns: LogFileName) -> dict[int, LogPath]: - return eval_source({seg: [get_url(sr.route_name, seg, fn) for fn in fns] for seg in sr.seg_idxs}) +def openpilotci_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, LogPath]: + return eval_source({seg: [get_url(sr.route_name, seg, fn) for fn in fns] for seg in seg_idxs}) -def comma_car_segments_source(sr: SegmentRange, fns: LogFileName) -> dict[int, LogPath]: - return eval_source({seg: get_comma_segments_url(sr.route_name, seg) for seg in sr.seg_idxs}) +def comma_car_segments_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, LogPath]: + return eval_source({seg: get_comma_segments_url(sr.route_name, seg) for seg in seg_idxs}) def direct_source(file_or_url: str) -> list[str]: @@ -205,8 +205,9 @@ def auto_source(identifier: str, sources: list[Source], default_mode: ReadMode) exceptions = {} sr = SegmentRange(identifier) - mode = default_mode if sr.selector is None else ReadMode(sr.selector) + needed_seg_idxs = sr.seg_idxs + mode = default_mode if sr.selector is None else ReadMode(sr.selector) if mode == ReadMode.QLOG: try_fns = [FileName.QLOG] else: @@ -222,16 +223,16 @@ def auto_source(identifier: str, sources: list[Source], default_mode: ReadMode) for fn in try_fns: for source in sources: try: - files = source(sr, fn) - - # Check every source returns an expected number of files - assert len(files) == len(valid_files) or len(valid_files) == 0, f"Source {source.__name__} returned unexpected number of files" + files = source(sr, needed_seg_idxs, fn) # Build a dict of valid files for idx, f in files.items(): if valid_files.get(idx) is None: valid_files[idx] = f + # Don't check for segment files that have already been found + needed_seg_idxs = [idx for idx in needed_seg_idxs if valid_files.get(idx) is None] + # We've found all files, return them if all(f is not None for f in valid_files.values()): return cast(list[str], list(valid_files.values())) From 7c6bc7031205409a8ee93b590b6971f87ffde7e2 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 14 Aug 2025 20:14:12 -0700 Subject: [PATCH 14/64] params: fix default boolean params (#35997) * fix * update test --- system/manager/manager.py | 2 +- system/manager/test/test_manager.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/system/manager/manager.py b/system/manager/manager.py index bd8e552dd3..36055d8635 100755 --- a/system/manager/manager.py +++ b/system/manager/manager.py @@ -40,7 +40,7 @@ def manager_init() -> None: # set unset params to their default value for k in params.all_keys(): default_value = params.get_default_value(k) - if default_value and params.get(k) is None: + if default_value is not None and params.get(k) is None: params.put(k, default_value) # Create folders needed for msgq diff --git a/system/manager/test/test_manager.py b/system/manager/test/test_manager.py index 5e55648283..34d07c6724 100644 --- a/system/manager/test/test_manager.py +++ b/system/manager/test/test_manager.py @@ -46,9 +46,10 @@ class TestManager: manager.main() for k in params.all_keys(): default_value = params.get_default_value(k) - if default_value: + if default_value is not None: assert params.get(k) == default_value assert params.get("OpenpilotEnabledToggle") + assert params.get("RouteCount") == 0 @pytest.mark.skip("this test is flaky the way it's currently written, should be moved to test_onroad") def test_clean_exit(self, subtests): From 385ad9e8397122da8eff77279db6e84365a35693 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 14 Aug 2025 21:38:27 -0700 Subject: [PATCH 15/64] updated: connectivity check with new setup (#35998) * default * fix --- common/params_keys.h | 4 ++-- system/updated/updated.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common/params_keys.h b/common/params_keys.h index 3d7281bf93..c54600462c 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -73,9 +73,9 @@ inline static std::unordered_map keys = { {"LastOffroadStatusPacket", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, JSON}}, {"LastPowerDropDetected", {CLEAR_ON_MANAGER_START, STRING}}, {"LastUpdateException", {CLEAR_ON_MANAGER_START, STRING}}, - {"LastUpdateRouteCount", {PERSISTENT, INT}}, + {"LastUpdateRouteCount", {PERSISTENT, INT, "0"}}, {"LastUpdateTime", {PERSISTENT, TIME}}, - {"LastUpdateUptimeOnroad", {PERSISTENT, FLOAT}}, + {"LastUpdateUptimeOnroad", {PERSISTENT, FLOAT, "0.0"}}, {"LiveDelay", {PERSISTENT, BYTES}}, {"LiveParameters", {PERSISTENT, JSON}}, {"LiveParametersV2", {PERSISTENT, BYTES}}, diff --git a/system/updated/updated.py b/system/updated/updated.py index 1fd9e8b717..72f9de7d01 100755 --- a/system/updated/updated.py +++ b/system/updated/updated.py @@ -283,8 +283,8 @@ class Updater: self.params.put("LastUpdateUptimeOnroad", last_uptime_onroad) self.params.put("LastUpdateRouteCount", last_route_count) else: - last_uptime_onroad = self.params.get("LastUpdateUptimeOnroad") or last_uptime_onroad - last_route_count = self.params.get("LastUpdateRouteCount") or last_route_count + last_uptime_onroad = self.params.get("LastUpdateUptimeOnroad", return_default=True) + last_route_count = self.params.get("LastUpdateRouteCount", return_default=True) if exception is None: self.params.remove("LastUpdateException") From b54d5997de76a34123f8a0b979f926314564443d Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 14 Aug 2025 21:59:25 -0700 Subject: [PATCH 16/64] Update RELEASES.md --- RELEASES.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 2fadae9238..e1c64efad4 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -5,11 +5,10 @@ Version 0.10.0 (2025-08-05) ======================== * New driving model * New training architecture - * Architecture outlined in CVPR paper: "Learning to Drive from a World Model" - * Longitudinal MPC replaced by E2E planning from worldmodel in experimental mode - * Action from lateral MPC as training objective replaced by E2E planning from worldmodel + * Described in our CVPR paper: "Learning to Drive from a World Model" + * Longitudinal MPC replaced by E2E planning from World Model in Experimental Mode + * Action from lateral MPC as training objective replaced by E2E planning from World Model * Low-speed lead car ground-truth fixes - * Enable live-learned steering actuation delay * Record driving feedback using LKAS button * Opt-in audio recording for dashcam video From 4536719353fc9eeb4b9c57692cd72766a2f86397 Mon Sep 17 00:00:00 2001 From: eFini Date: Sat, 16 Aug 2025 00:02:38 +0800 Subject: [PATCH 17/64] longitudinal_planner: Convert self.mode to a local variable in update() (#35999) Make 'mode' variable local --- selfdrive/controls/lib/longitudinal_planner.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py index 605d956b8e..34fc85f8a5 100755 --- a/selfdrive/controls/lib/longitudinal_planner.py +++ b/selfdrive/controls/lib/longitudinal_planner.py @@ -90,7 +90,7 @@ class LongitudinalPlanner: return x, v, a, j, throttle_prob def update(self, sm): - self.mode = 'blended' if sm['selfdriveState'].experimentalMode else 'acc' + mode = 'blended' if sm['selfdriveState'].experimentalMode else 'acc' if len(sm['carControl'].orientationNED) == 3: accel_coast = get_coast_accel(sm['carControl'].orientationNED[1]) @@ -113,7 +113,7 @@ class LongitudinalPlanner: # No change cost when user is controlling the speed, or when standstill prev_accel_constraint = not (reset_state or sm['carState'].standstill) - if self.mode == 'acc': + if mode == 'acc': accel_clip = [ACCEL_MIN, get_max_accel(v_ego)] steer_angle_without_offset = sm['carState'].steeringAngleDeg - sm['liveParameters'].angleOffsetDeg accel_clip = limit_accel_in_turns(v_ego, steer_angle_without_offset, accel_clip, self.CP) @@ -163,7 +163,7 @@ class LongitudinalPlanner: output_a_target_e2e = sm['modelV2'].action.desiredAcceleration output_should_stop_e2e = sm['modelV2'].action.shouldStop - if self.mode == 'acc': + if mode == 'acc': output_a_target = output_a_target_mpc self.output_should_stop = output_should_stop_mpc else: From 5417efaa1dd83c3126176d87d40f32c1e750c44a Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 15 Aug 2025 11:12:18 -0700 Subject: [PATCH 18/64] bump opendbc (#36001) --- opendbc_repo | 2 +- selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index 3024c6fccb..18f8c0e757 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 3024c6fccbaea8f7b4760a3c295e7c8fb8e8b4a6 +Subproject commit 18f8c0e757906c779a16b70817b2c0fb5020f406 diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 54ff189358..dd1f0b6a06 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -543bd2347fa35f8300478a3893fdd0a03a7c1fe6 \ No newline at end of file +4536719353fc9eeb4b9c57692cd72766a2f86397 \ No newline at end of file From 1805a47139238a9e8c7212f740f2d46321536ab6 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 15 Aug 2025 11:13:45 -0700 Subject: [PATCH 19/64] USB takes forever to come up... --- selfdrive/pandad/pandad.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/pandad/pandad.py b/selfdrive/pandad/pandad.py index b33ffb6473..4e49813f5d 100755 --- a/selfdrive/pandad/pandad.py +++ b/selfdrive/pandad/pandad.py @@ -87,7 +87,7 @@ def main() -> None: # TODO: remove this in the next AGNOS # wait until USB is up before counting - if time.monotonic() < 35.: + if time.monotonic() < 60.: no_internal_panda_count = 0 # Handle missing internal panda From ab44c9a4ff905db21c77c5328323653767650275 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 15 Aug 2025 11:39:56 -0700 Subject: [PATCH 20/64] Torque controller: refactor calculations to be in accel space (#35790) * clean up * little confusing but works * clean up * fix * pid outputs torque again, fix windup above max torque * clean up * fix * fix * typo * fix conflicts * fix PID * cleanups * seems correct * updte * inverse * whitespace * move * small cleanup * more cleanup * update ref --------- Co-authored-by: Bruce Wayne --- common/pid.py | 7 +++-- opendbc_repo | 2 +- selfdrive/controls/lib/latcontrol_torque.py | 28 +++++++++++-------- selfdrive/controls/lib/tests/__init__.py | 0 .../{lib => }/tests/test_latcontrol.py | 4 ++- selfdrive/test/process_replay/ref_commit | 2 +- 6 files changed, 26 insertions(+), 17 deletions(-) delete mode 100644 selfdrive/controls/lib/tests/__init__.py rename selfdrive/controls/{lib => }/tests/test_latcontrol.py (88%) diff --git a/common/pid.py b/common/pid.py index 721a7c9d65..99142280ca 100644 --- a/common/pid.py +++ b/common/pid.py @@ -14,8 +14,7 @@ class PIDController: if isinstance(self._k_d, Number): self._k_d = [[0], [self._k_d]] - self.pos_limit = pos_limit - self.neg_limit = neg_limit + self.set_limits(pos_limit, neg_limit) self.i_rate = 1.0 / rate self.speed = 0.0 @@ -41,6 +40,10 @@ class PIDController: self.f = 0.0 self.control = 0 + def set_limits(self, pos_limit, neg_limit): + self.pos_limit = pos_limit + self.neg_limit = neg_limit + def update(self, error, error_rate=0.0, speed=0.0, feedforward=0., freeze_integrator=False): self.speed = speed self.p = float(error) * self.k_p diff --git a/opendbc_repo b/opendbc_repo index 18f8c0e757..74bfaa2c75 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 18f8c0e757906c779a16b70817b2c0fb5020f406 +Subproject commit 74bfaa2c750f2eca584dd6b58c1b862ba59d29d8 diff --git a/selfdrive/controls/lib/latcontrol_torque.py b/selfdrive/controls/lib/latcontrol_torque.py index c368a5da43..dffe85c473 100644 --- a/selfdrive/controls/lib/latcontrol_torque.py +++ b/selfdrive/controls/lib/latcontrol_torque.py @@ -3,7 +3,6 @@ import numpy as np from cereal import log from opendbc.car.lateral import FRICTION_THRESHOLD, get_friction -from opendbc.car.interfaces import LatControlInputs from openpilot.common.constants import ACCELERATION_DUE_TO_GRAVITY from openpilot.selfdrive.controls.lib.latcontrol import LatControl from openpilot.common.pid import PIDController @@ -27,15 +26,22 @@ class LatControlTorque(LatControl): def __init__(self, CP, CI): super().__init__(CP, CI) self.torque_params = CP.lateralTuning.torque.as_builder() - self.pid = PIDController(self.torque_params.kp, self.torque_params.ki, - k_f=self.torque_params.kf, pos_limit=self.steer_max, neg_limit=-self.steer_max) self.torque_from_lateral_accel = CI.torque_from_lateral_accel() + self.lateral_accel_from_torque = CI.lateral_accel_from_torque() + self.pid = PIDController(self.torque_params.kp, self.torque_params.ki, + k_f=self.torque_params.kf) + self.update_limits() self.steering_angle_deadzone_deg = self.torque_params.steeringAngleDeadzoneDeg def update_live_torque_params(self, latAccelFactor, latAccelOffset, friction): self.torque_params.latAccelFactor = latAccelFactor self.torque_params.latAccelOffset = latAccelOffset self.torque_params.friction = friction + self.update_limits() + + def update_limits(self): + self.pid.set_limits(self.lateral_accel_from_torque(self.steer_max, self.torque_params), + self.lateral_accel_from_torque(-self.steer_max, self.torque_params)) def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited): pid_log = log.ControlsState.LateralTorqueState.new_message() @@ -57,27 +63,25 @@ class LatControlTorque(LatControl): setpoint = desired_lateral_accel + low_speed_factor * desired_curvature measurement = actual_lateral_accel + low_speed_factor * actual_curvature gravity_adjusted_lateral_accel = desired_lateral_accel - roll_compensation - torque_from_setpoint = self.torque_from_lateral_accel(LatControlInputs(setpoint, roll_compensation, CS.vEgo, CS.aEgo), self.torque_params, - gravity_adjusted=False) - torque_from_measurement = self.torque_from_lateral_accel(LatControlInputs(measurement, roll_compensation, CS.vEgo, CS.aEgo), self.torque_params, - gravity_adjusted=False) - pid_log.error = float(torque_from_setpoint - torque_from_measurement) - ff = self.torque_from_lateral_accel(LatControlInputs(gravity_adjusted_lateral_accel, roll_compensation, CS.vEgo, CS.aEgo), self.torque_params, - gravity_adjusted=True) + + # do error correction in lateral acceleration space, convert at end to handle non-linear torque responses correctly + pid_log.error = float(setpoint - measurement) + ff = gravity_adjusted_lateral_accel ff += get_friction(desired_lateral_accel - actual_lateral_accel, lateral_accel_deadzone, FRICTION_THRESHOLD, self.torque_params) freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5 - output_torque = self.pid.update(pid_log.error, + output_lataccel = self.pid.update(pid_log.error, feedforward=ff, speed=CS.vEgo, freeze_integrator=freeze_integrator) + output_torque = self.torque_from_lateral_accel(output_lataccel, self.torque_params) pid_log.active = True pid_log.p = float(self.pid.p) pid_log.i = float(self.pid.i) pid_log.d = float(self.pid.d) pid_log.f = float(self.pid.f) - pid_log.output = float(-output_torque) + pid_log.output = float(-output_torque) # TODO: log lat accel? pid_log.actualLateralAccel = float(actual_lateral_accel) pid_log.desiredLateralAccel = float(desired_lateral_accel) pid_log.saturated = bool(self._check_saturation(self.steer_max - abs(output_torque) < 1e-3, CS, steer_limited_by_safety, curvature_limited)) diff --git a/selfdrive/controls/lib/tests/__init__.py b/selfdrive/controls/lib/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/selfdrive/controls/lib/tests/test_latcontrol.py b/selfdrive/controls/tests/test_latcontrol.py similarity index 88% rename from selfdrive/controls/lib/tests/test_latcontrol.py rename to selfdrive/controls/tests/test_latcontrol.py index f32e20b1e5..0ce06dc996 100644 --- a/selfdrive/controls/lib/tests/test_latcontrol.py +++ b/selfdrive/controls/tests/test_latcontrol.py @@ -5,6 +5,7 @@ from opendbc.car.car_helpers import interfaces from opendbc.car.honda.values import CAR as HONDA from opendbc.car.toyota.values import CAR as TOYOTA from opendbc.car.nissan.values import CAR as NISSAN +from opendbc.car.gm.values import CAR as GM from opendbc.car.vehicle_model import VehicleModel from openpilot.selfdrive.controls.lib.latcontrol_pid import LatControlPID from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque @@ -13,7 +14,8 @@ from openpilot.selfdrive.controls.lib.latcontrol_angle import LatControlAngle class TestLatControl: - @parameterized.expand([(HONDA.HONDA_CIVIC, LatControlPID), (TOYOTA.TOYOTA_RAV4, LatControlTorque), (NISSAN.NISSAN_LEAF, LatControlAngle)]) + @parameterized.expand([(HONDA.HONDA_CIVIC, LatControlPID), (TOYOTA.TOYOTA_RAV4, LatControlTorque), + (NISSAN.NISSAN_LEAF, LatControlAngle), (GM.CHEVROLET_BOLT_EUV, LatControlTorque)]) def test_saturation(self, car_name, controller): CarInterface = interfaces[car_name] CP = CarInterface.get_non_essential_params(car_name) diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index dd1f0b6a06..a4297096c0 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -4536719353fc9eeb4b9c57692cd72766a2f86397 \ No newline at end of file +6d3219bca9f66a229b38a5382d301a92b0147edb \ No newline at end of file From 372682d4a90530615368ca68cf2cb5cb2f63d99b Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Fri, 15 Aug 2025 14:46:20 -0700 Subject: [PATCH 21/64] updated: branch migration (#35993) * release * Update system/updated/updated.py Co-authored-by: Adeeb Shihadeh --------- Co-authored-by: Adeeb Shihadeh --- Jenkinsfile | 2 +- system/updated/updated.py | 3 +++ system/version.py | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index a14bf59299..0905abd6da 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -167,7 +167,7 @@ node { env.GIT_COMMIT = checkout(scm).GIT_COMMIT def excludeBranches = ['__nightly', 'devel', 'devel-staging', 'release3', 'release3-staging', - 'testing-closet*', 'hotfix-*'] + 'release-tici', 'testing-closet*', 'hotfix-*'] def excludeRegex = excludeBranches.join('|').replaceAll('\\*', '.*') if (env.BRANCH_NAME != 'master' && !env.BRANCH_NAME.contains('__jenkins_loop_')) { diff --git a/system/updated/updated.py b/system/updated/updated.py index 72f9de7d01..11928bc24c 100755 --- a/system/updated/updated.py +++ b/system/updated/updated.py @@ -242,6 +242,9 @@ class Updater: b: str | None = self.params.get("UpdaterTargetBranch") if b is None: b = self.get_branch(BASEDIR) + b = { + ("tici", "release3"): "release-tici" + }.get((HARDWARE.get_device_type(), b), b) return b @property diff --git a/system/version.py b/system/version.py index cd0e2d7f38..5e4fcf5c33 100755 --- a/system/version.py +++ b/system/version.py @@ -10,7 +10,7 @@ from openpilot.common.basedir import BASEDIR from openpilot.common.swaglog import cloudlog from openpilot.common.git import get_commit, get_origin, get_branch, get_short_branch, get_commit_date -RELEASE_BRANCHES = ['release3-staging', 'release3', 'nightly'] +RELEASE_BRANCHES = ['release3-staging', 'release3', 'release-tici', 'nightly'] TESTED_BRANCHES = RELEASE_BRANCHES + ['devel', 'devel-staging', 'nightly-dev'] BUILD_METADATA_FILENAME = "build.json" From 6a67f9e56fd1fbcfbb1ff1701df2a2b02f9805a8 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Fri, 15 Aug 2025 23:10:47 -0700 Subject: [PATCH 22/64] setup: custom software warning (#36003) * warn * msg * label * space * Revert "space" This reverts commit ae9b8ad1149612c5741ae3b091740170238473ed. --- system/ui/setup.py | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/system/ui/setup.py b/system/ui/setup.py index 72718b5d6d..800ca7662c 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -13,6 +13,7 @@ import pyray as rl from cereal import log from openpilot.common.run import run_cmd from openpilot.system.hardware import HARDWARE +from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.button import Button, ButtonStyle, ButtonRadio @@ -109,14 +110,21 @@ class Setup(Widget): self._network_setup_continue_button.set_enabled(False) self._network_setup_title_label = Label("Connect to Wi-Fi", TITLE_FONT_SIZE, FontWeight.BOLD, TextAlignment.LEFT) - self._custom_software_warning_continue_button = Button("Continue", self._custom_software_warning_continue_button_callback) + self._custom_software_warning_continue_button = Button("Scroll to continue", self._custom_software_warning_continue_button_callback, + button_style=ButtonStyle.PRIMARY) + self._custom_software_warning_continue_button.set_enabled(False) self._custom_software_warning_back_button = Button("Back", self._custom_software_warning_back_button_callback) self._custom_software_warning_title_label = Label("WARNING: Custom Software", 100, FontWeight.BOLD, TextAlignment.LEFT, text_color=rl.Color(255,89,79,255), text_padding=60) - self._custom_software_warning_body_label = Label("Use caution when installing third-party software. Third-party software has not been tested by comma," - + " and may cause damage to your device and/or vehicle.\n\nIf you'd like to proceed, use https://flash.comma.ai " + self._custom_software_warning_body_label = Label("Use caution when installing third-party software.\n\n" + + "⚠️ It has not been tested by comma.\n\n" + + "⚠️ It may not comply with relevant safety standards.\n\n" + + "⚠️ It may cause damage to your device and/or vehicle.\n\n" + + "If you'd like to proceed, use https://flash.comma.ai " + "to restore your device to a factory state later.", 85, text_alignment=TextAlignment.LEFT, text_padding=60) + self._custom_software_warning_body_scroll_panel = GuiScrollPanel() + self._downloading_body_label = Label("Downloading...", TITLE_FONT_SIZE, FontWeight.MEDIUM) try: @@ -291,13 +299,23 @@ class Setup(Widget): self._download_failed_startover_button.render(rl.Rectangle(rect.x + MARGIN + button_width + BUTTON_SPACING, button_y, button_width, BUTTON_HEIGHT)) def render_custom_software_warning(self, rect: rl.Rectangle): - self._custom_software_warning_title_label.render(rl.Rectangle(rect.x + 50, rect.y + 150, rect.width - 265, TITLE_FONT_SIZE)) - self._custom_software_warning_body_label.render(rl.Rectangle(rect.x + 50, rect.y + 200 , rect.width - 50, BODY_FONT_SIZE * 3)) + warn_rect = rl.Rectangle(rect.x, rect.y, rect.width, 1500) + offset = self._custom_software_warning_body_scroll_panel.handle_scroll(rect, warn_rect) button_width = (rect.width - MARGIN * 3) / 2 button_y = rect.height - MARGIN - BUTTON_HEIGHT + + rl.begin_scissor_mode(int(rect.x), int(rect.y), int(rect.width), int(button_y - BODY_FONT_SIZE)) + y_offset = rect.y + offset.y + self._custom_software_warning_title_label.render(rl.Rectangle(rect.x + 50, y_offset + 150, rect.width - 265, TITLE_FONT_SIZE)) + self._custom_software_warning_body_label.render(rl.Rectangle(rect.x + 50, y_offset + 200 , rect.width - 50, BODY_FONT_SIZE * 3)) + rl.end_scissor_mode() + self._custom_software_warning_back_button.render(rl.Rectangle(rect.x + MARGIN, button_y, button_width, BUTTON_HEIGHT)) self._custom_software_warning_continue_button.render(rl.Rectangle(rect.x + MARGIN * 2 + button_width, button_y, button_width, BUTTON_HEIGHT)) + if offset.y < (rect.height - warn_rect.height): + self._custom_software_warning_continue_button.set_enabled(True) + self._custom_software_warning_continue_button.set_text("Continue") def render_custom_software(self): def handle_keyboard_result(result): From ceb557058c5a7e7fd4de8c2dc5608e32c8773eac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Sun, 17 Aug 2025 10:18:30 -0700 Subject: [PATCH 23/64] Steam Powered model (#36000) * f3e67f3e-6079-48cf-92a4-dee5eebd1d73/360 * f3e67f3e-6079-48cf-92a4-dee5eebd1d73/400 * No more action head: a8f96b93-bde2-4e28-a732-4df21ebba968/400 --- selfdrive/modeld/modeld.py | 11 ----------- selfdrive/modeld/models/driving_policy.onnx | 4 ++-- selfdrive/modeld/models/driving_vision.onnx | 4 ++-- 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 707b221bb9..8bc8bf01ab 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -102,15 +102,12 @@ class ModelState: self.full_features_buffer = np.zeros((1, ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32) self.full_desire = np.zeros((1, ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.DESIRE_LEN), dtype=np.float32) - self.full_prev_desired_curv = np.zeros((1, ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32) self.temporal_idxs = slice(-1-(ModelConstants.TEMPORAL_SKIP*(ModelConstants.INPUT_HISTORY_BUFFER_LEN-1)), None, ModelConstants.TEMPORAL_SKIP) # policy inputs self.numpy_inputs = { 'desire': np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.DESIRE_LEN), dtype=np.float32), 'traffic_convention': np.zeros((1, ModelConstants.TRAFFIC_CONVENTION_LEN), dtype=np.float32), - 'lateral_control_params': np.zeros((1, ModelConstants.LATERAL_CONTROL_PARAMS_LEN), dtype=np.float32), - 'prev_desired_curv': np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32), 'features_buffer': np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32), } @@ -143,7 +140,6 @@ class ModelState: self.numpy_inputs['desire'][:] = self.full_desire.reshape((1,ModelConstants.INPUT_HISTORY_BUFFER_LEN,ModelConstants.TEMPORAL_SKIP,-1)).max(axis=2) self.numpy_inputs['traffic_convention'][:] = inputs['traffic_convention'] - self.numpy_inputs['lateral_control_params'][:] = inputs['lateral_control_params'] imgs_cl = {name: self.frames[name].prepare(bufs[name], transforms[name].flatten()) for name in self.vision_input_names} if TICI and not USBGPU: @@ -169,11 +165,6 @@ class ModelState: self.policy_output = self.policy_run(**self.policy_inputs).contiguous().realize().uop.base.buffer.numpy() policy_outputs_dict = self.parser.parse_policy_outputs(self.slice_outputs(self.policy_output, self.policy_output_slices)) - # TODO model only uses last value now - self.full_prev_desired_curv[0,:-1] = self.full_prev_desired_curv[0,1:] - self.full_prev_desired_curv[0,-1,:] = policy_outputs_dict['desired_curvature'][0, :] - self.numpy_inputs['prev_desired_curv'][:] = 0*self.full_prev_desired_curv[0, self.temporal_idxs] - combined_outputs_dict = {**vision_outputs_dict, **policy_outputs_dict} if SEND_RAW_PRED: combined_outputs_dict['raw_pred'] = np.concatenate([self.vision_output.copy(), self.policy_output.copy()]) @@ -292,7 +283,6 @@ def main(demo=False): frame_id = sm["roadCameraState"].frameId v_ego = max(sm["carState"].vEgo, 0.) lat_delay = sm["liveDelay"].lateralDelay + LAT_SMOOTH_SECONDS - lateral_control_params = np.array([v_ego, lat_delay], dtype=np.float32) if sm.updated["liveCalibration"] and sm.seen['roadCameraState'] and sm.seen['deviceState']: device_from_calib_euler = np.array(sm["liveCalibration"].rpyCalib, dtype=np.float32) dc = DEVICE_CAMERAS[(str(sm['deviceState'].deviceType), str(sm['roadCameraState'].sensor))] @@ -325,7 +315,6 @@ def main(demo=False): inputs:dict[str, np.ndarray] = { 'desire': vec_desire, 'traffic_convention': traffic_convention, - 'lateral_control_params': lateral_control_params, } mt1 = time.perf_counter() diff --git a/selfdrive/modeld/models/driving_policy.onnx b/selfdrive/modeld/models/driving_policy.onnx index 267fc92a3f..867a0d3b9b 100644 --- a/selfdrive/modeld/models/driving_policy.onnx +++ b/selfdrive/modeld/models/driving_policy.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1af87c38492444521632a0e75839b5684ee46bf255b3474773784bffb9fe4f57 -size 15583374 +oid sha256:04b763fb71efe57a8a4c4168a8043ecd58939015026ded0dc755ded6905ac251 +size 12343523 diff --git a/selfdrive/modeld/models/driving_vision.onnx b/selfdrive/modeld/models/driving_vision.onnx index 18f63358db..ce0dc927e7 100644 --- a/selfdrive/modeld/models/driving_vision.onnx +++ b/selfdrive/modeld/models/driving_vision.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c824f68646a3b94f117f01c70dc8316fb466e05fbd42ccdba440b8a8dc86914b -size 46265993 +oid sha256:e66bb8d53eced3786ed71a59b55ffc6810944cb217f0518621cc76303260a1ef +size 46271991 From 63fa250f291c26ce933969fb821b26e9915d6524 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 17 Aug 2025 11:53:20 -0700 Subject: [PATCH 24/64] Add note around excessive actuation check (#36010) * Add note around excessive actuation check * Update selfdrived.py --- selfdrive/selfdrived/selfdrived.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/selfdrive/selfdrived/selfdrived.py b/selfdrive/selfdrived/selfdrived.py index 89fa36c906..cf22040e84 100755 --- a/selfdrive/selfdrived/selfdrived.py +++ b/selfdrive/selfdrived/selfdrived.py @@ -238,7 +238,10 @@ class SelfdriveD: if self.sm['driverAssistance'].leftLaneDeparture or self.sm['driverAssistance'].rightLaneDeparture: self.events.add(EventName.ldw) - # Check for excessive actuation + # ****************************************************************************************** + # NOTE: To fork maintainers. + # Disabling or nerfing safety features will get you and your users banned from our servers. + # We recommend that you do not change these numbers from the defaults. if self.sm.updated['liveCalibration']: self.pose_calibrator.feed_live_calib(self.sm['liveCalibration']) if self.sm.updated['livePose']: @@ -253,6 +256,7 @@ class SelfdriveD: if self.excessive_actuation: self.events.add(EventName.excessiveActuation) + # ****************************************************************************************** # Handle lane change if self.sm['modelV2'].meta.laneChangeState == LaneChangeState.preLaneChange: From ef9e430992220cc981249c93ba838deb7637b1e4 Mon Sep 17 00:00:00 2001 From: Muhammed Jaseem Pallikkal <64106637+jasseeeem@users.noreply.github.com> Date: Mon, 18 Aug 2025 02:21:12 +0530 Subject: [PATCH 25/64] Fixed development environment link in CONTRIBUTING.md (#36011) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fixed development environment link in CONTRIBUTING.md This is my first PR to openpilot 🎉 excited to contribute! * Using absolute path for tools --- docs/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 1950db8a05..f8c27b8815 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -6,7 +6,7 @@ Development is coordinated through [Discord](https://discord.comma.ai) and GitHu ### Getting Started -* Setup your [development environment](../tools/) +* Setup your [development environment](/tools/) * Join our [Discord](https://discord.comma.ai) * Docs are at https://docs.comma.ai and https://blog.comma.ai From 3d879dd1ae9e22fd2d1d3a02a7c8a9da7b5e37de Mon Sep 17 00:00:00 2001 From: commaci-public <60409688+commaci-public@users.noreply.github.com> Date: Sun, 17 Aug 2025 17:53:50 -0700 Subject: [PATCH 26/64] [bot] Update Python packages (#36012) * Update Python packages * add psa --------- Co-authored-by: Vehicle Researcher Co-authored-by: Adeeb Shihadeh --- opendbc_repo | 2 +- .../test/process_replay/test_processes.py | 2 +- tinygrad_repo | 2 +- uv.lock | 226 +++++++++--------- 4 files changed, 116 insertions(+), 116 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index 74bfaa2c75..b2f34136c0 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 74bfaa2c750f2eca584dd6b58c1b862ba59d29d8 +Subproject commit b2f34136c0e58a76d4b4197003a852e72e48767e diff --git a/selfdrive/test/process_replay/test_processes.py b/selfdrive/test/process_replay/test_processes.py index 54e7039a4e..5868ca4c1c 100755 --- a/selfdrive/test/process_replay/test_processes.py +++ b/selfdrive/test/process_replay/test_processes.py @@ -61,7 +61,7 @@ segments = [ ] # dashcamOnly makes don't need to be tested until a full port is done -excluded_interfaces = ["mock", "body"] +excluded_interfaces = ["mock", "body", "psa"] BASE_URL = "https://commadataci.blob.core.windows.net/openpilotci/" REF_COMMIT_FN = os.path.join(PROC_REPLAY_DIR, "ref_commit") diff --git a/tinygrad_repo b/tinygrad_repo index d2bb1bcb97..c30a113b2a 160000 --- a/tinygrad_repo +++ b/tinygrad_repo @@ -1 +1 @@ -Subproject commit d2bb1bcb976f106a41928f2d66d354ab7afd6f59 +Subproject commit c30a113b2a876cabaea1049601fea3a0b758c5b1 diff --git a/uv.lock b/uv.lock index 3b1baa3ee3..7cfaceed97 100644 --- a/uv.lock +++ b/uv.lock @@ -415,31 +415,31 @@ wheels = [ [[package]] name = "cython" -version = "3.1.2" +version = "3.1.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/40/7b17cd866158238db704965da1b5849af261dbad393ea3ac966f934b2d39/cython-3.1.2.tar.gz", hash = "sha256:6bbf7a953fa6762dfecdec015e3b054ba51c0121a45ad851fa130f63f5331381", size = 3184825, upload-time = "2025-06-09T07:08:48.465Z" } +sdist = { url = "https://files.pythonhosted.org/packages/18/ab/915337fb39ab4f4539a313df38fc69938df3bf14141b90d61dfd5c2919de/cython-3.1.3.tar.gz", hash = "sha256:10ee785e42328924b78f75a74f66a813cb956b4a9bc91c44816d089d5934c089", size = 3186689, upload-time = "2025-08-13T06:19:13.619Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/de/502ddebaf5fe78f13cd6361acdd74710d3a5b15c22a9edc0ea4c873a59a5/cython-3.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5548573e0912d7dc80579827493315384c462e2f15797b91a8ed177686d31eb9", size = 3007792, upload-time = "2025-06-09T07:09:28.777Z" }, - { url = "https://files.pythonhosted.org/packages/bb/c8/91b00bc68effba9ba1ff5b33988052ac4d98fc1ac3021ade7261661299c6/cython-3.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bf3ea5bc50d80762c490f42846820a868a6406fdb5878ae9e4cc2f11b50228a", size = 2870798, upload-time = "2025-06-09T07:09:30.745Z" }, - { url = "https://files.pythonhosted.org/packages/f4/4b/29d290f14607785112c00a5e1685d766f433531bbd6a11ad229ab61b7a70/cython-3.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20ce53951d06ab2bca39f153d9c5add1d631c2a44d58bf67288c9d631be9724e", size = 3131280, upload-time = "2025-06-09T07:09:32.785Z" }, - { url = "https://files.pythonhosted.org/packages/38/3c/7c61e9ce25377ec7c4aa0b7ceeed34559ebca7b5cfd384672ba64eeaa4ba/cython-3.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e05a36224e3002d48c7c1c695b3771343bd16bc57eab60d6c5d5e08f3cbbafd8", size = 3223898, upload-time = "2025-06-09T07:09:35.345Z" }, - { url = "https://files.pythonhosted.org/packages/10/96/2d3fbe7e50e98b53ac86fefb48b64262b2e1304b3495e8e25b3cd1c3473e/cython-3.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbc0fc0777c7ab82297c01c61a1161093a22a41714f62e8c35188a309bd5db8e", size = 3291527, upload-time = "2025-06-09T07:09:37.502Z" }, - { url = "https://files.pythonhosted.org/packages/bd/e4/4cd3624e250d86f05bdb121a567865b9cca75cdc6dce4eedd68e626ea4f8/cython-3.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:18161ef3dd0e90a944daa2be468dd27696712a5f792d6289e97d2a31298ad688", size = 3184034, upload-time = "2025-06-09T07:09:40.225Z" }, - { url = "https://files.pythonhosted.org/packages/24/de/f8c1243c3e50ec95cb81f3a7936c8cf162f28050db8683e291c3861b46a0/cython-3.1.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ca45020950cd52d82189d6dfb6225737586be6fe7b0b9d3fadd7daca62eff531", size = 3386084, upload-time = "2025-06-09T07:09:42.206Z" }, - { url = "https://files.pythonhosted.org/packages/c8/95/2365937da44741ef0781bb9ecc1f8f52b38b65acb7293b5fc7c3eaee5346/cython-3.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:aaae97d6d07610224be2b73a93e9e3dd85c09aedfd8e47054e3ef5a863387dae", size = 3309974, upload-time = "2025-06-09T07:09:44.801Z" }, - { url = "https://files.pythonhosted.org/packages/9b/b8/280eed114110a1a3aa9e2e76bcd06cdd5ef0df7ab77c0be9d5378ca28c57/cython-3.1.2-cp311-cp311-win32.whl", hash = "sha256:3d439d9b19e7e70f6ff745602906d282a853dd5219d8e7abbf355de680c9d120", size = 2482942, upload-time = "2025-06-09T07:09:46.583Z" }, - { url = "https://files.pythonhosted.org/packages/a2/50/0aa65be5a4ab65bde3224b8fd23ed795f699d1e724ac109bb0a32036b82d/cython-3.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:8efa44ee2f1876e40eb5e45f6513a19758077c56bf140623ccab43d31f873b61", size = 2686535, upload-time = "2025-06-09T07:09:48.345Z" }, - { url = "https://files.pythonhosted.org/packages/22/86/9393ab7204d5bb65f415dd271b658c18f57b9345d06002cae069376a5a7a/cython-3.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9c2c4b6f9a941c857b40168b3f3c81d514e509d985c2dcd12e1a4fea9734192e", size = 3015898, upload-time = "2025-06-09T07:09:50.79Z" }, - { url = "https://files.pythonhosted.org/packages/f9/b8/3d10ac37ab7b7ee60bc6bfb48f6682ebee7fddaccf56e1e135f0d46ca79f/cython-3.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bdbc115bbe1b8c1dcbcd1b03748ea87fa967eb8dfc3a1a9bb243d4a382efcff4", size = 2846204, upload-time = "2025-06-09T07:09:52.832Z" }, - { url = "https://files.pythonhosted.org/packages/f8/34/637771d8e10ebabc34a34cdd0d63fe797b66c334e150189955bf6442d710/cython-3.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05111f89db1ca98edc0675cfaa62be47b3ff519a29876eb095532a9f9e052b8", size = 3080671, upload-time = "2025-06-09T07:09:54.924Z" }, - { url = "https://files.pythonhosted.org/packages/6b/c8/383ad1851fb272920a152c5a30bb6f08c3471b5438079d9488fc3074a170/cython-3.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e7188df8709be32cfdfadc7c3782e361c929df9132f95e1bbc90a340dca3c7", size = 3199022, upload-time = "2025-06-09T07:09:56.978Z" }, - { url = "https://files.pythonhosted.org/packages/e6/11/20adc8f2db37a29f245e8fd4b8b8a8245fce4bbbd128185cc9a7b1065e4c/cython-3.1.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c0ecc71e60a051732c2607b8eb8f2a03a5dac09b28e52b8af323c329db9987b", size = 3241337, upload-time = "2025-06-09T07:09:59.156Z" }, - { url = "https://files.pythonhosted.org/packages/6f/0b/491f1fd3e177cccb6bb6d52f9609f78d395edde83ac47ebb06d21717ca29/cython-3.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f27143cf88835c8bcc9bf3304953f23f377d1d991e8942982fe7be344c7cfce3", size = 3131808, upload-time = "2025-06-09T07:10:01.31Z" }, - { url = "https://files.pythonhosted.org/packages/db/d2/5e7053a3214c9baa7ad72940555eb87cf4750e597f10b2bb43db62c3f39f/cython-3.1.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d8c43566701133f53bf13485839d8f3f309095fe0d3b9d0cd5873073394d2edc", size = 3340319, upload-time = "2025-06-09T07:10:03.485Z" }, - { url = "https://files.pythonhosted.org/packages/95/42/4842f8ddac9b36c94ae08b23c7fcde3f930c1dd49ac8992bb5320a4d96b5/cython-3.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a3bb893e85f027a929c1764bb14db4c31cbdf8a96f59a78f608f2ba7cfbbce95", size = 3287370, upload-time = "2025-06-09T07:10:05.637Z" }, - { url = "https://files.pythonhosted.org/packages/03/0d/417745ed75d414176e50310087b43299a3e611e75c379ff998f60f2ca1a8/cython-3.1.2-cp312-cp312-win32.whl", hash = "sha256:12c5902f105e43ca9af7874cdf87a23627f98c15d5a4f6d38bc9d334845145c0", size = 2487734, upload-time = "2025-06-09T07:10:07.591Z" }, - { url = "https://files.pythonhosted.org/packages/8e/82/df61d09ab81979ba171a8252af8fb8a3b26a0f19d1330c2679c11fe41667/cython-3.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:06789eb7bd2e55b38b9dd349e9309f794aee0fed99c26ea5c9562d463877763f", size = 2695542, upload-time = "2025-06-09T07:10:09.545Z" }, - { url = "https://files.pythonhosted.org/packages/25/d6/ef8557d5e75cc57d55df579af4976935ee111a85bbee4a5b72354e257066/cython-3.1.2-py3-none-any.whl", hash = "sha256:d23fd7ffd7457205f08571a42b108a3cf993e83a59fe4d72b42e6fc592cf2639", size = 1224753, upload-time = "2025-06-09T07:08:44.849Z" }, + { url = "https://files.pythonhosted.org/packages/b5/51/54f5d1bed7b7d003d0584996a030542497aeb05b9241782c217ea71061f5/cython-3.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4b3b2f6587b42efdece2d174a2aa4234da4524cc6673f3955c2e62b60c6d11fd", size = 3002973, upload-time = "2025-08-13T06:19:50.777Z" }, + { url = "https://files.pythonhosted.org/packages/05/07/b4043fed60070ee21b0d9ff3a877d2ecdc79231e55119ce852b79b690306/cython-3.1.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:963cf640d049fcca1cefd62d1653f859892d6dc8e4d958eb49a5babc491de6a1", size = 2865389, upload-time = "2025-08-13T06:19:52.675Z" }, + { url = "https://files.pythonhosted.org/packages/b4/e3/67d349b5310a40f281e153e826ca24d9f7ba2997c06800eda781253dccfd/cython-3.1.3-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2ab05d1bf2d5522ecff35d94ca233b77db2300413597c3ca0b6448377fa4bd7c", size = 3410316, upload-time = "2025-08-13T06:19:55.217Z" }, + { url = "https://files.pythonhosted.org/packages/49/c4/84aae921135174111091547fa726ea5f8bba718cd12b2589a70c2aab8d5c/cython-3.1.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cd517e3be052fb49443585b01f02f46080b3408e32c1108a0fdc4cc25b3c9d30", size = 3189568, upload-time = "2025-08-13T06:19:57.503Z" }, + { url = "https://files.pythonhosted.org/packages/e2/85/1bf18883f1a1f656ad83a671e54553caedb1ee2f39a3e792a14aa82557a2/cython-3.1.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a48e2180d74e3c528561d85b48f9a939a429537f9ea8aac7fb16180e7bff47e2", size = 3312649, upload-time = "2025-08-13T06:19:59.894Z" }, + { url = "https://files.pythonhosted.org/packages/68/da/cc1373decc0d14a25f1b434f47de5e27696f3092319aa5e19fcf84157415/cython-3.1.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e7c9daa90b15f59aa2a0d638ac1b36777a7e80122099952a0295c71190ce14bc", size = 3203821, upload-time = "2025-08-13T06:20:02.124Z" }, + { url = "https://files.pythonhosted.org/packages/0a/32/e10582d6f7b02ee63607f388dbbd34e329c54559c85961ddc5c655266519/cython-3.1.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:08ac646ff42781827f23b7a9b61669cdb92055f52724cd8cbe0e1defc56fce2e", size = 3426853, upload-time = "2025-08-13T06:20:04.474Z" }, + { url = "https://files.pythonhosted.org/packages/e0/da/5b0d1b5a4c7622ec812ad9374c9c6b426d69818f13f51e36f4f25ca01c86/cython-3.1.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bb0e0e7fceaffa22e4dc9600f7f75998eef5cc6ac5a8c0733b482851ba765ef2", size = 3328872, upload-time = "2025-08-13T06:20:06.834Z" }, + { url = "https://files.pythonhosted.org/packages/b0/5f/f102f6c8d27338f0baf094754c67f920828a19612053abc903e66f84506f/cython-3.1.3-cp311-cp311-win32.whl", hash = "sha256:42b1c3ebe36a52e2a8e939c0651e9ca5d30b81d03f800bbf0499deb0503ab565", size = 2480850, upload-time = "2025-08-13T06:20:08.988Z" }, + { url = "https://files.pythonhosted.org/packages/b7/60/415d0f0f7c2e227707baec11c968387d33507d2eb7f26a2950a323c705e5/cython-3.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:34a973844998281951bf54cdd0b6a9946ba03ba94580820738583a00da167d8f", size = 2712560, upload-time = "2025-08-13T06:20:11.877Z" }, + { url = "https://files.pythonhosted.org/packages/79/26/f433fdfd5182b9231819a99acc49a1f856669532306e7a38dce63ba7297e/cython-3.1.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:849ef3d15d4354e5f74cdb6d3c80d80b03209b3bf1f4ff93315890b19da18944", size = 3014237, upload-time = "2025-08-13T06:20:13.77Z" }, + { url = "https://files.pythonhosted.org/packages/e5/6c/1bebf44f5f177f8c750e608f82c08cd699b8f28cc233e799379bfcf2a2c2/cython-3.1.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:93dd0f62a3f8e93166d8584f8b243180d681ba8fed1f530b55d5f70c348c5797", size = 2844261, upload-time = "2025-08-13T06:20:15.619Z" }, + { url = "https://files.pythonhosted.org/packages/c7/74/983005ce5954f6dc8360b105a561e51a60ea619c53296afac98253d1c9d7/cython-3.1.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ff4a2cb84798faffb3988bd94636c3ad31a95ff44ef017f09121abffc56f84cf", size = 3388846, upload-time = "2025-08-13T06:20:17.679Z" }, + { url = "https://files.pythonhosted.org/packages/68/50/dbe7edefe9b652bc72d49da07785173e89197b9a2d64a2ac45da9795eccf/cython-3.1.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4b05319e36f34d5388deea5cc2bcfd65f9ebf76f4ea050829421a69625dbba4a", size = 3167022, upload-time = "2025-08-13T06:20:19.863Z" }, + { url = "https://files.pythonhosted.org/packages/4a/55/b50548b77203e22262002feae23a172f95282b4b8deb5ad104f08e3dc20d/cython-3.1.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ac902a17934a6da46f80755f49413bc4c03a569ae3c834f5d66da7288ba7e6c", size = 3317782, upload-time = "2025-08-13T06:20:21.962Z" }, + { url = "https://files.pythonhosted.org/packages/f6/1b/20a97507d528dc330d67be4fbad6bc767c681d56192bce8f7117a187b2ad/cython-3.1.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7d7a555a864b1b08576f9e8a67f3789796a065837544f9f683ebf3188012fdbd", size = 3179818, upload-time = "2025-08-13T06:20:24.419Z" }, + { url = "https://files.pythonhosted.org/packages/43/a5/7b32a19c4c6bb0e2cc7ae52269b6b23a42f67f730e4abd4f8dca63660f7a/cython-3.1.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b827ce7d97ef8624adcf2bdda594b3dcb6c9b4f124d8f72001d8aea27d69dc1c", size = 3403206, upload-time = "2025-08-13T06:20:26.846Z" }, + { url = "https://files.pythonhosted.org/packages/ba/e1/08cfd4c5e99f79e62d4a7b0009bbd906748633270935c8a55eee51fead76/cython-3.1.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7851107204085f4f02d0eb6b660ddcad2ce4846e8b7a1eaba724a0bd3cd68a6b", size = 3327837, upload-time = "2025-08-13T06:20:28.946Z" }, + { url = "https://files.pythonhosted.org/packages/23/1b/f3d384be86fa2a6e110b42f42bf97295a513197aaa5532f451c73bf5b48e/cython-3.1.3-cp312-cp312-win32.whl", hash = "sha256:ed20f1b45b2da5a4f8e71a80025bca1cdc96ba35820b0b17658a4a025be920b0", size = 2485990, upload-time = "2025-08-13T06:20:31.517Z" }, + { url = "https://files.pythonhosted.org/packages/de/f0/17cff75e3c141bda73d770f7412632f53e55778d3bfdadfeec4dd3a7d1d6/cython-3.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:dc4ca0f4dec55124cd79ddcfc555be1cbe0092cc99bcf1403621d17b9c6218bb", size = 2704002, upload-time = "2025-08-13T06:20:33.773Z" }, + { url = "https://files.pythonhosted.org/packages/56/c8/46ac27096684f33e27dab749ef43c6b0119c6a0d852971eaefb73256dc4c/cython-3.1.3-py3-none-any.whl", hash = "sha256:d13025b34f72f77bf7f65c1cd628914763e6c285f4deb934314c922b91e6be5a", size = 1225725, upload-time = "2025-08-13T06:19:09.593Z" }, ] [[package]] @@ -501,36 +501,36 @@ wheels = [ [[package]] name = "filelock" -version = "3.18.0" +version = "3.19.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075, upload-time = "2025-03-14T07:11:40.47Z" } +sdist = { url = "https://files.pythonhosted.org/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", size = 17687, upload-time = "2025-08-14T16:56:03.016Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215, upload-time = "2025-03-14T07:11:39.145Z" }, + { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988, upload-time = "2025-08-14T16:56:01.633Z" }, ] [[package]] name = "fonttools" -version = "4.59.0" +version = "4.59.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8a/27/ec3c723bfdf86f34c5c82bf6305df3e0f0d8ea798d2d3a7cb0c0a866d286/fonttools-4.59.0.tar.gz", hash = "sha256:be392ec3529e2f57faa28709d60723a763904f71a2b63aabe14fee6648fe3b14", size = 3532521, upload-time = "2025-07-16T12:04:54.613Z" } +sdist = { url = "https://files.pythonhosted.org/packages/11/7f/29c9c3fe4246f6ad96fee52b88d0dc3a863c7563b0afc959e36d78b965dc/fonttools-4.59.1.tar.gz", hash = "sha256:74995b402ad09822a4c8002438e54940d9f1ecda898d2bb057729d7da983e4cb", size = 3534394, upload-time = "2025-08-14T16:28:14.266Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/06/96/520733d9602fa1bf6592e5354c6721ac6fc9ea72bc98d112d0c38b967199/fonttools-4.59.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:841b2186adce48903c0fef235421ae21549020eca942c1da773ac380b056ab3c", size = 2782387, upload-time = "2025-07-16T12:03:51.424Z" }, - { url = "https://files.pythonhosted.org/packages/87/6a/170fce30b9bce69077d8eec9bea2cfd9f7995e8911c71be905e2eba6368b/fonttools-4.59.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9bcc1e77fbd1609198966ded6b2a9897bd6c6bcbd2287a2fc7d75f1a254179c5", size = 2342194, upload-time = "2025-07-16T12:03:53.295Z" }, - { url = "https://files.pythonhosted.org/packages/b0/b6/7c8166c0066856f1408092f7968ac744060cf72ca53aec9036106f57eeca/fonttools-4.59.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:37c377f7cb2ab2eca8a0b319c68146d34a339792f9420fca6cd49cf28d370705", size = 5032333, upload-time = "2025-07-16T12:03:55.177Z" }, - { url = "https://files.pythonhosted.org/packages/eb/0c/707c5a19598eafcafd489b73c4cb1c142102d6197e872f531512d084aa76/fonttools-4.59.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fa39475eaccb98f9199eccfda4298abaf35ae0caec676ffc25b3a5e224044464", size = 4974422, upload-time = "2025-07-16T12:03:57.406Z" }, - { url = "https://files.pythonhosted.org/packages/f6/e7/6d33737d9fe632a0f59289b6f9743a86d2a9d0673de2a0c38c0f54729822/fonttools-4.59.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d3972b13148c1d1fbc092b27678a33b3080d1ac0ca305742b0119b75f9e87e38", size = 5010631, upload-time = "2025-07-16T12:03:59.449Z" }, - { url = "https://files.pythonhosted.org/packages/63/e1/a4c3d089ab034a578820c8f2dff21ef60daf9668034a1e4fb38bb1cc3398/fonttools-4.59.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a408c3c51358c89b29cfa5317cf11518b7ce5de1717abb55c5ae2d2921027de6", size = 5122198, upload-time = "2025-07-16T12:04:01.542Z" }, - { url = "https://files.pythonhosted.org/packages/09/77/ca82b9c12fa4de3c520b7760ee61787640cf3fde55ef1b0bfe1de38c8153/fonttools-4.59.0-cp311-cp311-win32.whl", hash = "sha256:6770d7da00f358183d8fd5c4615436189e4f683bdb6affb02cad3d221d7bb757", size = 2214216, upload-time = "2025-07-16T12:04:03.515Z" }, - { url = "https://files.pythonhosted.org/packages/ab/25/5aa7ca24b560b2f00f260acf32c4cf29d7aaf8656e159a336111c18bc345/fonttools-4.59.0-cp311-cp311-win_amd64.whl", hash = "sha256:84fc186980231a287b28560d3123bd255d3c6b6659828c642b4cf961e2b923d0", size = 2261879, upload-time = "2025-07-16T12:04:05.015Z" }, - { url = "https://files.pythonhosted.org/packages/e2/77/b1c8af22f4265e951cd2e5535dbef8859efcef4fb8dee742d368c967cddb/fonttools-4.59.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f9b3a78f69dcbd803cf2fb3f972779875b244c1115481dfbdd567b2c22b31f6b", size = 2767562, upload-time = "2025-07-16T12:04:06.895Z" }, - { url = "https://files.pythonhosted.org/packages/ff/5a/aeb975699588176bb357e8b398dfd27e5d3a2230d92b81ab8cbb6187358d/fonttools-4.59.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:57bb7e26928573ee7c6504f54c05860d867fd35e675769f3ce01b52af38d48e2", size = 2335168, upload-time = "2025-07-16T12:04:08.695Z" }, - { url = "https://files.pythonhosted.org/packages/54/97/c6101a7e60ae138c4ef75b22434373a0da50a707dad523dd19a4889315bf/fonttools-4.59.0-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4536f2695fe5c1ffb528d84a35a7d3967e5558d2af58b4775e7ab1449d65767b", size = 4909850, upload-time = "2025-07-16T12:04:10.761Z" }, - { url = "https://files.pythonhosted.org/packages/bd/6c/fa4d18d641054f7bff878cbea14aa9433f292b9057cb1700d8e91a4d5f4f/fonttools-4.59.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:885bde7d26e5b40e15c47bd5def48b38cbd50830a65f98122a8fb90962af7cd1", size = 4955131, upload-time = "2025-07-16T12:04:12.846Z" }, - { url = "https://files.pythonhosted.org/packages/20/5c/331947fc1377deb928a69bde49f9003364f5115e5cbe351eea99e39412a2/fonttools-4.59.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6801aeddb6acb2c42eafa45bc1cb98ba236871ae6f33f31e984670b749a8e58e", size = 4899667, upload-time = "2025-07-16T12:04:14.558Z" }, - { url = "https://files.pythonhosted.org/packages/8a/46/b66469dfa26b8ff0baa7654b2cc7851206c6d57fe3abdabbaab22079a119/fonttools-4.59.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:31003b6a10f70742a63126b80863ab48175fb8272a18ca0846c0482968f0588e", size = 5051349, upload-time = "2025-07-16T12:04:16.388Z" }, - { url = "https://files.pythonhosted.org/packages/2e/05/ebfb6b1f3a4328ab69787d106a7d92ccde77ce66e98659df0f9e3f28d93d/fonttools-4.59.0-cp312-cp312-win32.whl", hash = "sha256:fbce6dae41b692a5973d0f2158f782b9ad05babc2c2019a970a1094a23909b1b", size = 2201315, upload-time = "2025-07-16T12:04:18.557Z" }, - { url = "https://files.pythonhosted.org/packages/09/45/d2bdc9ea20bbadec1016fd0db45696d573d7a26d95ab5174ffcb6d74340b/fonttools-4.59.0-cp312-cp312-win_amd64.whl", hash = "sha256:332bfe685d1ac58ca8d62b8d6c71c2e52a6c64bc218dc8f7825c9ea51385aa01", size = 2249408, upload-time = "2025-07-16T12:04:20.489Z" }, - { url = "https://files.pythonhosted.org/packages/d0/9c/df0ef2c51845a13043e5088f7bb988ca6cd5bb82d5d4203d6a158aa58cf2/fonttools-4.59.0-py3-none-any.whl", hash = "sha256:241313683afd3baacb32a6bd124d0bce7404bc5280e12e291bae1b9bba28711d", size = 1128050, upload-time = "2025-07-16T12:04:52.687Z" }, + { url = "https://files.pythonhosted.org/packages/34/62/9667599561f623d4a523cc9eb4f66f3b94b6155464110fa9aebbf90bbec7/fonttools-4.59.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4909cce2e35706f3d18c54d3dcce0414ba5e0fb436a454dffec459c61653b513", size = 2778815, upload-time = "2025-08-14T16:26:28.484Z" }, + { url = "https://files.pythonhosted.org/packages/8f/78/cc25bcb2ce86033a9df243418d175e58f1956a35047c685ef553acae67d6/fonttools-4.59.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:efbec204fa9f877641747f2d9612b2b656071390d7a7ef07a9dbf0ecf9c7195c", size = 2341631, upload-time = "2025-08-14T16:26:30.396Z" }, + { url = "https://files.pythonhosted.org/packages/a4/cc/fcbb606dd6871f457ac32f281c20bcd6cc77d9fce77b5a4e2b2afab1f500/fonttools-4.59.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39dfd42cc2dc647b2c5469bc7a5b234d9a49e72565b96dd14ae6f11c2c59ef15", size = 5022222, upload-time = "2025-08-14T16:26:32.447Z" }, + { url = "https://files.pythonhosted.org/packages/61/96/c0b1cf2b74d08eb616a80dbf5564351fe4686147291a25f7dce8ace51eb3/fonttools-4.59.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b11bc177a0d428b37890825d7d025040d591aa833f85f8d8878ed183354f47df", size = 4966512, upload-time = "2025-08-14T16:26:34.621Z" }, + { url = "https://files.pythonhosted.org/packages/a4/26/51ce2e3e0835ffc2562b1b11d1fb9dafd0aca89c9041b64a9e903790a761/fonttools-4.59.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b9b4c35b3be45e5bc774d3fc9608bbf4f9a8d371103b858c80edbeed31dd5aa", size = 5001645, upload-time = "2025-08-14T16:26:36.876Z" }, + { url = "https://files.pythonhosted.org/packages/36/11/ef0b23f4266349b6d5ccbd1a07b7adc998d5bce925792aa5d1ec33f593e3/fonttools-4.59.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:01158376b8a418a0bae9625c476cebfcfcb5e6761e9d243b219cd58341e7afbb", size = 5113777, upload-time = "2025-08-14T16:26:39.002Z" }, + { url = "https://files.pythonhosted.org/packages/d0/da/b398fe61ef433da0a0472cdb5d4399124f7581ffe1a31b6242c91477d802/fonttools-4.59.1-cp311-cp311-win32.whl", hash = "sha256:cf7c5089d37787387123f1cb8f1793a47c5e1e3d1e4e7bfbc1cc96e0f925eabe", size = 2215076, upload-time = "2025-08-14T16:26:41.196Z" }, + { url = "https://files.pythonhosted.org/packages/94/bd/e2624d06ab94e41c7c77727b2941f1baed7edb647e63503953e6888020c9/fonttools-4.59.1-cp311-cp311-win_amd64.whl", hash = "sha256:c866eef7a0ba320486ade6c32bfc12813d1a5db8567e6904fb56d3d40acc5116", size = 2262779, upload-time = "2025-08-14T16:26:43.483Z" }, + { url = "https://files.pythonhosted.org/packages/ac/fe/6e069cc4cb8881d164a9bd956e9df555bc62d3eb36f6282e43440200009c/fonttools-4.59.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:43ab814bbba5f02a93a152ee61a04182bb5809bd2bc3609f7822e12c53ae2c91", size = 2769172, upload-time = "2025-08-14T16:26:45.729Z" }, + { url = "https://files.pythonhosted.org/packages/b9/98/ec4e03f748fefa0dd72d9d95235aff6fef16601267f4a2340f0e16b9330f/fonttools-4.59.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4f04c3ffbfa0baafcbc550657cf83657034eb63304d27b05cff1653b448ccff6", size = 2337281, upload-time = "2025-08-14T16:26:47.921Z" }, + { url = "https://files.pythonhosted.org/packages/8b/b1/890360a7e3d04a30ba50b267aca2783f4c1364363797e892e78a4f036076/fonttools-4.59.1-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d601b153e51a5a6221f0d4ec077b6bfc6ac35bfe6c19aeaa233d8990b2b71726", size = 4909215, upload-time = "2025-08-14T16:26:49.682Z" }, + { url = "https://files.pythonhosted.org/packages/8a/ec/2490599550d6c9c97a44c1e36ef4de52d6acf742359eaa385735e30c05c4/fonttools-4.59.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c735e385e30278c54f43a0d056736942023c9043f84ee1021eff9fd616d17693", size = 4951958, upload-time = "2025-08-14T16:26:51.616Z" }, + { url = "https://files.pythonhosted.org/packages/d1/40/bd053f6f7634234a9b9805ff8ae4f32df4f2168bee23cafd1271ba9915a9/fonttools-4.59.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1017413cdc8555dce7ee23720da490282ab7ec1cf022af90a241f33f9a49afc4", size = 4894738, upload-time = "2025-08-14T16:26:53.836Z" }, + { url = "https://files.pythonhosted.org/packages/ac/a1/3cd12a010d288325a7cfcf298a84825f0f9c29b01dee1baba64edfe89257/fonttools-4.59.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5c6d8d773470a5107052874341ed3c487c16ecd179976d81afed89dea5cd7406", size = 5045983, upload-time = "2025-08-14T16:26:56.153Z" }, + { url = "https://files.pythonhosted.org/packages/a2/af/8a2c3f6619cc43cf87951405337cc8460d08a4e717bb05eaa94b335d11dc/fonttools-4.59.1-cp312-cp312-win32.whl", hash = "sha256:2a2d0d33307f6ad3a2086a95dd607c202ea8852fa9fb52af9b48811154d1428a", size = 2203407, upload-time = "2025-08-14T16:26:58.165Z" }, + { url = "https://files.pythonhosted.org/packages/8e/f2/a19b874ddbd3ebcf11d7e25188ef9ac3f68b9219c62263acb34aca8cde05/fonttools-4.59.1-cp312-cp312-win_amd64.whl", hash = "sha256:0b9e4fa7eaf046ed6ac470f6033d52c052481ff7a6e0a92373d14f556f298dc0", size = 2251561, upload-time = "2025-08-14T16:27:00.646Z" }, + { url = "https://files.pythonhosted.org/packages/0f/64/9d606e66d498917cd7a2ff24f558010d42d6fd4576d9dd57f0bd98333f5a/fonttools-4.59.1-py3-none-any.whl", hash = "sha256:647db657073672a8330608970a984d51573557f328030566521bc03415535042", size = 1130094, upload-time = "2025-08-14T16:28:12.048Z" }, ] [[package]] @@ -1536,16 +1536,16 @@ wheels = [ [[package]] name = "protobuf" -version = "6.31.1" +version = "6.32.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/f3/b9655a711b32c19720253f6f06326faf90580834e2e83f840472d752bc8b/protobuf-6.31.1.tar.gz", hash = "sha256:d8cac4c982f0b957a4dc73a80e2ea24fab08e679c0de9deb835f4a12d69aca9a", size = 441797, upload-time = "2025-05-28T19:25:54.947Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/df/fb4a8eeea482eca989b51cffd274aac2ee24e825f0bf3cbce5281fa1567b/protobuf-6.32.0.tar.gz", hash = "sha256:a81439049127067fc49ec1d36e25c6ee1d1a2b7be930675f919258d03c04e7d2", size = 440614, upload-time = "2025-08-14T21:21:25.015Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/6f/6ab8e4bf962fd5570d3deaa2d5c38f0a363f57b4501047b5ebeb83ab1125/protobuf-6.31.1-cp310-abi3-win32.whl", hash = "sha256:7fa17d5a29c2e04b7d90e5e32388b8bfd0e7107cd8e616feef7ed3fa6bdab5c9", size = 423603, upload-time = "2025-05-28T19:25:41.198Z" }, - { url = "https://files.pythonhosted.org/packages/44/3a/b15c4347dd4bf3a1b0ee882f384623e2063bb5cf9fa9d57990a4f7df2fb6/protobuf-6.31.1-cp310-abi3-win_amd64.whl", hash = "sha256:426f59d2964864a1a366254fa703b8632dcec0790d8862d30034d8245e1cd447", size = 435283, upload-time = "2025-05-28T19:25:44.275Z" }, - { url = "https://files.pythonhosted.org/packages/6a/c9/b9689a2a250264a84e66c46d8862ba788ee7a641cdca39bccf64f59284b7/protobuf-6.31.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:6f1227473dc43d44ed644425268eb7c2e488ae245d51c6866d19fe158e207402", size = 425604, upload-time = "2025-05-28T19:25:45.702Z" }, - { url = "https://files.pythonhosted.org/packages/76/a1/7a5a94032c83375e4fe7e7f56e3976ea6ac90c5e85fac8576409e25c39c3/protobuf-6.31.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:a40fc12b84c154884d7d4c4ebd675d5b3b5283e155f324049ae396b95ddebc39", size = 322115, upload-time = "2025-05-28T19:25:47.128Z" }, - { url = "https://files.pythonhosted.org/packages/fa/b1/b59d405d64d31999244643d88c45c8241c58f17cc887e73bcb90602327f8/protobuf-6.31.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4ee898bf66f7a8b0bd21bce523814e6fbd8c6add948045ce958b73af7e8878c6", size = 321070, upload-time = "2025-05-28T19:25:50.036Z" }, - { url = "https://files.pythonhosted.org/packages/f7/af/ab3c51ab7507a7325e98ffe691d9495ee3d3aa5f589afad65ec920d39821/protobuf-6.31.1-py3-none-any.whl", hash = "sha256:720a6c7e6b77288b85063569baae8536671b39f15cc22037ec7045658d80489e", size = 168724, upload-time = "2025-05-28T19:25:53.926Z" }, + { url = "https://files.pythonhosted.org/packages/33/18/df8c87da2e47f4f1dcc5153a81cd6bca4e429803f4069a299e236e4dd510/protobuf-6.32.0-cp310-abi3-win32.whl", hash = "sha256:84f9e3c1ff6fb0308dbacb0950d8aa90694b0d0ee68e75719cb044b7078fe741", size = 424409, upload-time = "2025-08-14T21:21:12.366Z" }, + { url = "https://files.pythonhosted.org/packages/e1/59/0a820b7310f8139bd8d5a9388e6a38e1786d179d6f33998448609296c229/protobuf-6.32.0-cp310-abi3-win_amd64.whl", hash = "sha256:a8bdbb2f009cfc22a36d031f22a625a38b615b5e19e558a7b756b3279723e68e", size = 435735, upload-time = "2025-08-14T21:21:15.046Z" }, + { url = "https://files.pythonhosted.org/packages/cc/5b/0d421533c59c789e9c9894683efac582c06246bf24bb26b753b149bd88e4/protobuf-6.32.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d52691e5bee6c860fff9a1c86ad26a13afbeb4b168cd4445c922b7e2cf85aaf0", size = 426449, upload-time = "2025-08-14T21:21:16.687Z" }, + { url = "https://files.pythonhosted.org/packages/ec/7b/607764ebe6c7a23dcee06e054fd1de3d5841b7648a90fd6def9a3bb58c5e/protobuf-6.32.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:501fe6372fd1c8ea2a30b4d9be8f87955a64d6be9c88a973996cef5ef6f0abf1", size = 322869, upload-time = "2025-08-14T21:21:18.282Z" }, + { url = "https://files.pythonhosted.org/packages/40/01/2e730bd1c25392fc32e3268e02446f0d77cb51a2c3a8486b1798e34d5805/protobuf-6.32.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:75a2aab2bd1aeb1f5dc7c5f33bcb11d82ea8c055c9becbb41c26a8c43fd7092c", size = 322009, upload-time = "2025-08-14T21:21:19.893Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f2/80ffc4677aac1bc3519b26bc7f7f5de7fce0ee2f7e36e59e27d8beb32dd1/protobuf-6.32.0-py3-none-any.whl", hash = "sha256:ba377e5b67b908c8f3072a57b63e2c6a4cbd18aea4ed98d2584350dbf46f2783", size = 169287, upload-time = "2025-08-14T21:21:23.515Z" }, ] [[package]] @@ -4310,7 +4310,7 @@ wheels = [ [[package]] name = "pytest-xdist" -version = "3.7.1.dev24+g2b4372b" +version = "3.7.1.dev24+g2b4372bd6" source = { git = "https://github.com/sshane/pytest-xdist?rev=2b4372bd62699fb412c4fe2f95bf9f01bd2018da#2b4372bd62699fb412c4fe2f95bf9f01bd2018da" } dependencies = [ { name = "execnet" }, @@ -4586,27 +4586,28 @@ wheels = [ [[package]] name = "ruff" -version = "0.12.8" +version = "0.12.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4b/da/5bd7565be729e86e1442dad2c9a364ceeff82227c2dece7c29697a9795eb/ruff-0.12.8.tar.gz", hash = "sha256:4cb3a45525176e1009b2b64126acf5f9444ea59066262791febf55e40493a033", size = 5242373, upload-time = "2025-08-07T19:05:47.268Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4a/45/2e403fa7007816b5fbb324cb4f8ed3c7402a927a0a0cb2b6279879a8bfdc/ruff-0.12.9.tar.gz", hash = "sha256:fbd94b2e3c623f659962934e52c2bea6fc6da11f667a427a368adaf3af2c866a", size = 5254702, upload-time = "2025-08-14T16:08:55.2Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c9/1e/c843bfa8ad1114fab3eb2b78235dda76acd66384c663a4e0415ecc13aa1e/ruff-0.12.8-py3-none-linux_armv6l.whl", hash = "sha256:63cb5a5e933fc913e5823a0dfdc3c99add73f52d139d6cd5cc8639d0e0465513", size = 11675315, upload-time = "2025-08-07T19:05:06.15Z" }, - { url = "https://files.pythonhosted.org/packages/24/ee/af6e5c2a8ca3a81676d5480a1025494fd104b8896266502bb4de2a0e8388/ruff-0.12.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9a9bbe28f9f551accf84a24c366c1aa8774d6748438b47174f8e8565ab9dedbc", size = 12456653, upload-time = "2025-08-07T19:05:09.759Z" }, - { url = "https://files.pythonhosted.org/packages/99/9d/e91f84dfe3866fa648c10512904991ecc326fd0b66578b324ee6ecb8f725/ruff-0.12.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2fae54e752a3150f7ee0e09bce2e133caf10ce9d971510a9b925392dc98d2fec", size = 11659690, upload-time = "2025-08-07T19:05:12.551Z" }, - { url = "https://files.pythonhosted.org/packages/fe/ac/a363d25ec53040408ebdd4efcee929d48547665858ede0505d1d8041b2e5/ruff-0.12.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0acbcf01206df963d9331b5838fb31f3b44fa979ee7fa368b9b9057d89f4a53", size = 11896923, upload-time = "2025-08-07T19:05:14.821Z" }, - { url = "https://files.pythonhosted.org/packages/58/9f/ea356cd87c395f6ade9bb81365bd909ff60860975ca1bc39f0e59de3da37/ruff-0.12.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ae3e7504666ad4c62f9ac8eedb52a93f9ebdeb34742b8b71cd3cccd24912719f", size = 11477612, upload-time = "2025-08-07T19:05:16.712Z" }, - { url = "https://files.pythonhosted.org/packages/1a/46/92e8fa3c9dcfd49175225c09053916cb97bb7204f9f899c2f2baca69e450/ruff-0.12.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb82efb5d35d07497813a1c5647867390a7d83304562607f3579602fa3d7d46f", size = 13182745, upload-time = "2025-08-07T19:05:18.709Z" }, - { url = "https://files.pythonhosted.org/packages/5e/c4/f2176a310f26e6160deaf661ef60db6c3bb62b7a35e57ae28f27a09a7d63/ruff-0.12.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:dbea798fc0065ad0b84a2947b0aff4233f0cb30f226f00a2c5850ca4393de609", size = 14206885, upload-time = "2025-08-07T19:05:21.025Z" }, - { url = "https://files.pythonhosted.org/packages/87/9d/98e162f3eeeb6689acbedbae5050b4b3220754554526c50c292b611d3a63/ruff-0.12.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:49ebcaccc2bdad86fd51b7864e3d808aad404aab8df33d469b6e65584656263a", size = 13639381, upload-time = "2025-08-07T19:05:23.423Z" }, - { url = "https://files.pythonhosted.org/packages/81/4e/1b7478b072fcde5161b48f64774d6edd59d6d198e4ba8918d9f4702b8043/ruff-0.12.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ac9c570634b98c71c88cb17badd90f13fc076a472ba6ef1d113d8ed3df109fb", size = 12613271, upload-time = "2025-08-07T19:05:25.507Z" }, - { url = "https://files.pythonhosted.org/packages/e8/67/0c3c9179a3ad19791ef1b8f7138aa27d4578c78700551c60d9260b2c660d/ruff-0.12.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:560e0cd641e45591a3e42cb50ef61ce07162b9c233786663fdce2d8557d99818", size = 12847783, upload-time = "2025-08-07T19:05:28.14Z" }, - { url = "https://files.pythonhosted.org/packages/4e/2a/0b6ac3dd045acf8aa229b12c9c17bb35508191b71a14904baf99573a21bd/ruff-0.12.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:71c83121512e7743fba5a8848c261dcc454cafb3ef2934a43f1b7a4eb5a447ea", size = 11702672, upload-time = "2025-08-07T19:05:30.413Z" }, - { url = "https://files.pythonhosted.org/packages/9d/ee/f9fdc9f341b0430110de8b39a6ee5fa68c5706dc7c0aa940817947d6937e/ruff-0.12.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:de4429ef2ba091ecddedd300f4c3f24bca875d3d8b23340728c3cb0da81072c3", size = 11440626, upload-time = "2025-08-07T19:05:32.492Z" }, - { url = "https://files.pythonhosted.org/packages/89/fb/b3aa2d482d05f44e4d197d1de5e3863feb13067b22c571b9561085c999dc/ruff-0.12.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a2cab5f60d5b65b50fba39a8950c8746df1627d54ba1197f970763917184b161", size = 12462162, upload-time = "2025-08-07T19:05:34.449Z" }, - { url = "https://files.pythonhosted.org/packages/18/9f/5c5d93e1d00d854d5013c96e1a92c33b703a0332707a7cdbd0a4880a84fb/ruff-0.12.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:45c32487e14f60b88aad6be9fd5da5093dbefb0e3e1224131cb1d441d7cb7d46", size = 12913212, upload-time = "2025-08-07T19:05:36.541Z" }, - { url = "https://files.pythonhosted.org/packages/71/13/ab9120add1c0e4604c71bfc2e4ef7d63bebece0cfe617013da289539cef8/ruff-0.12.8-py3-none-win32.whl", hash = "sha256:daf3475060a617fd5bc80638aeaf2f5937f10af3ec44464e280a9d2218e720d3", size = 11694382, upload-time = "2025-08-07T19:05:38.468Z" }, - { url = "https://files.pythonhosted.org/packages/f6/dc/a2873b7c5001c62f46266685863bee2888caf469d1edac84bf3242074be2/ruff-0.12.8-py3-none-win_amd64.whl", hash = "sha256:7209531f1a1fcfbe8e46bcd7ab30e2f43604d8ba1c49029bb420b103d0b5f76e", size = 12740482, upload-time = "2025-08-07T19:05:40.391Z" }, - { url = "https://files.pythonhosted.org/packages/cb/5c/799a1efb8b5abab56e8a9f2a0b72d12bd64bb55815e9476c7d0a2887d2f7/ruff-0.12.8-py3-none-win_arm64.whl", hash = "sha256:c90e1a334683ce41b0e7a04f41790c429bf5073b62c1ae701c9dc5b3d14f0749", size = 11884718, upload-time = "2025-08-07T19:05:42.866Z" }, + { url = "https://files.pythonhosted.org/packages/ad/20/53bf098537adb7b6a97d98fcdebf6e916fcd11b2e21d15f8c171507909cc/ruff-0.12.9-py3-none-linux_armv6l.whl", hash = "sha256:fcebc6c79fcae3f220d05585229463621f5dbf24d79fdc4936d9302e177cfa3e", size = 11759705, upload-time = "2025-08-14T16:08:12.968Z" }, + { url = "https://files.pythonhosted.org/packages/20/4d/c764ee423002aac1ec66b9d541285dd29d2c0640a8086c87de59ebbe80d5/ruff-0.12.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aed9d15f8c5755c0e74467731a007fcad41f19bcce41cd75f768bbd687f8535f", size = 12527042, upload-time = "2025-08-14T16:08:16.54Z" }, + { url = "https://files.pythonhosted.org/packages/8b/45/cfcdf6d3eb5fc78a5b419e7e616d6ccba0013dc5b180522920af2897e1be/ruff-0.12.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5b15ea354c6ff0d7423814ba6d44be2807644d0c05e9ed60caca87e963e93f70", size = 11724457, upload-time = "2025-08-14T16:08:18.686Z" }, + { url = "https://files.pythonhosted.org/packages/72/e6/44615c754b55662200c48bebb02196dbb14111b6e266ab071b7e7297b4ec/ruff-0.12.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d596c2d0393c2502eaabfef723bd74ca35348a8dac4267d18a94910087807c53", size = 11949446, upload-time = "2025-08-14T16:08:21.059Z" }, + { url = "https://files.pythonhosted.org/packages/fd/d1/9b7d46625d617c7df520d40d5ac6cdcdf20cbccb88fad4b5ecd476a6bb8d/ruff-0.12.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1b15599931a1a7a03c388b9c5df1bfa62be7ede6eb7ef753b272381f39c3d0ff", size = 11566350, upload-time = "2025-08-14T16:08:23.433Z" }, + { url = "https://files.pythonhosted.org/packages/59/20/b73132f66f2856bc29d2d263c6ca457f8476b0bbbe064dac3ac3337a270f/ruff-0.12.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d02faa2977fb6f3f32ddb7828e212b7dd499c59eb896ae6c03ea5c303575756", size = 13270430, upload-time = "2025-08-14T16:08:25.837Z" }, + { url = "https://files.pythonhosted.org/packages/a2/21/eaf3806f0a3d4c6be0a69d435646fba775b65f3f2097d54898b0fd4bb12e/ruff-0.12.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:17d5b6b0b3a25259b69ebcba87908496e6830e03acfb929ef9fd4c58675fa2ea", size = 14264717, upload-time = "2025-08-14T16:08:27.907Z" }, + { url = "https://files.pythonhosted.org/packages/d2/82/1d0c53bd37dcb582b2c521d352fbf4876b1e28bc0d8894344198f6c9950d/ruff-0.12.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:72db7521860e246adbb43f6ef464dd2a532ef2ef1f5dd0d470455b8d9f1773e0", size = 13684331, upload-time = "2025-08-14T16:08:30.352Z" }, + { url = "https://files.pythonhosted.org/packages/3b/2f/1c5cf6d8f656306d42a686f1e207f71d7cebdcbe7b2aa18e4e8a0cb74da3/ruff-0.12.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a03242c1522b4e0885af63320ad754d53983c9599157ee33e77d748363c561ce", size = 12739151, upload-time = "2025-08-14T16:08:32.55Z" }, + { url = "https://files.pythonhosted.org/packages/47/09/25033198bff89b24d734e6479e39b1968e4c992e82262d61cdccaf11afb9/ruff-0.12.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fc83e4e9751e6c13b5046d7162f205d0a7bac5840183c5beebf824b08a27340", size = 12954992, upload-time = "2025-08-14T16:08:34.816Z" }, + { url = "https://files.pythonhosted.org/packages/52/8e/d0dbf2f9dca66c2d7131feefc386523404014968cd6d22f057763935ab32/ruff-0.12.9-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:881465ed56ba4dd26a691954650de6ad389a2d1fdb130fe51ff18a25639fe4bb", size = 12899569, upload-time = "2025-08-14T16:08:36.852Z" }, + { url = "https://files.pythonhosted.org/packages/a0/bd/b614d7c08515b1428ed4d3f1d4e3d687deffb2479703b90237682586fa66/ruff-0.12.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:43f07a3ccfc62cdb4d3a3348bf0588358a66da756aa113e071b8ca8c3b9826af", size = 11751983, upload-time = "2025-08-14T16:08:39.314Z" }, + { url = "https://files.pythonhosted.org/packages/58/d6/383e9f818a2441b1a0ed898d7875f11273f10882f997388b2b51cb2ae8b5/ruff-0.12.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:07adb221c54b6bba24387911e5734357f042e5669fa5718920ee728aba3cbadc", size = 11538635, upload-time = "2025-08-14T16:08:41.297Z" }, + { url = "https://files.pythonhosted.org/packages/20/9c/56f869d314edaa9fc1f491706d1d8a47747b9d714130368fbd69ce9024e9/ruff-0.12.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f5cd34fabfdea3933ab85d72359f118035882a01bff15bd1d2b15261d85d5f66", size = 12534346, upload-time = "2025-08-14T16:08:43.39Z" }, + { url = "https://files.pythonhosted.org/packages/bd/4b/d8b95c6795a6c93b439bc913ee7a94fda42bb30a79285d47b80074003ee7/ruff-0.12.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f6be1d2ca0686c54564da8e7ee9e25f93bdd6868263805f8c0b8fc6a449db6d7", size = 13017021, upload-time = "2025-08-14T16:08:45.889Z" }, + { url = "https://files.pythonhosted.org/packages/c7/c1/5f9a839a697ce1acd7af44836f7c2181cdae5accd17a5cb85fcbd694075e/ruff-0.12.9-py3-none-win32.whl", hash = "sha256:cc7a37bd2509974379d0115cc5608a1a4a6c4bff1b452ea69db83c8855d53f93", size = 11734785, upload-time = "2025-08-14T16:08:48.062Z" }, + { url = "https://files.pythonhosted.org/packages/fa/66/cdddc2d1d9a9f677520b7cfc490d234336f523d4b429c1298de359a3be08/ruff-0.12.9-py3-none-win_amd64.whl", hash = "sha256:6fb15b1977309741d7d098c8a3cb7a30bc112760a00fb6efb7abc85f00ba5908", size = 12840654, upload-time = "2025-08-14T16:08:50.158Z" }, + { url = "https://files.pythonhosted.org/packages/ac/fd/669816bc6b5b93b9586f3c1d87cd6bc05028470b3ecfebb5938252c47a35/ruff-0.12.9-py3-none-win_arm64.whl", hash = "sha256:63c8c819739d86b96d500cce885956a1a48ab056bbcbc61b747ad494b2485089", size = 11949623, upload-time = "2025-08-14T16:08:52.233Z" }, ] [[package]] @@ -4620,15 +4621,15 @@ wheels = [ [[package]] name = "sentry-sdk" -version = "2.34.1" +version = "2.35.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3a/38/10d6bfe23df1bfc65ac2262ed10b45823f47f810b0057d3feeea1ca5c7ed/sentry_sdk-2.34.1.tar.gz", hash = "sha256:69274eb8c5c38562a544c3e9f68b5be0a43be4b697f5fd385bf98e4fbe672687", size = 336969, upload-time = "2025-07-30T11:13:37.93Z" } +sdist = { url = "https://files.pythonhosted.org/packages/31/83/055dc157b719651ef13db569bb8cf2103df11174478649735c1b2bf3f6bc/sentry_sdk-2.35.0.tar.gz", hash = "sha256:5ea58d352779ce45d17bc2fa71ec7185205295b83a9dbb5707273deb64720092", size = 343014, upload-time = "2025-08-14T17:11:20.223Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/3e/bb34de65a5787f76848a533afbb6610e01fbcdd59e76d8679c254e02255c/sentry_sdk-2.34.1-py2.py3-none-any.whl", hash = "sha256:b7a072e1cdc5abc48101d5146e1ae680fa81fe886d8d95aaa25a0b450c818d32", size = 357743, upload-time = "2025-07-30T11:13:36.145Z" }, + { url = "https://files.pythonhosted.org/packages/36/3d/742617a7c644deb0c1628dcf6bb2d2165ab7c6aab56fe5222758994007f8/sentry_sdk-2.35.0-py2.py3-none-any.whl", hash = "sha256:6e0c29b9a5d34de8575ffb04d289a987ff3053cf2c98ede445bea995e3830263", size = 363806, upload-time = "2025-08-14T17:11:18.29Z" }, ] [[package]] @@ -4963,43 +4964,42 @@ wheels = [ [[package]] name = "zstandard" -version = "0.23.0" +version = "0.24.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation == 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ed/f6/2ac0287b442160a89d726b17a9184a4c615bb5237db763791a7fd16d9df1/zstandard-0.23.0.tar.gz", hash = "sha256:b2d8c62d08e7255f68f7a740bae85b3c9b8e5466baa9cbf7f57f1cde0ac6bc09", size = 681701, upload-time = "2024-07-15T00:18:06.141Z" } +sdist = { url = "https://files.pythonhosted.org/packages/09/1b/c20b2ef1d987627765dcd5bf1dadb8ef6564f00a87972635099bb76b7a05/zstandard-0.24.0.tar.gz", hash = "sha256:fe3198b81c00032326342d973e526803f183f97aa9e9a98e3f897ebafe21178f", size = 905681, upload-time = "2025-08-17T18:36:36.352Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/40/f67e7d2c25a0e2dc1744dd781110b0b60306657f8696cafb7ad7579469bd/zstandard-0.23.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:34895a41273ad33347b2fc70e1bff4240556de3c46c6ea430a7ed91f9042aa4e", size = 788699, upload-time = "2024-07-15T00:14:04.909Z" }, - { url = "https://files.pythonhosted.org/packages/e8/46/66d5b55f4d737dd6ab75851b224abf0afe5774976fe511a54d2eb9063a41/zstandard-0.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:77ea385f7dd5b5676d7fd943292ffa18fbf5c72ba98f7d09fc1fb9e819b34c23", size = 633681, upload-time = "2024-07-15T00:14:13.99Z" }, - { url = "https://files.pythonhosted.org/packages/63/b6/677e65c095d8e12b66b8f862b069bcf1f1d781b9c9c6f12eb55000d57583/zstandard-0.23.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:983b6efd649723474f29ed42e1467f90a35a74793437d0bc64a5bf482bedfa0a", size = 4944328, upload-time = "2024-07-15T00:14:16.588Z" }, - { url = "https://files.pythonhosted.org/packages/59/cc/e76acb4c42afa05a9d20827116d1f9287e9c32b7ad58cc3af0721ce2b481/zstandard-0.23.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80a539906390591dd39ebb8d773771dc4db82ace6372c4d41e2d293f8e32b8db", size = 5311955, upload-time = "2024-07-15T00:14:19.389Z" }, - { url = "https://files.pythonhosted.org/packages/78/e4/644b8075f18fc7f632130c32e8f36f6dc1b93065bf2dd87f03223b187f26/zstandard-0.23.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:445e4cb5048b04e90ce96a79b4b63140e3f4ab5f662321975679b5f6360b90e2", size = 5344944, upload-time = "2024-07-15T00:14:22.173Z" }, - { url = "https://files.pythonhosted.org/packages/76/3f/dbafccf19cfeca25bbabf6f2dd81796b7218f768ec400f043edc767015a6/zstandard-0.23.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd30d9c67d13d891f2360b2a120186729c111238ac63b43dbd37a5a40670b8ca", size = 5442927, upload-time = "2024-07-15T00:14:24.825Z" }, - { url = "https://files.pythonhosted.org/packages/0c/c3/d24a01a19b6733b9f218e94d1a87c477d523237e07f94899e1c10f6fd06c/zstandard-0.23.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d20fd853fbb5807c8e84c136c278827b6167ded66c72ec6f9a14b863d809211c", size = 4864910, upload-time = "2024-07-15T00:14:26.982Z" }, - { url = "https://files.pythonhosted.org/packages/1c/a9/cf8f78ead4597264f7618d0875be01f9bc23c9d1d11afb6d225b867cb423/zstandard-0.23.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ed1708dbf4d2e3a1c5c69110ba2b4eb6678262028afd6c6fbcc5a8dac9cda68e", size = 4935544, upload-time = "2024-07-15T00:14:29.582Z" }, - { url = "https://files.pythonhosted.org/packages/2c/96/8af1e3731b67965fb995a940c04a2c20997a7b3b14826b9d1301cf160879/zstandard-0.23.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:be9b5b8659dff1f913039c2feee1aca499cfbc19e98fa12bc85e037c17ec6ca5", size = 5467094, upload-time = "2024-07-15T00:14:40.126Z" }, - { url = "https://files.pythonhosted.org/packages/ff/57/43ea9df642c636cb79f88a13ab07d92d88d3bfe3e550b55a25a07a26d878/zstandard-0.23.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:65308f4b4890aa12d9b6ad9f2844b7ee42c7f7a4fd3390425b242ffc57498f48", size = 4860440, upload-time = "2024-07-15T00:14:42.786Z" }, - { url = "https://files.pythonhosted.org/packages/46/37/edb78f33c7f44f806525f27baa300341918fd4c4af9472fbc2c3094be2e8/zstandard-0.23.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:98da17ce9cbf3bfe4617e836d561e433f871129e3a7ac16d6ef4c680f13a839c", size = 4700091, upload-time = "2024-07-15T00:14:45.184Z" }, - { url = "https://files.pythonhosted.org/packages/c1/f1/454ac3962671a754f3cb49242472df5c2cced4eb959ae203a377b45b1a3c/zstandard-0.23.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8ed7d27cb56b3e058d3cf684d7200703bcae623e1dcc06ed1e18ecda39fee003", size = 5208682, upload-time = "2024-07-15T00:14:47.407Z" }, - { url = "https://files.pythonhosted.org/packages/85/b2/1734b0fff1634390b1b887202d557d2dd542de84a4c155c258cf75da4773/zstandard-0.23.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:b69bb4f51daf461b15e7b3db033160937d3ff88303a7bc808c67bbc1eaf98c78", size = 5669707, upload-time = "2024-07-15T00:15:03.529Z" }, - { url = "https://files.pythonhosted.org/packages/52/5a/87d6971f0997c4b9b09c495bf92189fb63de86a83cadc4977dc19735f652/zstandard-0.23.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:034b88913ecc1b097f528e42b539453fa82c3557e414b3de9d5632c80439a473", size = 5201792, upload-time = "2024-07-15T00:15:28.372Z" }, - { url = "https://files.pythonhosted.org/packages/79/02/6f6a42cc84459d399bd1a4e1adfc78d4dfe45e56d05b072008d10040e13b/zstandard-0.23.0-cp311-cp311-win32.whl", hash = "sha256:f2d4380bf5f62daabd7b751ea2339c1a21d1c9463f1feb7fc2bdcea2c29c3160", size = 430586, upload-time = "2024-07-15T00:15:32.26Z" }, - { url = "https://files.pythonhosted.org/packages/be/a2/4272175d47c623ff78196f3c10e9dc7045c1b9caf3735bf041e65271eca4/zstandard-0.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:62136da96a973bd2557f06ddd4e8e807f9e13cbb0bfb9cc06cfe6d98ea90dfe0", size = 495420, upload-time = "2024-07-15T00:15:34.004Z" }, - { url = "https://files.pythonhosted.org/packages/7b/83/f23338c963bd9de687d47bf32efe9fd30164e722ba27fb59df33e6b1719b/zstandard-0.23.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b4567955a6bc1b20e9c31612e615af6b53733491aeaa19a6b3b37f3b65477094", size = 788713, upload-time = "2024-07-15T00:15:35.815Z" }, - { url = "https://files.pythonhosted.org/packages/5b/b3/1a028f6750fd9227ee0b937a278a434ab7f7fdc3066c3173f64366fe2466/zstandard-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e172f57cd78c20f13a3415cc8dfe24bf388614324d25539146594c16d78fcc8", size = 633459, upload-time = "2024-07-15T00:15:37.995Z" }, - { url = "https://files.pythonhosted.org/packages/26/af/36d89aae0c1f95a0a98e50711bc5d92c144939efc1f81a2fcd3e78d7f4c1/zstandard-0.23.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0e166f698c5a3e914947388c162be2583e0c638a4703fc6a543e23a88dea3c1", size = 4945707, upload-time = "2024-07-15T00:15:39.872Z" }, - { url = "https://files.pythonhosted.org/packages/cd/2e/2051f5c772f4dfc0aae3741d5fc72c3dcfe3aaeb461cc231668a4db1ce14/zstandard-0.23.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12a289832e520c6bd4dcaad68e944b86da3bad0d339ef7989fb7e88f92e96072", size = 5306545, upload-time = "2024-07-15T00:15:41.75Z" }, - { url = "https://files.pythonhosted.org/packages/0a/9e/a11c97b087f89cab030fa71206963090d2fecd8eb83e67bb8f3ffb84c024/zstandard-0.23.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d50d31bfedd53a928fed6707b15a8dbeef011bb6366297cc435accc888b27c20", size = 5337533, upload-time = "2024-07-15T00:15:44.114Z" }, - { url = "https://files.pythonhosted.org/packages/fc/79/edeb217c57fe1bf16d890aa91a1c2c96b28c07b46afed54a5dcf310c3f6f/zstandard-0.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72c68dda124a1a138340fb62fa21b9bf4848437d9ca60bd35db36f2d3345f373", size = 5436510, upload-time = "2024-07-15T00:15:46.509Z" }, - { url = "https://files.pythonhosted.org/packages/81/4f/c21383d97cb7a422ddf1ae824b53ce4b51063d0eeb2afa757eb40804a8ef/zstandard-0.23.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53dd9d5e3d29f95acd5de6802e909ada8d8d8cfa37a3ac64836f3bc4bc5512db", size = 4859973, upload-time = "2024-07-15T00:15:49.939Z" }, - { url = "https://files.pythonhosted.org/packages/ab/15/08d22e87753304405ccac8be2493a495f529edd81d39a0870621462276ef/zstandard-0.23.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6a41c120c3dbc0d81a8e8adc73312d668cd34acd7725f036992b1b72d22c1772", size = 4936968, upload-time = "2024-07-15T00:15:52.025Z" }, - { url = "https://files.pythonhosted.org/packages/eb/fa/f3670a597949fe7dcf38119a39f7da49a8a84a6f0b1a2e46b2f71a0ab83f/zstandard-0.23.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:40b33d93c6eddf02d2c19f5773196068d875c41ca25730e8288e9b672897c105", size = 5467179, upload-time = "2024-07-15T00:15:54.971Z" }, - { url = "https://files.pythonhosted.org/packages/4e/a9/dad2ab22020211e380adc477a1dbf9f109b1f8d94c614944843e20dc2a99/zstandard-0.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9206649ec587e6b02bd124fb7799b86cddec350f6f6c14bc82a2b70183e708ba", size = 4848577, upload-time = "2024-07-15T00:15:57.634Z" }, - { url = "https://files.pythonhosted.org/packages/08/03/dd28b4484b0770f1e23478413e01bee476ae8227bbc81561f9c329e12564/zstandard-0.23.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76e79bc28a65f467e0409098fa2c4376931fd3207fbeb6b956c7c476d53746dd", size = 4693899, upload-time = "2024-07-15T00:16:00.811Z" }, - { url = "https://files.pythonhosted.org/packages/2b/64/3da7497eb635d025841e958bcd66a86117ae320c3b14b0ae86e9e8627518/zstandard-0.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:66b689c107857eceabf2cf3d3fc699c3c0fe8ccd18df2219d978c0283e4c508a", size = 5199964, upload-time = "2024-07-15T00:16:03.669Z" }, - { url = "https://files.pythonhosted.org/packages/43/a4/d82decbab158a0e8a6ebb7fc98bc4d903266bce85b6e9aaedea1d288338c/zstandard-0.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9c236e635582742fee16603042553d276cca506e824fa2e6489db04039521e90", size = 5655398, upload-time = "2024-07-15T00:16:06.694Z" }, - { url = "https://files.pythonhosted.org/packages/f2/61/ac78a1263bc83a5cf29e7458b77a568eda5a8f81980691bbc6eb6a0d45cc/zstandard-0.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a8fffdbd9d1408006baaf02f1068d7dd1f016c6bcb7538682622c556e7b68e35", size = 5191313, upload-time = "2024-07-15T00:16:09.758Z" }, - { url = "https://files.pythonhosted.org/packages/e7/54/967c478314e16af5baf849b6ee9d6ea724ae5b100eb506011f045d3d4e16/zstandard-0.23.0-cp312-cp312-win32.whl", hash = "sha256:dc1d33abb8a0d754ea4763bad944fd965d3d95b5baef6b121c0c9013eaf1907d", size = 430877, upload-time = "2024-07-15T00:16:11.758Z" }, - { url = "https://files.pythonhosted.org/packages/75/37/872d74bd7739639c4553bf94c84af7d54d8211b626b352bc57f0fd8d1e3f/zstandard-0.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:64585e1dba664dc67c7cdabd56c1e5685233fbb1fc1966cfba2a340ec0dfff7b", size = 495595, upload-time = "2024-07-15T00:16:13.731Z" }, + { url = "https://files.pythonhosted.org/packages/01/1f/5c72806f76043c0ef9191a2b65281dacdf3b65b0828eb13bb2c987c4fb90/zstandard-0.24.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:addfc23e3bd5f4b6787b9ca95b2d09a1a67ad5a3c318daaa783ff90b2d3a366e", size = 795228, upload-time = "2025-08-17T18:21:46.978Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ba/3059bd5cd834666a789251d14417621b5c61233bd46e7d9023ea8bc1043a/zstandard-0.24.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6b005bcee4be9c3984b355336283afe77b2defa76ed6b89332eced7b6fa68b68", size = 640520, upload-time = "2025-08-17T18:21:48.162Z" }, + { url = "https://files.pythonhosted.org/packages/57/07/f0e632bf783f915c1fdd0bf68614c4764cae9dd46ba32cbae4dd659592c3/zstandard-0.24.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:3f96a9130171e01dbb6c3d4d9925d604e2131a97f540e223b88ba45daf56d6fb", size = 5347682, upload-time = "2025-08-17T18:21:50.266Z" }, + { url = "https://files.pythonhosted.org/packages/a6/4c/63523169fe84773a7462cd090b0989cb7c7a7f2a8b0a5fbf00009ba7d74d/zstandard-0.24.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd0d3d16e63873253bad22b413ec679cf6586e51b5772eb10733899832efec42", size = 5057650, upload-time = "2025-08-17T18:21:52.634Z" }, + { url = "https://files.pythonhosted.org/packages/c6/16/49013f7ef80293f5cebf4c4229535a9f4c9416bbfd238560edc579815dbe/zstandard-0.24.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:b7a8c30d9bf4bd5e4dcfe26900bef0fcd9749acde45cdf0b3c89e2052fda9a13", size = 5404893, upload-time = "2025-08-17T18:21:54.54Z" }, + { url = "https://files.pythonhosted.org/packages/4d/38/78e8bcb5fc32a63b055f2b99e0be49b506f2351d0180173674f516cf8a7a/zstandard-0.24.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:52cd7d9fa0a115c9446abb79b06a47171b7d916c35c10e0c3aa6f01d57561382", size = 5452389, upload-time = "2025-08-17T18:21:56.822Z" }, + { url = "https://files.pythonhosted.org/packages/55/8a/81671f05619edbacd49bd84ce6899a09fc8299be20c09ae92f6618ccb92d/zstandard-0.24.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a0f6fc2ea6e07e20df48752e7700e02e1892c61f9a6bfbacaf2c5b24d5ad504b", size = 5558888, upload-time = "2025-08-17T18:21:58.68Z" }, + { url = "https://files.pythonhosted.org/packages/49/cc/e83feb2d7d22d1f88434defbaeb6e5e91f42a4f607b5d4d2d58912b69d67/zstandard-0.24.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e46eb6702691b24ddb3e31e88b4a499e31506991db3d3724a85bd1c5fc3cfe4e", size = 5048038, upload-time = "2025-08-17T18:22:00.642Z" }, + { url = "https://files.pythonhosted.org/packages/08/c3/7a5c57ff49ef8943877f85c23368c104c2aea510abb339a2dc31ad0a27c3/zstandard-0.24.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5e3b9310fd7f0d12edc75532cd9a56da6293840c84da90070d692e0bb15f186", size = 5573833, upload-time = "2025-08-17T18:22:02.402Z" }, + { url = "https://files.pythonhosted.org/packages/f9/00/64519983cd92535ba4bdd4ac26ac52db00040a52d6c4efb8d1764abcc343/zstandard-0.24.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:76cdfe7f920738ea871f035568f82bad3328cbc8d98f1f6988264096b5264efd", size = 4961072, upload-time = "2025-08-17T18:22:04.384Z" }, + { url = "https://files.pythonhosted.org/packages/72/ab/3a08a43067387d22994fc87c3113636aa34ccd2914a4d2d188ce365c5d85/zstandard-0.24.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3f2fe35ec84908dddf0fbf66b35d7c2878dbe349552dd52e005c755d3493d61c", size = 5268462, upload-time = "2025-08-17T18:22:06.095Z" }, + { url = "https://files.pythonhosted.org/packages/49/cf/2abb3a1ad85aebe18c53e7eca73223f1546ddfa3bf4d2fb83fc5a064c5ca/zstandard-0.24.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:aa705beb74ab116563f4ce784fa94771f230c05d09ab5de9c397793e725bb1db", size = 5443319, upload-time = "2025-08-17T18:22:08.572Z" }, + { url = "https://files.pythonhosted.org/packages/40/42/0dd59fc2f68f1664cda11c3b26abdf987f4e57cb6b6b0f329520cd074552/zstandard-0.24.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:aadf32c389bb7f02b8ec5c243c38302b92c006da565e120dfcb7bf0378f4f848", size = 5822355, upload-time = "2025-08-17T18:22:10.537Z" }, + { url = "https://files.pythonhosted.org/packages/99/c0/ea4e640fd4f7d58d6f87a1e7aca11fb886ac24db277fbbb879336c912f63/zstandard-0.24.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e40cd0fc734aa1d4bd0e7ad102fd2a1aefa50ce9ef570005ffc2273c5442ddc3", size = 5365257, upload-time = "2025-08-17T18:22:13.159Z" }, + { url = "https://files.pythonhosted.org/packages/27/a9/92da42a5c4e7e4003271f2e1f0efd1f37cfd565d763ad3604e9597980a1c/zstandard-0.24.0-cp311-cp311-win32.whl", hash = "sha256:cda61c46343809ecda43dc620d1333dd7433a25d0a252f2dcc7667f6331c7b61", size = 435559, upload-time = "2025-08-17T18:22:17.29Z" }, + { url = "https://files.pythonhosted.org/packages/e2/8e/2c8e5c681ae4937c007938f954a060fa7c74f36273b289cabdb5ef0e9a7e/zstandard-0.24.0-cp311-cp311-win_amd64.whl", hash = "sha256:3b95fc06489aa9388400d1aab01a83652bc040c9c087bd732eb214909d7fb0dd", size = 505070, upload-time = "2025-08-17T18:22:14.808Z" }, + { url = "https://files.pythonhosted.org/packages/52/10/a2f27a66bec75e236b575c9f7b0d7d37004a03aa2dcde8e2decbe9ed7b4d/zstandard-0.24.0-cp311-cp311-win_arm64.whl", hash = "sha256:ad9fd176ff6800a0cf52bcf59c71e5de4fa25bf3ba62b58800e0f84885344d34", size = 461507, upload-time = "2025-08-17T18:22:15.964Z" }, + { url = "https://files.pythonhosted.org/packages/26/e9/0bd281d9154bba7fc421a291e263911e1d69d6951aa80955b992a48289f6/zstandard-0.24.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a2bda8f2790add22773ee7a4e43c90ea05598bffc94c21c40ae0a9000b0133c3", size = 795710, upload-time = "2025-08-17T18:22:19.189Z" }, + { url = "https://files.pythonhosted.org/packages/36/26/b250a2eef515caf492e2d86732e75240cdac9d92b04383722b9753590c36/zstandard-0.24.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cc76de75300f65b8eb574d855c12518dc25a075dadb41dd18f6322bda3fe15d5", size = 640336, upload-time = "2025-08-17T18:22:20.466Z" }, + { url = "https://files.pythonhosted.org/packages/79/bf/3ba6b522306d9bf097aac8547556b98a4f753dc807a170becaf30dcd6f01/zstandard-0.24.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:d2b3b4bda1a025b10fe0269369475f420177f2cb06e0f9d32c95b4873c9f80b8", size = 5342533, upload-time = "2025-08-17T18:22:22.326Z" }, + { url = "https://files.pythonhosted.org/packages/ea/ec/22bc75bf054e25accdf8e928bc68ab36b4466809729c554ff3a1c1c8bce6/zstandard-0.24.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b84c6c210684286e504022d11ec294d2b7922d66c823e87575d8b23eba7c81f", size = 5062837, upload-time = "2025-08-17T18:22:24.416Z" }, + { url = "https://files.pythonhosted.org/packages/48/cc/33edfc9d286e517fb5b51d9c3210e5bcfce578d02a675f994308ca587ae1/zstandard-0.24.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c59740682a686bf835a1a4d8d0ed1eefe31ac07f1c5a7ed5f2e72cf577692b00", size = 5393855, upload-time = "2025-08-17T18:22:26.786Z" }, + { url = "https://files.pythonhosted.org/packages/73/36/59254e9b29da6215fb3a717812bf87192d89f190f23817d88cb8868c47ac/zstandard-0.24.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6324fde5cf5120fbf6541d5ff3c86011ec056e8d0f915d8e7822926a5377193a", size = 5451058, upload-time = "2025-08-17T18:22:28.885Z" }, + { url = "https://files.pythonhosted.org/packages/9a/c7/31674cb2168b741bbbe71ce37dd397c9c671e73349d88ad3bca9e9fae25b/zstandard-0.24.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51a86bd963de3f36688553926a84e550d45d7f9745bd1947d79472eca27fcc75", size = 5546619, upload-time = "2025-08-17T18:22:31.115Z" }, + { url = "https://files.pythonhosted.org/packages/e6/01/1a9f22239f08c00c156f2266db857545ece66a6fc0303d45c298564bc20b/zstandard-0.24.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d82ac87017b734f2fb70ff93818c66f0ad2c3810f61040f077ed38d924e19980", size = 5046676, upload-time = "2025-08-17T18:22:33.077Z" }, + { url = "https://files.pythonhosted.org/packages/a7/91/6c0cf8fa143a4988a0361380ac2ef0d7cb98a374704b389fbc38b5891712/zstandard-0.24.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92ea7855d5bcfb386c34557516c73753435fb2d4a014e2c9343b5f5ba148b5d8", size = 5576381, upload-time = "2025-08-17T18:22:35.391Z" }, + { url = "https://files.pythonhosted.org/packages/e2/77/1526080e22e78871e786ccf3c84bf5cec9ed25110a9585507d3c551da3d6/zstandard-0.24.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3adb4b5414febf074800d264ddf69ecade8c658837a83a19e8ab820e924c9933", size = 4953403, upload-time = "2025-08-17T18:22:37.266Z" }, + { url = "https://files.pythonhosted.org/packages/6e/d0/a3a833930bff01eab697eb8abeafb0ab068438771fa066558d96d7dafbf9/zstandard-0.24.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6374feaf347e6b83ec13cc5dcfa70076f06d8f7ecd46cc71d58fac798ff08b76", size = 5267396, upload-time = "2025-08-17T18:22:39.757Z" }, + { url = "https://files.pythonhosted.org/packages/f3/5e/90a0db9a61cd4769c06374297ecfcbbf66654f74cec89392519deba64d76/zstandard-0.24.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:13fc548e214df08d896ee5f29e1f91ee35db14f733fef8eabea8dca6e451d1e2", size = 5433269, upload-time = "2025-08-17T18:22:42.131Z" }, + { url = "https://files.pythonhosted.org/packages/ce/58/fc6a71060dd67c26a9c5566e0d7c99248cbe5abfda6b3b65b8f1a28d59f7/zstandard-0.24.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0a416814608610abf5488889c74e43ffa0343ca6cf43957c6b6ec526212422da", size = 5814203, upload-time = "2025-08-17T18:22:44.017Z" }, + { url = "https://files.pythonhosted.org/packages/5c/6a/89573d4393e3ecbfa425d9a4e391027f58d7810dec5cdb13a26e4cdeef5c/zstandard-0.24.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0d66da2649bb0af4471699aeb7a83d6f59ae30236fb9f6b5d20fb618ef6c6777", size = 5359622, upload-time = "2025-08-17T18:22:45.802Z" }, + { url = "https://files.pythonhosted.org/packages/60/ff/2cbab815d6f02a53a9d8d8703bc727d8408a2e508143ca9af6c3cca2054b/zstandard-0.24.0-cp312-cp312-win32.whl", hash = "sha256:ff19efaa33e7f136fe95f9bbcc90ab7fb60648453b03f95d1de3ab6997de0f32", size = 435968, upload-time = "2025-08-17T18:22:49.493Z" }, + { url = "https://files.pythonhosted.org/packages/ce/a3/8f96b8ddb7ad12344218fbd0fd2805702dafd126ae9f8a1fb91eef7b33da/zstandard-0.24.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc05f8a875eb651d1cc62e12a4a0e6afa5cd0cc231381adb830d2e9c196ea895", size = 505195, upload-time = "2025-08-17T18:22:47.193Z" }, + { url = "https://files.pythonhosted.org/packages/a3/4a/bfca20679da63bfc236634ef2e4b1b4254203098b0170e3511fee781351f/zstandard-0.24.0-cp312-cp312-win_arm64.whl", hash = "sha256:b04c94718f7a8ed7cdd01b162b6caa1954b3c9d486f00ecbbd300f149d2b2606", size = 461605, upload-time = "2025-08-17T18:22:48.317Z" }, ] From 31101ecaab71620cfdcc31dc3074117b78c7e10a Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Mon, 18 Aug 2025 15:37:44 -0700 Subject: [PATCH 27/64] AGNOS 12.8 (#36008) * staging * prod --- launch_env.sh | 2 +- system/hardware/tici/agnos.json | 12 ++++++------ system/hardware/tici/all-partitions.json | 12 ++++++------ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/launch_env.sh b/launch_env.sh index e1a0da9b67..4c011c6ac0 100755 --- a/launch_env.sh +++ b/launch_env.sh @@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1 export VECLIB_MAXIMUM_THREADS=1 if [ -z "$AGNOS_VERSION" ]; then - export AGNOS_VERSION="12.7" + export AGNOS_VERSION="12.8" fi export STAGING_ROOT="/data/safe_staging" diff --git a/system/hardware/tici/agnos.json b/system/hardware/tici/agnos.json index 0900c51d10..941a4956bf 100644 --- a/system/hardware/tici/agnos.json +++ b/system/hardware/tici/agnos.json @@ -67,17 +67,17 @@ }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63.img.xz", - "hash": "e6fbc73b6ef9551f57f123791f94f2f72db8ce59e9fba8ccd44c30685582368b", - "hash_raw": "6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63", + "url": "https://commadist.azureedge.net/agnosupdate/system-e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087.img.xz", + "hash": "1468d50b7ad0fda0f04074755d21e786e3b1b6ca5dd5b17eb2608202025e6126", + "hash_raw": "e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087", "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "af2a42284ecfddc9d8aa50fde0e2093ba18cf1dd2242a7a3fbe05f78f6ec0228", + "ondevice_hash": "242aa5adad1c04e1398e00e2440d1babf962022eb12b89adf2e60ee3068946e7", "alt": { - "hash": "6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63", - "url": "https://commadist.azureedge.net/agnosupdate/system-6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63.img", + "hash": "e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087", + "url": "https://commadist.azureedge.net/agnosupdate/system-e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087.img", "size": 5368709120 } } diff --git a/system/hardware/tici/all-partitions.json b/system/hardware/tici/all-partitions.json index 5d1bcc65a7..e49f93a066 100644 --- a/system/hardware/tici/all-partitions.json +++ b/system/hardware/tici/all-partitions.json @@ -350,17 +350,17 @@ }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63.img.xz", - "hash": "e6fbc73b6ef9551f57f123791f94f2f72db8ce59e9fba8ccd44c30685582368b", - "hash_raw": "6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63", + "url": "https://commadist.azureedge.net/agnosupdate/system-e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087.img.xz", + "hash": "1468d50b7ad0fda0f04074755d21e786e3b1b6ca5dd5b17eb2608202025e6126", + "hash_raw": "e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087", "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "af2a42284ecfddc9d8aa50fde0e2093ba18cf1dd2242a7a3fbe05f78f6ec0228", + "ondevice_hash": "242aa5adad1c04e1398e00e2440d1babf962022eb12b89adf2e60ee3068946e7", "alt": { - "hash": "6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63", - "url": "https://commadist.azureedge.net/agnosupdate/system-6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63.img", + "hash": "e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087", + "url": "https://commadist.azureedge.net/agnosupdate/system-e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087.img", "size": 5368709120 } }, From dfc66d780745bd2c393130d26162ed00452e6006 Mon Sep 17 00:00:00 2001 From: commaci-public <60409688+commaci-public@users.noreply.github.com> Date: Mon, 18 Aug 2025 18:52:05 -0700 Subject: [PATCH 28/64] [bot] Update Python packages (#36014) Update Python packages Co-authored-by: Vehicle Researcher --- opendbc_repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendbc_repo b/opendbc_repo index b2f34136c0..4b203ff5d1 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit b2f34136c0e58a76d4b4197003a852e72e48767e +Subproject commit 4b203ff5d1ad867de127de6b27382ba73e6e31a7 From 4d55671b17d44c66954ee1f471189fb748b4b8ff Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Mon, 18 Aug 2025 18:57:16 -0700 Subject: [PATCH 29/64] feedbackd: temp disable LKAS button as feedback (#36017) * feedbackd: temp disable LKAS button as feedback * disable that * mark --- RELEASES.md | 2 +- selfdrive/ui/feedback/feedbackd.py | 3 ++- selfdrive/ui/tests/test_feedbackd.py | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index e1c64efad4..cb7274e827 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,5 +1,6 @@ Version 0.10.1 (2025-09-08) ======================== +* Record driving feedback using LKAS button Version 0.10.0 (2025-08-05) ======================== @@ -10,7 +11,6 @@ Version 0.10.0 (2025-08-05) * Action from lateral MPC as training objective replaced by E2E planning from World Model * Low-speed lead car ground-truth fixes * Enable live-learned steering actuation delay -* Record driving feedback using LKAS button * Opt-in audio recording for dashcam video * Acura MDX 2025 support thanks to vanillagorillaa and MVL! * Honda Accord 2023-25 support thanks to vanillagorillaa and MVL! diff --git a/selfdrive/ui/feedback/feedbackd.py b/selfdrive/ui/feedback/feedbackd.py index b02e5d97a7..2d131a0d5e 100755 --- a/selfdrive/ui/feedback/feedbackd.py +++ b/selfdrive/ui/feedback/feedbackd.py @@ -22,7 +22,8 @@ def main(): sm.update() should_send_bookmark = False - if sm.updated['carState'] and sm['carState'].canValid: + # TODO: https://github.com/commaai/openpilot/issues/36015 + if False and sm.updated['carState'] and sm['carState'].canValid: for be in sm['carState'].buttonEvents: if be.type == ButtonType.lkas: if be.pressed: diff --git a/selfdrive/ui/tests/test_feedbackd.py b/selfdrive/ui/tests/test_feedbackd.py index c2d81aef83..6b7ec44863 100644 --- a/selfdrive/ui/tests/test_feedbackd.py +++ b/selfdrive/ui/tests/test_feedbackd.py @@ -5,6 +5,7 @@ from openpilot.common.params import Params from openpilot.system.manager.process_config import managed_processes +@pytest.mark.skip("tmp disabled") class TestFeedbackd: def setup_method(self): self.pm = messaging.PubMaster(['carState', 'rawAudioData']) From f55f3bb7cdda25e2bc7949917a37212d2725d890 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 18 Aug 2025 19:33:34 -0700 Subject: [PATCH 30/64] setup is a noun! --- docs/CONTRIBUTING.md | 2 +- docs/how-to/turn-the-speed-blue.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index f8c27b8815..154734b7fc 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -6,7 +6,7 @@ Development is coordinated through [Discord](https://discord.comma.ai) and GitHu ### Getting Started -* Setup your [development environment](/tools/) +* Set up your [development environment](/tools/) * Join our [Discord](https://discord.comma.ai) * Docs are at https://docs.comma.ai and https://blog.comma.ai diff --git a/docs/how-to/turn-the-speed-blue.md b/docs/how-to/turn-the-speed-blue.md index 64f4475dfa..13b3b03e80 100644 --- a/docs/how-to/turn-the-speed-blue.md +++ b/docs/how-to/turn-the-speed-blue.md @@ -1,11 +1,11 @@ # Turn the speed blue *A getting started guide for openpilot development* -In 30 minutes, we'll get an openpilot development environment setup on your computer and make some changes to openpilot's UI. +In 30 minutes, we'll get an openpilot development environment set up on your computer and make some changes to openpilot's UI. And if you have a comma 3/3X, we'll deploy the change to your device for testing. -## 1. Setup your development environment +## 1. Set up your development environment Run this to clone openpilot and install all the dependencies: ```bash From 2148e2dff2c915f79c93fe4a975bd631e4aa2f1f Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Mon, 18 Aug 2025 19:48:08 -0700 Subject: [PATCH 31/64] build_devel: clean submodules --- release/build_devel.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/release/build_devel.sh b/release/build_devel.sh index 05dcaafcaa..c4ef280258 100755 --- a/release/build_devel.sh +++ b/release/build_devel.sh @@ -30,6 +30,7 @@ git reset --hard __nightly git checkout __nightly git reset --hard origin/devel git clean -xdff +git submodule foreach --recursive git clean -xdff git lfs uninstall # remove everything except .git From 2cec2587be926bd4ef42dc8f92177324c05e997a Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 19 Aug 2025 09:18:14 -0700 Subject: [PATCH 32/64] bump panda --- panda | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda b/panda index 5b0f1a2eca..3dc2138623 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 5b0f1a2eca710121ef7573f1d183d4bc6c07b6ec +Subproject commit 3dc21386239e3073a623156b75901aa302340d6c From c085b8af19438956c15592828bd082803f43dfaf Mon Sep 17 00:00:00 2001 From: Jimmy <9859727+Quantizr@users.noreply.github.com> Date: Tue, 19 Aug 2025 09:18:32 -0700 Subject: [PATCH 33/64] feedbackd: remove lkas toggle for this release (#36018) remove lkas toggle for this release --- selfdrive/ui/layouts/settings/toggles.py | 10 ---------- selfdrive/ui/qt/offroad/settings.cc | 7 ------- 2 files changed, 17 deletions(-) diff --git a/selfdrive/ui/layouts/settings/toggles.py b/selfdrive/ui/layouts/settings/toggles.py index ff0564a61a..58afcec5ef 100644 --- a/selfdrive/ui/layouts/settings/toggles.py +++ b/selfdrive/ui/layouts/settings/toggles.py @@ -23,10 +23,6 @@ DESCRIPTIONS = { 'RecordFront': "Upload data from the driver facing camera and help improve the driver monitoring algorithm.", "IsMetric": "Display speed in km/h instead of mph.", "RecordAudio": "Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect.", - "RecordAudioFeedback": ( - "Press the LKAS button to record audio feedback about openpilot. When this toggle is disabled, the button acts as a bookmark button. " + - "The event will be highlighted in comma connect and the segment will be preserved on your device's storage." - ), } @@ -85,12 +81,6 @@ class TogglesLayout(Widget): self._params.get_bool("RecordAudio"), icon="microphone.png", ), - toggle_item( - "Record Audio Feedback with LKAS button", - DESCRIPTIONS["RecordAudioFeedback"], - self._params.get_bool("RecordAudioFeedback"), - icon="microphone.png", - ), toggle_item( "Use Metric System", DESCRIPTIONS["IsMetric"], self._params.get_bool("IsMetric"), icon="metric.png" ), diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index 01761295dc..96734d69d9 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -68,13 +68,6 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { "../assets/icons/microphone.png", true, }, - { - "RecordAudioFeedback", - tr("Record Audio Feedback with LKAS button"), - tr("Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage.\n\nNote that this feature is only compatible with select cars."), - "../assets/icons/microphone.png", - false, - }, { "IsMetric", tr("Use Metric System"), From 803b54ebdb7ca851c1b246bb150b5b72ccb3ad39 Mon Sep 17 00:00:00 2001 From: YassineYousfi Date: Tue, 19 Aug 2025 10:09:09 -0700 Subject: [PATCH 34/64] model parser: use check missing for mhp checks (#36020) * model parser: use check missing for mhp checks * lint + support re * lint... * no walrus * just remove --- selfdrive/modeld/modeld.py | 2 +- selfdrive/modeld/parse_model_outputs.py | 35 ++++++++++++++----------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 8bc8bf01ab..33da8f02b3 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -116,7 +116,7 @@ class ModelState: self.vision_output = np.zeros(vision_output_size, dtype=np.float32) self.policy_inputs = {k: Tensor(v, device='NPY').realize() for k,v in self.numpy_inputs.items()} self.policy_output = np.zeros(policy_output_size, dtype=np.float32) - self.parser = Parser() + self.parser = Parser(ignore_missing=('desired_curvature',)) with open(VISION_PKL_PATH, "rb") as f: self.vision_run = pickle.load(f) diff --git a/selfdrive/modeld/parse_model_outputs.py b/selfdrive/modeld/parse_model_outputs.py index 9e1c048735..5812e0b32e 100644 --- a/selfdrive/modeld/parse_model_outputs.py +++ b/selfdrive/modeld/parse_model_outputs.py @@ -22,9 +22,10 @@ class Parser: self.ignore_missing = ignore_missing def check_missing(self, outs, name): - if name not in outs and not self.ignore_missing: + missing = name not in outs + if missing and not self.ignore_missing: raise ValueError(f"Missing output {name}") - return name not in outs + return missing def parse_categorical_crossentropy(self, name, outs, out_shape=None): if self.check_missing(outs, name): @@ -84,6 +85,13 @@ class Parser: outs[name] = pred_mu_final.reshape(final_shape) outs[name + '_stds'] = pred_std_final.reshape(final_shape) + def is_mhp(self, outs, name, shape): + if self.check_missing(outs, name): + return False + if outs[name].shape[1] == 2 * shape: + return False + return True + def parse_vision_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]: self.parse_mdn('pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,)) self.parse_mdn('wide_from_device_euler', outs, in_N=0, out_N=0, out_shape=(ModelConstants.WIDE_FROM_DEVICE_WIDTH,)) @@ -94,23 +102,18 @@ class Parser: self.parse_categorical_crossentropy('desire_pred', outs, out_shape=(ModelConstants.DESIRE_PRED_LEN,ModelConstants.DESIRE_PRED_WIDTH)) self.parse_binary_crossentropy('meta', outs) self.parse_binary_crossentropy('lead_prob', outs) - if outs['lead'].shape[1] == 2 * ModelConstants.LEAD_MHP_SELECTION *ModelConstants.LEAD_TRAJ_LEN * ModelConstants.LEAD_WIDTH: - self.parse_mdn('lead', outs, in_N=0, out_N=0, - out_shape=(ModelConstants.LEAD_MHP_SELECTION, ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH)) - else: - self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION, - out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH)) + lead_mhp = self.is_mhp(outs, 'lead', ModelConstants.LEAD_MHP_SELECTION * ModelConstants.LEAD_TRAJ_LEN * ModelConstants.LEAD_WIDTH) + lead_in_N, lead_out_N = (ModelConstants.LEAD_MHP_N, ModelConstants.LEAD_MHP_SELECTION) if lead_mhp else (0, 0) + self.parse_mdn( + 'lead', outs, in_N=lead_in_N, out_N=lead_out_N, + out_shape=(ModelConstants.LEAD_MHP_SELECTION, ModelConstants.LEAD_TRAJ_LEN, ModelConstants.LEAD_WIDTH) + ) return outs def parse_policy_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]: - if outs['plan'].shape[1] == 2 * ModelConstants.IDX_N * ModelConstants.PLAN_WIDTH: - self.parse_mdn('plan', outs, in_N=0, out_N=0, - out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH)) - else: - self.parse_mdn('plan', outs, in_N=ModelConstants.PLAN_MHP_N, out_N=ModelConstants.PLAN_MHP_SELECTION, - out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH)) - if 'desired_curvature' in outs: - self.parse_mdn('desired_curvature', outs, in_N=0, out_N=0, out_shape=(ModelConstants.DESIRED_CURV_WIDTH,)) + plan_mhp = self.is_mhp(outs, 'plan', ModelConstants.IDX_N * ModelConstants.PLAN_WIDTH) + plan_in_N, plan_out_N = (ModelConstants.PLAN_MHP_N, ModelConstants.PLAN_MHP_SELECTION) if plan_mhp else (0, 0) + self.parse_mdn('plan', outs, in_N=plan_in_N, out_N=plan_out_N, out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH)) self.parse_categorical_crossentropy('desire_state', outs, out_shape=(ModelConstants.DESIRE_PRED_WIDTH,)) return outs From 51314fa9fef0350ed389932c39a30d746e73137f Mon Sep 17 00:00:00 2001 From: YassineYousfi Date: Tue, 19 Aug 2025 10:09:59 -0700 Subject: [PATCH 35/64] Revert "model parser: use check missing for mhp checks" (#36022) Revert "model parser: use check missing for mhp checks (#36020)" This reverts commit 803b54ebdb7ca851c1b246bb150b5b72ccb3ad39. --- selfdrive/modeld/modeld.py | 2 +- selfdrive/modeld/parse_model_outputs.py | 35 +++++++++++-------------- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 33da8f02b3..8bc8bf01ab 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -116,7 +116,7 @@ class ModelState: self.vision_output = np.zeros(vision_output_size, dtype=np.float32) self.policy_inputs = {k: Tensor(v, device='NPY').realize() for k,v in self.numpy_inputs.items()} self.policy_output = np.zeros(policy_output_size, dtype=np.float32) - self.parser = Parser(ignore_missing=('desired_curvature',)) + self.parser = Parser() with open(VISION_PKL_PATH, "rb") as f: self.vision_run = pickle.load(f) diff --git a/selfdrive/modeld/parse_model_outputs.py b/selfdrive/modeld/parse_model_outputs.py index 5812e0b32e..9e1c048735 100644 --- a/selfdrive/modeld/parse_model_outputs.py +++ b/selfdrive/modeld/parse_model_outputs.py @@ -22,10 +22,9 @@ class Parser: self.ignore_missing = ignore_missing def check_missing(self, outs, name): - missing = name not in outs - if missing and not self.ignore_missing: + if name not in outs and not self.ignore_missing: raise ValueError(f"Missing output {name}") - return missing + return name not in outs def parse_categorical_crossentropy(self, name, outs, out_shape=None): if self.check_missing(outs, name): @@ -85,13 +84,6 @@ class Parser: outs[name] = pred_mu_final.reshape(final_shape) outs[name + '_stds'] = pred_std_final.reshape(final_shape) - def is_mhp(self, outs, name, shape): - if self.check_missing(outs, name): - return False - if outs[name].shape[1] == 2 * shape: - return False - return True - def parse_vision_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]: self.parse_mdn('pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,)) self.parse_mdn('wide_from_device_euler', outs, in_N=0, out_N=0, out_shape=(ModelConstants.WIDE_FROM_DEVICE_WIDTH,)) @@ -102,18 +94,23 @@ class Parser: self.parse_categorical_crossentropy('desire_pred', outs, out_shape=(ModelConstants.DESIRE_PRED_LEN,ModelConstants.DESIRE_PRED_WIDTH)) self.parse_binary_crossentropy('meta', outs) self.parse_binary_crossentropy('lead_prob', outs) - lead_mhp = self.is_mhp(outs, 'lead', ModelConstants.LEAD_MHP_SELECTION * ModelConstants.LEAD_TRAJ_LEN * ModelConstants.LEAD_WIDTH) - lead_in_N, lead_out_N = (ModelConstants.LEAD_MHP_N, ModelConstants.LEAD_MHP_SELECTION) if lead_mhp else (0, 0) - self.parse_mdn( - 'lead', outs, in_N=lead_in_N, out_N=lead_out_N, - out_shape=(ModelConstants.LEAD_MHP_SELECTION, ModelConstants.LEAD_TRAJ_LEN, ModelConstants.LEAD_WIDTH) - ) + if outs['lead'].shape[1] == 2 * ModelConstants.LEAD_MHP_SELECTION *ModelConstants.LEAD_TRAJ_LEN * ModelConstants.LEAD_WIDTH: + self.parse_mdn('lead', outs, in_N=0, out_N=0, + out_shape=(ModelConstants.LEAD_MHP_SELECTION, ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH)) + else: + self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION, + out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH)) return outs def parse_policy_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]: - plan_mhp = self.is_mhp(outs, 'plan', ModelConstants.IDX_N * ModelConstants.PLAN_WIDTH) - plan_in_N, plan_out_N = (ModelConstants.PLAN_MHP_N, ModelConstants.PLAN_MHP_SELECTION) if plan_mhp else (0, 0) - self.parse_mdn('plan', outs, in_N=plan_in_N, out_N=plan_out_N, out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH)) + if outs['plan'].shape[1] == 2 * ModelConstants.IDX_N * ModelConstants.PLAN_WIDTH: + self.parse_mdn('plan', outs, in_N=0, out_N=0, + out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH)) + else: + self.parse_mdn('plan', outs, in_N=ModelConstants.PLAN_MHP_N, out_N=ModelConstants.PLAN_MHP_SELECTION, + out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH)) + if 'desired_curvature' in outs: + self.parse_mdn('desired_curvature', outs, in_N=0, out_N=0, out_shape=(ModelConstants.DESIRED_CURV_WIDTH,)) self.parse_categorical_crossentropy('desire_state', outs, out_shape=(ModelConstants.DESIRE_PRED_WIDTH,)) return outs From 3d24225cc151732ab8d1501f24e8da3f027b3a64 Mon Sep 17 00:00:00 2001 From: YassineYousfi Date: Tue, 19 Aug 2025 10:19:00 -0700 Subject: [PATCH 36/64] model parser: use check missing for mhp checks (#36023) * model parser: use check missing for mhp checks * lint + support re * lint... * no walrus * just remove * forgot this --- selfdrive/modeld/parse_model_outputs.py | 35 ++++++++++++++----------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/selfdrive/modeld/parse_model_outputs.py b/selfdrive/modeld/parse_model_outputs.py index 9e1c048735..5812e0b32e 100644 --- a/selfdrive/modeld/parse_model_outputs.py +++ b/selfdrive/modeld/parse_model_outputs.py @@ -22,9 +22,10 @@ class Parser: self.ignore_missing = ignore_missing def check_missing(self, outs, name): - if name not in outs and not self.ignore_missing: + missing = name not in outs + if missing and not self.ignore_missing: raise ValueError(f"Missing output {name}") - return name not in outs + return missing def parse_categorical_crossentropy(self, name, outs, out_shape=None): if self.check_missing(outs, name): @@ -84,6 +85,13 @@ class Parser: outs[name] = pred_mu_final.reshape(final_shape) outs[name + '_stds'] = pred_std_final.reshape(final_shape) + def is_mhp(self, outs, name, shape): + if self.check_missing(outs, name): + return False + if outs[name].shape[1] == 2 * shape: + return False + return True + def parse_vision_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]: self.parse_mdn('pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,)) self.parse_mdn('wide_from_device_euler', outs, in_N=0, out_N=0, out_shape=(ModelConstants.WIDE_FROM_DEVICE_WIDTH,)) @@ -94,23 +102,18 @@ class Parser: self.parse_categorical_crossentropy('desire_pred', outs, out_shape=(ModelConstants.DESIRE_PRED_LEN,ModelConstants.DESIRE_PRED_WIDTH)) self.parse_binary_crossentropy('meta', outs) self.parse_binary_crossentropy('lead_prob', outs) - if outs['lead'].shape[1] == 2 * ModelConstants.LEAD_MHP_SELECTION *ModelConstants.LEAD_TRAJ_LEN * ModelConstants.LEAD_WIDTH: - self.parse_mdn('lead', outs, in_N=0, out_N=0, - out_shape=(ModelConstants.LEAD_MHP_SELECTION, ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH)) - else: - self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION, - out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH)) + lead_mhp = self.is_mhp(outs, 'lead', ModelConstants.LEAD_MHP_SELECTION * ModelConstants.LEAD_TRAJ_LEN * ModelConstants.LEAD_WIDTH) + lead_in_N, lead_out_N = (ModelConstants.LEAD_MHP_N, ModelConstants.LEAD_MHP_SELECTION) if lead_mhp else (0, 0) + self.parse_mdn( + 'lead', outs, in_N=lead_in_N, out_N=lead_out_N, + out_shape=(ModelConstants.LEAD_MHP_SELECTION, ModelConstants.LEAD_TRAJ_LEN, ModelConstants.LEAD_WIDTH) + ) return outs def parse_policy_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]: - if outs['plan'].shape[1] == 2 * ModelConstants.IDX_N * ModelConstants.PLAN_WIDTH: - self.parse_mdn('plan', outs, in_N=0, out_N=0, - out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH)) - else: - self.parse_mdn('plan', outs, in_N=ModelConstants.PLAN_MHP_N, out_N=ModelConstants.PLAN_MHP_SELECTION, - out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH)) - if 'desired_curvature' in outs: - self.parse_mdn('desired_curvature', outs, in_N=0, out_N=0, out_shape=(ModelConstants.DESIRED_CURV_WIDTH,)) + plan_mhp = self.is_mhp(outs, 'plan', ModelConstants.IDX_N * ModelConstants.PLAN_WIDTH) + plan_in_N, plan_out_N = (ModelConstants.PLAN_MHP_N, ModelConstants.PLAN_MHP_SELECTION) if plan_mhp else (0, 0) + self.parse_mdn('plan', outs, in_N=plan_in_N, out_N=plan_out_N, out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH)) self.parse_categorical_crossentropy('desire_state', outs, out_shape=(ModelConstants.DESIRE_PRED_WIDTH,)) return outs From 560c503871b100fa5b2d5c05369d10c40813518a Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 19 Aug 2025 11:19:58 -0700 Subject: [PATCH 37/64] new release flow (#36021) * new release flow * Update README.md --- release/README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/release/README.md b/release/README.md index 0ceb765597..266d881d3c 100644 --- a/release/README.md +++ b/release/README.md @@ -3,33 +3,33 @@ ``` ## release checklist -**Go to `devel-staging`** -- [ ] make issue to track release +**Go to staging** +- [ ] make a GitHub issue to track release +- [ ] create release master branch - [ ] update RELEASES.md -- [ ] trigger new nightly build: https://github.com/commaai/openpilot/actions/workflows/release.yaml -- [ ] update `devel-staging`: `git reset --hard origin/__nightly` +- [ ] bump version on master: `common/version.h` and `RELEASES.md` - [ ] build new userdata partition from `release3-staging` -- [ ] open a pull request from `devel-staging` to `devel` - [ ] post on Discord, tag `@release crew` -**Go to `devel`** -- [ ] bump version on master: `common/version.h` and `RELEASES.md` -- [ ] before merging the pull request, test the following: +Updates to staging: +- [ ] either rebase on master or cherry-pick changes +- [ ] run this to update: `BRANCH=devel-staging release/build_devel.sh` + +**Go to release** +- [ ] before going to release, test the following: - [ ] update from previous release -> new release - [ ] update from new release -> previous release - [ ] fresh install with `openpilot-test.comma.ai` - [ ] drive on fresh install - [ ] no submodules or LFS - [ ] check sentry, MTBF, etc. - - [ ] stress test in production - -**Go to `release3`** + - [ ] stress test passes in production - [ ] publish the blog post - [ ] `git reset --hard origin/release3-staging` - [ ] tag the release: `git tag v0.X.X && git push origin v0.X.X` - [ ] create GitHub release - [ ] final test install on `openpilot.comma.ai` - [ ] update factory provisioning -- [ ] close out milestone +- [ ] close out milestone and issue - [ ] post on Discord, X, etc. ``` From d097a0c201d407f61ccdb18058919d05dccea3ac Mon Sep 17 00:00:00 2001 From: YassineYousfi Date: Tue, 19 Aug 2025 11:35:22 -0700 Subject: [PATCH 38/64] model parser: fix lead mhp out shape (#36024) * model parser: fix lead mhp out shape * fix for real --- selfdrive/modeld/parse_model_outputs.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/selfdrive/modeld/parse_model_outputs.py b/selfdrive/modeld/parse_model_outputs.py index 5812e0b32e..038f51ca9c 100644 --- a/selfdrive/modeld/parse_model_outputs.py +++ b/selfdrive/modeld/parse_model_outputs.py @@ -104,16 +104,15 @@ class Parser: self.parse_binary_crossentropy('lead_prob', outs) lead_mhp = self.is_mhp(outs, 'lead', ModelConstants.LEAD_MHP_SELECTION * ModelConstants.LEAD_TRAJ_LEN * ModelConstants.LEAD_WIDTH) lead_in_N, lead_out_N = (ModelConstants.LEAD_MHP_N, ModelConstants.LEAD_MHP_SELECTION) if lead_mhp else (0, 0) - self.parse_mdn( - 'lead', outs, in_N=lead_in_N, out_N=lead_out_N, - out_shape=(ModelConstants.LEAD_MHP_SELECTION, ModelConstants.LEAD_TRAJ_LEN, ModelConstants.LEAD_WIDTH) - ) + lead_out_shape = (ModelConstants.LEAD_TRAJ_LEN, ModelConstants.LEAD_WIDTH) if lead_mhp else \ + (ModelConstants.LEAD_MHP_SELECTION, ModelConstants.LEAD_TRAJ_LEN, ModelConstants.LEAD_WIDTH) + self.parse_mdn('lead', outs, in_N=lead_in_N, out_N=lead_out_N, out_shape=lead_out_shape) return outs def parse_policy_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]: plan_mhp = self.is_mhp(outs, 'plan', ModelConstants.IDX_N * ModelConstants.PLAN_WIDTH) plan_in_N, plan_out_N = (ModelConstants.PLAN_MHP_N, ModelConstants.PLAN_MHP_SELECTION) if plan_mhp else (0, 0) - self.parse_mdn('plan', outs, in_N=plan_in_N, out_N=plan_out_N, out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH)) + self.parse_mdn('plan', outs, in_N=plan_in_N, out_N=plan_out_N, out_shape=(ModelConstants.IDX_N, ModelConstants.PLAN_WIDTH)) self.parse_categorical_crossentropy('desire_state', outs, out_shape=(ModelConstants.DESIRE_PRED_WIDTH,)) return outs From 09aa21390dda71c49cf82df9fd9455f927f0db3c Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Tue, 19 Aug 2025 15:38:55 -0400 Subject: [PATCH 39/64] Honda: Adding support for Honda City (#36026) * bump opendbc * release notes * regen CARS.md * bump opendbc correctly this time --- RELEASES.md | 1 + docs/CARS.md | 6 ++++-- opendbc_repo | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index cb7274e827..dacf0eaa17 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,6 +1,7 @@ Version 0.10.1 (2025-09-08) ======================== * Record driving feedback using LKAS button +* Honda City 2023 support thanks to drFritz! Version 0.10.0 (2025-08-05) ======================== diff --git a/docs/CARS.md b/docs/CARS.md index fcb87236c8..7269f25737 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,7 +4,7 @@ A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified. -# 319 Supported Cars +# 321 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Hardware Needed
 |Video|Setup Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| @@ -76,10 +76,12 @@ A supported vehicle is one that just works when you install a comma device. All |Honda|Accord 2023-25|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Accord Hybrid 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Accord Hybrid 2023-25|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|City (Brazil only) 2023|All|openpilot available[1](#footnotes)|0 mph|14 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Civic 2019-21|All|openpilot available[1](#footnotes)|0 mph|2 mph[5](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Civic 2022-24|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| -|Honda|Civic Hatchback 2017-21|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Civic Hatchback 2017-18|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Civic Hatchback 2019-21|All|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Civic Hatchback 2022-24|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Civic Hatchback Hybrid 2025|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Civic Hatchback Hybrid (Europe only) 2023|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| diff --git a/opendbc_repo b/opendbc_repo index 4b203ff5d1..43006b9a41 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 4b203ff5d1ad867de127de6b27382ba73e6e31a7 +Subproject commit 43006b9a41e233325cb7cbcb6ff40de0234217a0 From 6005b12f940b96f14ccf87fb14c37a9c2fca8948 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 19 Aug 2025 15:04:17 -0700 Subject: [PATCH 40/64] format logreader --- tools/lib/logreader.py | 2 ++ tools/lib/route.py | 7 +++---- tools/lib/url_file.py | 18 ++++++++++-------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index bedafb8566..833f9825bb 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -40,6 +40,7 @@ def save_log(dest, log_msgs, compress=True): with open(dest, "wb") as f: f.write(dat) + def decompress_stream(data: bytes): dctx = zstd.ZstdDecompressor() decompressed_data = b"" @@ -353,6 +354,7 @@ class LogReader: def time_series(self): return msgs_to_time_series(self) + if __name__ == "__main__": import codecs diff --git a/tools/lib/route.py b/tools/lib/route.py index 882585a151..1fc26fb996 100644 --- a/tools/lib/route.py +++ b/tools/lib/route.py @@ -231,7 +231,6 @@ class RouteName: def __str__(self) -> str: return self._canonical_name - class SegmentName: # TODO: add constructor that takes dongle_id, time_str, segment_num and then create instances # of this class instead of manually constructing a segment name (use canonical_name prop instead) @@ -252,7 +251,7 @@ class SegmentName: @property def canonical_name(self) -> str: return self._canonical_name - #TODO should only use one name + # TODO should only use one name @property def data_name(self) -> str: return f"{self._route_name.canonical_name}/{self._num}" @@ -283,7 +282,7 @@ class SegmentName: @staticmethod def from_file_name(file_name): # ??????/xxxxxxxxxxxxxxxx|1111-11-11-11--11-11-11/1/rlog.bz2 - dongle_id, route_name, segment_num = file_name.replace('|','/').split('/')[-4:-1] + dongle_id, route_name, segment_num = file_name.replace('|', '/').split('/')[-4:-1] return SegmentName(dongle_id + "|" + route_name + "--" + segment_num) @staticmethod @@ -304,6 +303,7 @@ class SegmentName: dongle_id, route_name, segment_num = prefix.split("/") return SegmentName(dongle_id + "|" + route_name + "--" + segment_num) + @cache def get_max_seg_number_cached(sr: 'SegmentRange') -> int: try: @@ -365,4 +365,3 @@ class SegmentRange: def __repr__(self) -> str: return self.__str__() - diff --git a/tools/lib/url_file.py b/tools/lib/url_file.py index 204726363d..e80ba1399d 100644 --- a/tools/lib/url_file.py +++ b/tools/lib/url_file.py @@ -9,12 +9,14 @@ from urllib3.util import Timeout from openpilot.common.file_helpers import atomic_write_in_dir from openpilot.system.hardware.hw import Paths + # Cache chunk size K = 1000 CHUNK_SIZE = 1000 * K logging.getLogger("urllib3").setLevel(logging.WARNING) + def hash_256(link: str) -> str: return sha256((link.split("?")[0]).encode('utf-8')).hexdigest() @@ -24,7 +26,7 @@ class URLFileException(Exception): class URLFile: - _pool_manager: PoolManager|None = None + _pool_manager: PoolManager | None = None @staticmethod def reset() -> None: @@ -33,16 +35,16 @@ class URLFile: @staticmethod def pool_manager() -> PoolManager: if URLFile._pool_manager is None: - socket_options = [(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1),] + socket_options = [(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)] retries = Retry(total=5, backoff_factor=0.5, status_forcelist=[409, 429, 503, 504]) URLFile._pool_manager = PoolManager(num_pools=10, maxsize=100, socket_options=socket_options, retries=retries) return URLFile._pool_manager - def __init__(self, url: str, timeout: int=10, debug: bool=False, cache: bool|None=None): + def __init__(self, url: str, timeout: int = 10, debug: bool = False, cache: bool | None = None): self._url = url self._timeout = Timeout(connect=timeout, read=timeout) self._pos = 0 - self._length: int|None = None + self._length: int | None = None self._debug = debug # True by default, false if FILEREADER_CACHE is defined, but can be overwritten by the cache input self._force_download = not int(os.environ.get("FILEREADER_CACHE", "0")) @@ -58,7 +60,7 @@ class URLFile: def __exit__(self, exc_type, exc_value, traceback) -> None: pass - def _request(self, method: str, url: str, headers: dict[str, str]|None=None) -> BaseHTTPResponse: + def _request(self, method: str, url: str, headers: dict[str, str] | None = None) -> BaseHTTPResponse: return URLFile.pool_manager().request(method, url, timeout=self._timeout, headers=headers) def get_length_online(self) -> int: @@ -85,7 +87,7 @@ class URLFile: file_length.write(str(self._length)) return self._length - def read(self, ll: int|None=None) -> bytes: + def read(self, ll: int | None = None) -> bytes: if self._force_download: return self.read_aux(ll=ll) @@ -117,7 +119,7 @@ class URLFile: self._pos = file_end return response - def read_aux(self, ll: int|None=None) -> bytes: + def read_aux(self, ll: int | None = None) -> bytes: download_range = False headers = {} if self._pos != 0 or ll is not None: @@ -152,7 +154,7 @@ class URLFile: self._pos += len(ret) return ret - def seek(self, pos:int) -> None: + def seek(self, pos: int) -> None: self._pos = pos @property From 927548621be1be0c2c9063868b93d1f5020904de Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 19 Aug 2025 15:37:39 -0700 Subject: [PATCH 41/64] update to latest userdata partition (#36027) bump --- system/hardware/tici/all-partitions.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/system/hardware/tici/all-partitions.json b/system/hardware/tici/all-partitions.json index e49f93a066..5891e2748a 100644 --- a/system/hardware/tici/all-partitions.json +++ b/system/hardware/tici/all-partitions.json @@ -366,35 +366,35 @@ }, { "name": "userdata_90", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-ad38266b508280f22d02c3749322291eb083e08580ff8b7014add6989d290b12.img.xz", - "hash": "59e1bb2606b65293721dd89bcdc8d0c5cae47718c213eb40f235149aa5a408ae", - "hash_raw": "ad38266b508280f22d02c3749322291eb083e08580ff8b7014add6989d290b12", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-602d5103cba97e1b07f76508d5febb47cfc4463a7e31bd20e461b55c801feb0a.img.xz", + "hash": "6a11d448bac50467791809339051eed2894aae971c37bf6284b3b972a99ba3ac", + "hash_raw": "602d5103cba97e1b07f76508d5febb47cfc4463a7e31bd20e461b55c801feb0a", "size": 96636764160, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "40b5e666ec137a863178b51a7947e3bb76fe9259584d84e6a66361fabced3da5" + "ondevice_hash": "e014d92940a696bf8582807259820ab73948b950656ed83a45da738f26083705" }, { "name": "userdata_89", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-2f78f798861e0a3dd55af87e7a0c1982502a9293340d3eee6f3b8c9c88878e33.img.xz", - "hash": "e555a29c3ccb547ed840085c052cd1c6126c32d5c6dafe1712f593ae631be190", - "hash_raw": "2f78f798861e0a3dd55af87e7a0c1982502a9293340d3eee6f3b8c9c88878e33", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-4d7f6d12a5557eb6e3cbff9a4cd595677456fdfddcc879eddcea96a43a9d8b48.img.xz", + "hash": "748e31a5fc01fc256c012e359c3382d1f98cce98feafe8ecc0fca3e47caef116", + "hash_raw": "4d7f6d12a5557eb6e3cbff9a4cd595677456fdfddcc879eddcea96a43a9d8b48", "size": 95563022336, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "f5bc368dbe52ac7800634f74b1fd764fc3b6e68337984f40fd59222b1276d9f2" + "ondevice_hash": "c181b93050787adcfef730c086bcb780f28508d84e6376d9b80d37e5dc02b55e" }, { "name": "userdata_30", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-6367d73ba6a32823035c9941168ca3af3704c783f6bcd912f70afe1dbdfdd14b.img.xz", - "hash": "f5dfbe1dcba25b9a1920259bc89f52b5e406519539ff0112d5469fff3f1b6dba", - "hash_raw": "6367d73ba6a32823035c9941168ca3af3704c783f6bcd912f70afe1dbdfdd14b", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-80a76c8e56bbd7536fd5e87e8daa12984e2960db4edeb1f83229b2baeecc4668.img.xz", + "hash": "09ff390e639e4373d772e1688d05a5ac77a573463ed1deeff86390686fa686f9", + "hash_raw": "80a76c8e56bbd7536fd5e87e8daa12984e2960db4edeb1f83229b2baeecc4668", "size": 32212254720, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "b8f447f0ea40faae7292d3a0dc380d194caab6ec3f5007eda5661f2aa4d2f4ab" + "ondevice_hash": "2c01ab470c02121c721ff6afc25582437e821686207f3afef659387afb69c507" } ] \ No newline at end of file From 5ec9aee2166ec2fefd15128360e4ca26180e6afb Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 19 Aug 2025 15:39:44 -0700 Subject: [PATCH 42/64] File sourcing: simplify return type (#36028) * rm str | none pattern * clean up * more clean up * stash * Revert "stash" This reverts commit 3e2472160cc97e9d11922137757d9ef942a0312d. * fix da prints * fix cmt --- tools/lib/logreader.py | 43 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index 833f9825bb..50e3b9dc7a 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -140,9 +140,8 @@ class ReadMode(enum.StrEnum): AUTO_INTERACTIVE = "i" # default to rlogs, fallback to qlogs with a prompt from the user -LogPath = str | None LogFileName = tuple[str, ...] -Source = Callable[[SegmentRange, list[int], LogFileName], dict[int, LogPath]] +Source = Callable[[SegmentRange, list[int], LogFileName], dict[int, str]] InternalUnavailableException = Exception("Internal source not available") @@ -151,17 +150,17 @@ class LogsUnavailable(Exception): pass -def comma_api_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, LogPath]: +def comma_api_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, str]: route = Route(sr.route_name) # comma api will have already checked if the file exists if fns == FileName.RLOG: - return {seg: route.log_paths()[seg] for seg in seg_idxs} + return {seg: route.log_paths()[seg] for seg in seg_idxs if route.log_paths()[seg] is not None} else: - return {seg: route.qlog_paths()[seg] for seg in seg_idxs} + return {seg: route.qlog_paths()[seg] for seg in seg_idxs if route.qlog_paths()[seg] is not None} -def internal_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName, endpoint_url: str = DATA_ENDPOINT) -> dict[int, LogPath]: +def internal_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName, endpoint_url: str = DATA_ENDPOINT) -> dict[int, str]: if not internal_source_available(endpoint_url): raise InternalUnavailableException @@ -171,11 +170,11 @@ def internal_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName, end return eval_source({seg: [get_internal_url(sr, seg, fn) for fn in fns] for seg in seg_idxs}) -def openpilotci_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, LogPath]: +def openpilotci_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, str]: return eval_source({seg: [get_url(sr.route_name, seg, fn) for fn in fns] for seg in seg_idxs}) -def comma_car_segments_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, LogPath]: +def comma_car_segments_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, str]: return eval_source({seg: get_comma_segments_url(sr.route_name, seg) for seg in seg_idxs}) @@ -183,21 +182,19 @@ def direct_source(file_or_url: str) -> list[str]: return [file_or_url] -def eval_source(files: dict[int, list[str] | str]) -> dict[int, LogPath]: +def eval_source(files: dict[int, list[str] | str]) -> dict[int, str]: # Returns valid file URLs given a list of possible file URLs for each segment (e.g. rlog.bz2, rlog.zst) - valid_files: dict[int, LogPath] = {} + valid_files: dict[int, str] = {} for seg_idx, urls in files.items(): if isinstance(urls, str): urls = [urls] - # Add first valid file URL or None + # Add first valid file URL for url in urls: if file_exists(url): valid_files[seg_idx] = url break - else: - valid_files[seg_idx] = None return valid_files @@ -220,37 +217,35 @@ def auto_source(identifier: str, sources: list[Source], default_mode: ReadMode) # Build a dict of valid files as we evaluate each source. May contain mix of rlogs, qlogs, and None. # This function only returns when we've sourced all files, or throws an exception - valid_files: dict[int, LogPath] = {} + valid_files: dict[int, str] = {} for fn in try_fns: for source in sources: try: files = source(sr, needed_seg_idxs, fn) # Build a dict of valid files - for idx, f in files.items(): - if valid_files.get(idx) is None: - valid_files[idx] = f + valid_files |= files # Don't check for segment files that have already been found - needed_seg_idxs = [idx for idx in needed_seg_idxs if valid_files.get(idx) is None] + needed_seg_idxs = [idx for idx in needed_seg_idxs if idx not in valid_files] # We've found all files, return them - if all(f is not None for f in valid_files.values()): + if len(needed_seg_idxs) == 0: return cast(list[str], list(valid_files.values())) except Exception as e: exceptions[source.__name__] = e if fn == try_fns[0]: - missing_logs = list(valid_files.values()).count(None) + missing_logs = len(needed_seg_idxs) if mode == ReadMode.AUTO: - cloudlog.warning(f"{missing_logs}/{len(valid_files)} rlogs were not found, falling back to qlogs for those segments...") + cloudlog.warning(f"{missing_logs}/{len(sr.seg_idxs)} rlogs were not found, falling back to qlogs for those segments...") elif mode == ReadMode.AUTO_INTERACTIVE: - if input(f"{missing_logs}/{len(valid_files)} rlogs were not found, would you like to fallback to qlogs for those segments? (y/N) ").lower() != "y": + if input(f"{missing_logs}/{len(sr.seg_idxs)} rlogs were not found, would you like to fallback to qlogs for those segments? (y/N) ").lower() != "y": break - missing_logs = list(valid_files.values()).count(None) - raise LogsUnavailable(f"{missing_logs}/{len(valid_files)} logs were not found, please ensure all logs " + + missing_logs = len(needed_seg_idxs) + raise LogsUnavailable(f"{missing_logs}/{len(sr.seg_idxs)} logs were not found, please ensure all logs " + "are uploaded. You can fall back to qlogs with '/a' selector at the end of the route name.\n\n" + "Exceptions for sources:\n - " + "\n - ".join([f"{k}: {repr(v)}" for k, v in exceptions.items()])) From 18b7ddef8fb8e6e0f1dbb2f8a7799e423d0bd208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Tue, 19 Aug 2025 16:25:13 -0700 Subject: [PATCH 43/64] File sourcing: Not all files are logs (#36025) * Not all files are logs * more refactor * linting ok * fix tests * import exception * whoops forgot to git add * fix --------- Co-authored-by: Shane Smiskol --- tools/lib/file_sources.py | 57 +++++++++++++++++++++++++++++ tools/lib/logreader.py | 61 +++---------------------------- tools/lib/tests/test_logreader.py | 5 ++- 3 files changed, 65 insertions(+), 58 deletions(-) create mode 100755 tools/lib/file_sources.py diff --git a/tools/lib/file_sources.py b/tools/lib/file_sources.py new file mode 100755 index 0000000000..cb7bf15114 --- /dev/null +++ b/tools/lib/file_sources.py @@ -0,0 +1,57 @@ +from collections.abc import Callable + +from openpilot.tools.lib.comma_car_segments import get_url as get_comma_segments_url +from openpilot.tools.lib.openpilotci import get_url +from openpilot.tools.lib.filereader import DATA_ENDPOINT, file_exists, internal_source_available +from openpilot.tools.lib.route import Route, SegmentRange, FileName + +# When passed a tuple of file names, each source will return the first that exists (rlog.zst, rlog.bz2) +FileNames = tuple[str, ...] +Source = Callable[[SegmentRange, list[int], FileNames], dict[int, str]] + +InternalUnavailableException = Exception("Internal source not available") + + +def comma_api_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames) -> dict[int, str]: + route = Route(sr.route_name) + + # comma api will have already checked if the file exists + if fns == FileName.RLOG: + return {seg: route.log_paths()[seg] for seg in seg_idxs if route.log_paths()[seg] is not None} + else: + return {seg: route.qlog_paths()[seg] for seg in seg_idxs if route.qlog_paths()[seg] is not None} + + +def internal_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames, endpoint_url: str = DATA_ENDPOINT) -> dict[int, str]: + if not internal_source_available(endpoint_url): + raise InternalUnavailableException + + def get_internal_url(sr: SegmentRange, seg, file): + return f"{endpoint_url.rstrip('/')}/{sr.dongle_id}/{sr.log_id}/{seg}/{file}" + + return eval_source({seg: [get_internal_url(sr, seg, fn) for fn in fns] for seg in seg_idxs}) + + +def openpilotci_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames) -> dict[int, str]: + return eval_source({seg: [get_url(sr.route_name, seg, fn) for fn in fns] for seg in seg_idxs}) + + +def comma_car_segments_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames) -> dict[int, str]: + return eval_source({seg: get_comma_segments_url(sr.route_name, seg) for seg in seg_idxs}) + + +def eval_source(files: dict[int, list[str] | str]) -> dict[int, str]: + # Returns valid file URLs given a list of possible file URLs for each segment (e.g. rlog.bz2, rlog.zst) + valid_files: dict[int, str] = {} + + for seg_idx, urls in files.items(): + if isinstance(urls, str): + urls = [urls] + + # Add first valid file URL + for url in urls: + if file_exists(url): + valid_files[seg_idx] = url + break + + return valid_files diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index 50e3b9dc7a..8d84cdbd5d 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -12,16 +12,15 @@ import urllib.parse import warnings import zstandard as zstd -from collections.abc import Callable, Iterable, Iterator +from collections.abc import Iterable, Iterator from typing import cast from urllib.parse import parse_qs, urlparse from cereal import log as capnp_log from openpilot.common.swaglog import cloudlog -from openpilot.tools.lib.comma_car_segments import get_url as get_comma_segments_url -from openpilot.tools.lib.openpilotci import get_url -from openpilot.tools.lib.filereader import DATA_ENDPOINT, FileReader, file_exists, internal_source_available -from openpilot.tools.lib.route import Route, SegmentRange, FileName +from openpilot.tools.lib.filereader import FileReader +from openpilot.tools.lib.file_sources import comma_api_source, internal_source, openpilotci_source, comma_car_segments_source, Source +from openpilot.tools.lib.route import SegmentRange, FileName from openpilot.tools.lib.log_time_series import msgs_to_time_series LogMessage = type[capnp._DynamicStructReader] @@ -140,65 +139,15 @@ class ReadMode(enum.StrEnum): AUTO_INTERACTIVE = "i" # default to rlogs, fallback to qlogs with a prompt from the user -LogFileName = tuple[str, ...] -Source = Callable[[SegmentRange, list[int], LogFileName], dict[int, str]] - -InternalUnavailableException = Exception("Internal source not available") - - class LogsUnavailable(Exception): pass -def comma_api_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, str]: - route = Route(sr.route_name) - - # comma api will have already checked if the file exists - if fns == FileName.RLOG: - return {seg: route.log_paths()[seg] for seg in seg_idxs if route.log_paths()[seg] is not None} - else: - return {seg: route.qlog_paths()[seg] for seg in seg_idxs if route.qlog_paths()[seg] is not None} - - -def internal_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName, endpoint_url: str = DATA_ENDPOINT) -> dict[int, str]: - if not internal_source_available(endpoint_url): - raise InternalUnavailableException - - def get_internal_url(sr: SegmentRange, seg, file): - return f"{endpoint_url.rstrip('/')}/{sr.dongle_id}/{sr.log_id}/{seg}/{file}" - - return eval_source({seg: [get_internal_url(sr, seg, fn) for fn in fns] for seg in seg_idxs}) - - -def openpilotci_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, str]: - return eval_source({seg: [get_url(sr.route_name, seg, fn) for fn in fns] for seg in seg_idxs}) - - -def comma_car_segments_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, str]: - return eval_source({seg: get_comma_segments_url(sr.route_name, seg) for seg in seg_idxs}) - - def direct_source(file_or_url: str) -> list[str]: return [file_or_url] -def eval_source(files: dict[int, list[str] | str]) -> dict[int, str]: - # Returns valid file URLs given a list of possible file URLs for each segment (e.g. rlog.bz2, rlog.zst) - valid_files: dict[int, str] = {} - - for seg_idx, urls in files.items(): - if isinstance(urls, str): - urls = [urls] - - # Add first valid file URL - for url in urls: - if file_exists(url): - valid_files[seg_idx] = url - break - - return valid_files - - +# TODO this should apply to camera files as well def auto_source(identifier: str, sources: list[Source], default_mode: ReadMode) -> list[str]: exceptions = {} diff --git a/tools/lib/tests/test_logreader.py b/tools/lib/tests/test_logreader.py index 7a70ad6aab..6e508967b9 100644 --- a/tools/lib/tests/test_logreader.py +++ b/tools/lib/tests/test_logreader.py @@ -10,7 +10,8 @@ import requests from parameterized import parameterized from cereal import log as capnp_log -from openpilot.tools.lib.logreader import LogsUnavailable, LogIterable, LogReader, comma_api_source, parse_indirect, ReadMode, InternalUnavailableException +from openpilot.tools.lib.logreader import LogsUnavailable, LogIterable, LogReader, parse_indirect, ReadMode +from openpilot.tools.lib.log_sources import comma_api_source, InternalUnavailableException from openpilot.tools.lib.route import SegmentRange from openpilot.tools.lib.url_file import URLFileException @@ -90,7 +91,7 @@ class TestLogReader: @pytest.mark.parametrize("cache_enabled", [True, False]) def test_direct_parsing(self, mocker, cache_enabled): - file_exists_mock = mocker.patch("openpilot.tools.lib.logreader.file_exists") + file_exists_mock = mocker.patch("openpilot.tools.lib.filereader.file_exists") os.environ["FILEREADER_CACHE"] = "1" if cache_enabled else "0" qlog = tempfile.NamedTemporaryFile(mode='wb', delete=False) From dd5f5fdb98fed20d37cde4b9c786146814b00b58 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 19 Aug 2025 17:11:29 -0700 Subject: [PATCH 44/64] ci: show all unit test failures (#36029) * testci * fix * Revert "testci" This reverts commit b62a0aacb604fc0fd39c6e50a726b686979b9880. --- .github/workflows/selfdrive_tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index 2d94a39260..fa462166b0 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -162,7 +162,7 @@ jobs: timeout-minutes: ${{ contains(runner.name, 'nsc') && ((steps.setup-step.outputs.duration < 18) && 1 || 2) || 20 }} run: | ${{ env.RUN }} "source selfdrive/test/setup_xvfb.sh && \ - $PYTEST --collect-only -m 'not slow' &> /dev/null && \ + $PYTEST --collect-only -m 'not slow' && \ MAX_EXAMPLES=1 $PYTEST -m 'not slow' && \ ./selfdrive/ui/tests/create_test_translations.sh && \ QT_QPA_PLATFORM=offscreen ./selfdrive/ui/tests/test_translations && \ From 3570022b9a7e90c98a0188d5b7f7f2e14710af61 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 19 Aug 2025 17:11:53 -0700 Subject: [PATCH 45/64] Revert "File sourcing: Not all files are logs (#36025)" This reverts commit 18b7ddef8fb8e6e0f1dbb2f8a7799e423d0bd208. --- tools/lib/file_sources.py | 57 ----------------------------- tools/lib/logreader.py | 61 ++++++++++++++++++++++++++++--- tools/lib/tests/test_logreader.py | 5 +-- 3 files changed, 58 insertions(+), 65 deletions(-) delete mode 100755 tools/lib/file_sources.py diff --git a/tools/lib/file_sources.py b/tools/lib/file_sources.py deleted file mode 100755 index cb7bf15114..0000000000 --- a/tools/lib/file_sources.py +++ /dev/null @@ -1,57 +0,0 @@ -from collections.abc import Callable - -from openpilot.tools.lib.comma_car_segments import get_url as get_comma_segments_url -from openpilot.tools.lib.openpilotci import get_url -from openpilot.tools.lib.filereader import DATA_ENDPOINT, file_exists, internal_source_available -from openpilot.tools.lib.route import Route, SegmentRange, FileName - -# When passed a tuple of file names, each source will return the first that exists (rlog.zst, rlog.bz2) -FileNames = tuple[str, ...] -Source = Callable[[SegmentRange, list[int], FileNames], dict[int, str]] - -InternalUnavailableException = Exception("Internal source not available") - - -def comma_api_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames) -> dict[int, str]: - route = Route(sr.route_name) - - # comma api will have already checked if the file exists - if fns == FileName.RLOG: - return {seg: route.log_paths()[seg] for seg in seg_idxs if route.log_paths()[seg] is not None} - else: - return {seg: route.qlog_paths()[seg] for seg in seg_idxs if route.qlog_paths()[seg] is not None} - - -def internal_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames, endpoint_url: str = DATA_ENDPOINT) -> dict[int, str]: - if not internal_source_available(endpoint_url): - raise InternalUnavailableException - - def get_internal_url(sr: SegmentRange, seg, file): - return f"{endpoint_url.rstrip('/')}/{sr.dongle_id}/{sr.log_id}/{seg}/{file}" - - return eval_source({seg: [get_internal_url(sr, seg, fn) for fn in fns] for seg in seg_idxs}) - - -def openpilotci_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames) -> dict[int, str]: - return eval_source({seg: [get_url(sr.route_name, seg, fn) for fn in fns] for seg in seg_idxs}) - - -def comma_car_segments_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames) -> dict[int, str]: - return eval_source({seg: get_comma_segments_url(sr.route_name, seg) for seg in seg_idxs}) - - -def eval_source(files: dict[int, list[str] | str]) -> dict[int, str]: - # Returns valid file URLs given a list of possible file URLs for each segment (e.g. rlog.bz2, rlog.zst) - valid_files: dict[int, str] = {} - - for seg_idx, urls in files.items(): - if isinstance(urls, str): - urls = [urls] - - # Add first valid file URL - for url in urls: - if file_exists(url): - valid_files[seg_idx] = url - break - - return valid_files diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index 8d84cdbd5d..50e3b9dc7a 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -12,15 +12,16 @@ import urllib.parse import warnings import zstandard as zstd -from collections.abc import Iterable, Iterator +from collections.abc import Callable, Iterable, Iterator from typing import cast from urllib.parse import parse_qs, urlparse from cereal import log as capnp_log from openpilot.common.swaglog import cloudlog -from openpilot.tools.lib.filereader import FileReader -from openpilot.tools.lib.file_sources import comma_api_source, internal_source, openpilotci_source, comma_car_segments_source, Source -from openpilot.tools.lib.route import SegmentRange, FileName +from openpilot.tools.lib.comma_car_segments import get_url as get_comma_segments_url +from openpilot.tools.lib.openpilotci import get_url +from openpilot.tools.lib.filereader import DATA_ENDPOINT, FileReader, file_exists, internal_source_available +from openpilot.tools.lib.route import Route, SegmentRange, FileName from openpilot.tools.lib.log_time_series import msgs_to_time_series LogMessage = type[capnp._DynamicStructReader] @@ -139,15 +140,65 @@ class ReadMode(enum.StrEnum): AUTO_INTERACTIVE = "i" # default to rlogs, fallback to qlogs with a prompt from the user +LogFileName = tuple[str, ...] +Source = Callable[[SegmentRange, list[int], LogFileName], dict[int, str]] + +InternalUnavailableException = Exception("Internal source not available") + + class LogsUnavailable(Exception): pass +def comma_api_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, str]: + route = Route(sr.route_name) + + # comma api will have already checked if the file exists + if fns == FileName.RLOG: + return {seg: route.log_paths()[seg] for seg in seg_idxs if route.log_paths()[seg] is not None} + else: + return {seg: route.qlog_paths()[seg] for seg in seg_idxs if route.qlog_paths()[seg] is not None} + + +def internal_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName, endpoint_url: str = DATA_ENDPOINT) -> dict[int, str]: + if not internal_source_available(endpoint_url): + raise InternalUnavailableException + + def get_internal_url(sr: SegmentRange, seg, file): + return f"{endpoint_url.rstrip('/')}/{sr.dongle_id}/{sr.log_id}/{seg}/{file}" + + return eval_source({seg: [get_internal_url(sr, seg, fn) for fn in fns] for seg in seg_idxs}) + + +def openpilotci_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, str]: + return eval_source({seg: [get_url(sr.route_name, seg, fn) for fn in fns] for seg in seg_idxs}) + + +def comma_car_segments_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, str]: + return eval_source({seg: get_comma_segments_url(sr.route_name, seg) for seg in seg_idxs}) + + def direct_source(file_or_url: str) -> list[str]: return [file_or_url] -# TODO this should apply to camera files as well +def eval_source(files: dict[int, list[str] | str]) -> dict[int, str]: + # Returns valid file URLs given a list of possible file URLs for each segment (e.g. rlog.bz2, rlog.zst) + valid_files: dict[int, str] = {} + + for seg_idx, urls in files.items(): + if isinstance(urls, str): + urls = [urls] + + # Add first valid file URL + for url in urls: + if file_exists(url): + valid_files[seg_idx] = url + break + + return valid_files + + def auto_source(identifier: str, sources: list[Source], default_mode: ReadMode) -> list[str]: exceptions = {} diff --git a/tools/lib/tests/test_logreader.py b/tools/lib/tests/test_logreader.py index 6e508967b9..7a70ad6aab 100644 --- a/tools/lib/tests/test_logreader.py +++ b/tools/lib/tests/test_logreader.py @@ -10,8 +10,7 @@ import requests from parameterized import parameterized from cereal import log as capnp_log -from openpilot.tools.lib.logreader import LogsUnavailable, LogIterable, LogReader, parse_indirect, ReadMode -from openpilot.tools.lib.log_sources import comma_api_source, InternalUnavailableException +from openpilot.tools.lib.logreader import LogsUnavailable, LogIterable, LogReader, comma_api_source, parse_indirect, ReadMode, InternalUnavailableException from openpilot.tools.lib.route import SegmentRange from openpilot.tools.lib.url_file import URLFileException @@ -91,7 +90,7 @@ class TestLogReader: @pytest.mark.parametrize("cache_enabled", [True, False]) def test_direct_parsing(self, mocker, cache_enabled): - file_exists_mock = mocker.patch("openpilot.tools.lib.filereader.file_exists") + file_exists_mock = mocker.patch("openpilot.tools.lib.logreader.file_exists") os.environ["FILEREADER_CACHE"] = "1" if cache_enabled else "0" qlog = tempfile.NamedTemporaryFile(mode='wb', delete=False) From 22e79479d257bc3fca5692a82dc9b570db23c044 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 19 Aug 2025 19:39:27 -0700 Subject: [PATCH 46/64] unit tests: add comment (#36030) * remove collection * test * back * wtf it actually saves 10s?! * ah that makes sense * rm * ? * ugh * qq * bc --- .github/workflows/selfdrive_tests.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index fa462166b0..aad772757d 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -162,7 +162,8 @@ jobs: timeout-minutes: ${{ contains(runner.name, 'nsc') && ((steps.setup-step.outputs.duration < 18) && 1 || 2) || 20 }} run: | ${{ env.RUN }} "source selfdrive/test/setup_xvfb.sh && \ - $PYTEST --collect-only -m 'not slow' && \ + # Pre-compile Python bytecode so each pytest worker doesn't need to + $PYTEST --collect-only -m 'not slow' -qq && \ MAX_EXAMPLES=1 $PYTEST -m 'not slow' && \ ./selfdrive/ui/tests/create_test_translations.sh && \ QT_QPA_PLATFORM=offscreen ./selfdrive/ui/tests/test_translations && \ From 60c34a083791e96b0a244dc56decb1423e080da0 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 19 Aug 2025 19:58:47 -0700 Subject: [PATCH 47/64] LogReader: run source test (#36031) run "slow" test --- tools/lib/tests/test_logreader.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/lib/tests/test_logreader.py b/tools/lib/tests/test_logreader.py index 7a70ad6aab..a9f7b2352d 100644 --- a/tools/lib/tests/test_logreader.py +++ b/tools/lib/tests/test_logreader.py @@ -40,7 +40,7 @@ def setup_source_scenario(mocker, is_internal=False): else: internal_source_mock.side_effect = InternalUnavailableException - openpilotci_source_mock.return_value = {3: None} + openpilotci_source_mock.return_value = {} comma_api_source_mock.return_value = {3: QLOG_FILE} yield @@ -208,13 +208,12 @@ class TestLogReader: assert qlog_len == log_len @pytest.mark.parametrize("is_internal", [True, False]) - @pytest.mark.slow def test_auto_source_scenarios(self, mocker, is_internal): lr = LogReader(QLOG_FILE) qlog_len = len(list(lr)) with setup_source_scenario(mocker, is_internal=is_internal): - lr = LogReader(f"{TEST_ROUTE}/0/q") + lr = LogReader(f"{TEST_ROUTE}/3/q") log_len = len(list(lr)) assert qlog_len == log_len From 870d19f33de194679e060b49f7a970c3cfaa3836 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 19 Aug 2025 19:59:50 -0700 Subject: [PATCH 48/64] Reapply "File sourcing: Not all files are logs (#36025)" This reverts commit 3570022b9a7e90c98a0188d5b7f7f2e14710af61. Fix test --- tools/lib/file_sources.py | 57 +++++++++++++++++++++++++++++ tools/lib/logreader.py | 61 +++---------------------------- tools/lib/tests/test_logreader.py | 5 ++- 3 files changed, 65 insertions(+), 58 deletions(-) create mode 100755 tools/lib/file_sources.py diff --git a/tools/lib/file_sources.py b/tools/lib/file_sources.py new file mode 100755 index 0000000000..cb7bf15114 --- /dev/null +++ b/tools/lib/file_sources.py @@ -0,0 +1,57 @@ +from collections.abc import Callable + +from openpilot.tools.lib.comma_car_segments import get_url as get_comma_segments_url +from openpilot.tools.lib.openpilotci import get_url +from openpilot.tools.lib.filereader import DATA_ENDPOINT, file_exists, internal_source_available +from openpilot.tools.lib.route import Route, SegmentRange, FileName + +# When passed a tuple of file names, each source will return the first that exists (rlog.zst, rlog.bz2) +FileNames = tuple[str, ...] +Source = Callable[[SegmentRange, list[int], FileNames], dict[int, str]] + +InternalUnavailableException = Exception("Internal source not available") + + +def comma_api_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames) -> dict[int, str]: + route = Route(sr.route_name) + + # comma api will have already checked if the file exists + if fns == FileName.RLOG: + return {seg: route.log_paths()[seg] for seg in seg_idxs if route.log_paths()[seg] is not None} + else: + return {seg: route.qlog_paths()[seg] for seg in seg_idxs if route.qlog_paths()[seg] is not None} + + +def internal_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames, endpoint_url: str = DATA_ENDPOINT) -> dict[int, str]: + if not internal_source_available(endpoint_url): + raise InternalUnavailableException + + def get_internal_url(sr: SegmentRange, seg, file): + return f"{endpoint_url.rstrip('/')}/{sr.dongle_id}/{sr.log_id}/{seg}/{file}" + + return eval_source({seg: [get_internal_url(sr, seg, fn) for fn in fns] for seg in seg_idxs}) + + +def openpilotci_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames) -> dict[int, str]: + return eval_source({seg: [get_url(sr.route_name, seg, fn) for fn in fns] for seg in seg_idxs}) + + +def comma_car_segments_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames) -> dict[int, str]: + return eval_source({seg: get_comma_segments_url(sr.route_name, seg) for seg in seg_idxs}) + + +def eval_source(files: dict[int, list[str] | str]) -> dict[int, str]: + # Returns valid file URLs given a list of possible file URLs for each segment (e.g. rlog.bz2, rlog.zst) + valid_files: dict[int, str] = {} + + for seg_idx, urls in files.items(): + if isinstance(urls, str): + urls = [urls] + + # Add first valid file URL + for url in urls: + if file_exists(url): + valid_files[seg_idx] = url + break + + return valid_files diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index 50e3b9dc7a..8d84cdbd5d 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -12,16 +12,15 @@ import urllib.parse import warnings import zstandard as zstd -from collections.abc import Callable, Iterable, Iterator +from collections.abc import Iterable, Iterator from typing import cast from urllib.parse import parse_qs, urlparse from cereal import log as capnp_log from openpilot.common.swaglog import cloudlog -from openpilot.tools.lib.comma_car_segments import get_url as get_comma_segments_url -from openpilot.tools.lib.openpilotci import get_url -from openpilot.tools.lib.filereader import DATA_ENDPOINT, FileReader, file_exists, internal_source_available -from openpilot.tools.lib.route import Route, SegmentRange, FileName +from openpilot.tools.lib.filereader import FileReader +from openpilot.tools.lib.file_sources import comma_api_source, internal_source, openpilotci_source, comma_car_segments_source, Source +from openpilot.tools.lib.route import SegmentRange, FileName from openpilot.tools.lib.log_time_series import msgs_to_time_series LogMessage = type[capnp._DynamicStructReader] @@ -140,65 +139,15 @@ class ReadMode(enum.StrEnum): AUTO_INTERACTIVE = "i" # default to rlogs, fallback to qlogs with a prompt from the user -LogFileName = tuple[str, ...] -Source = Callable[[SegmentRange, list[int], LogFileName], dict[int, str]] - -InternalUnavailableException = Exception("Internal source not available") - - class LogsUnavailable(Exception): pass -def comma_api_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, str]: - route = Route(sr.route_name) - - # comma api will have already checked if the file exists - if fns == FileName.RLOG: - return {seg: route.log_paths()[seg] for seg in seg_idxs if route.log_paths()[seg] is not None} - else: - return {seg: route.qlog_paths()[seg] for seg in seg_idxs if route.qlog_paths()[seg] is not None} - - -def internal_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName, endpoint_url: str = DATA_ENDPOINT) -> dict[int, str]: - if not internal_source_available(endpoint_url): - raise InternalUnavailableException - - def get_internal_url(sr: SegmentRange, seg, file): - return f"{endpoint_url.rstrip('/')}/{sr.dongle_id}/{sr.log_id}/{seg}/{file}" - - return eval_source({seg: [get_internal_url(sr, seg, fn) for fn in fns] for seg in seg_idxs}) - - -def openpilotci_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, str]: - return eval_source({seg: [get_url(sr.route_name, seg, fn) for fn in fns] for seg in seg_idxs}) - - -def comma_car_segments_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, str]: - return eval_source({seg: get_comma_segments_url(sr.route_name, seg) for seg in seg_idxs}) - - def direct_source(file_or_url: str) -> list[str]: return [file_or_url] -def eval_source(files: dict[int, list[str] | str]) -> dict[int, str]: - # Returns valid file URLs given a list of possible file URLs for each segment (e.g. rlog.bz2, rlog.zst) - valid_files: dict[int, str] = {} - - for seg_idx, urls in files.items(): - if isinstance(urls, str): - urls = [urls] - - # Add first valid file URL - for url in urls: - if file_exists(url): - valid_files[seg_idx] = url - break - - return valid_files - - +# TODO this should apply to camera files as well def auto_source(identifier: str, sources: list[Source], default_mode: ReadMode) -> list[str]: exceptions = {} diff --git a/tools/lib/tests/test_logreader.py b/tools/lib/tests/test_logreader.py index a9f7b2352d..8d0870171f 100644 --- a/tools/lib/tests/test_logreader.py +++ b/tools/lib/tests/test_logreader.py @@ -10,7 +10,8 @@ import requests from parameterized import parameterized from cereal import log as capnp_log -from openpilot.tools.lib.logreader import LogsUnavailable, LogIterable, LogReader, comma_api_source, parse_indirect, ReadMode, InternalUnavailableException +from openpilot.tools.lib.logreader import LogsUnavailable, LogIterable, LogReader, parse_indirect, ReadMode +from openpilot.tools.lib.file_sources import comma_api_source, InternalUnavailableException from openpilot.tools.lib.route import SegmentRange from openpilot.tools.lib.url_file import URLFileException @@ -90,7 +91,7 @@ class TestLogReader: @pytest.mark.parametrize("cache_enabled", [True, False]) def test_direct_parsing(self, mocker, cache_enabled): - file_exists_mock = mocker.patch("openpilot.tools.lib.logreader.file_exists") + file_exists_mock = mocker.patch("openpilot.tools.lib.filereader.file_exists") os.environ["FILEREADER_CACHE"] = "1" if cache_enabled else "0" qlog = tempfile.NamedTemporaryFile(mode='wb', delete=False) From d0069c136b4302c6978717abc6e3859efeea3a93 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 19 Aug 2025 22:19:56 -0700 Subject: [PATCH 49/64] raylib: fix experimental mode path gradient (#36033) * fix! * this is enough to fix the broken colors * clean up * fix * use last colors -- need this so we don't have to always pass perfect gradient * clean up * clean up * clean up --- selfdrive/ui/onroad/model_renderer.py | 16 ++++++++-------- system/ui/lib/shader_polygon.py | 4 ++++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/selfdrive/ui/onroad/model_renderer.py b/selfdrive/ui/onroad/model_renderer.py index 4439141a40..684f2a8453 100644 --- a/selfdrive/ui/onroad/model_renderer.py +++ b/selfdrive/ui/onroad/model_renderer.py @@ -187,9 +187,9 @@ class ModelRenderer(Widget): self._path.raw_points, 0.9, self._path_offset_z, max_idx, allow_invert=False ) - self._update_experimental_gradient(self._rect.height) + self._update_experimental_gradient() - def _update_experimental_gradient(self, height): + def _update_experimental_gradient(self): """Pre-calculate experimental mode gradient colors""" if not self._experimental_mode: return @@ -201,14 +201,14 @@ class ModelRenderer(Widget): i = 0 while i < max_len: - track_idx = max_len - i - 1 # flip idx to start from bottom right - track_y = self._path.projected_points[track_idx][1] - if track_y < 0 or track_y > height: + # Some points are out of frame + track_y = self._path.projected_points[i][1] + if track_y < self._rect.y or track_y > self._rect.height: i += 1 continue - # Calculate color based on acceleration - lin_grad_point = (height - track_y) / height + # Calculate color based on acceleration (0 is bottom, 1 is top) + lin_grad_point = 1 - (track_y - self._rect.y) / self._rect.height # speed up: 120, slow down: 0 path_hue = max(min(60 + self._acceleration_x[i] * 35, 120), 0) @@ -280,7 +280,7 @@ class ModelRenderer(Widget): if self._experimental_mode: # Draw with acceleration coloring - if len(self._exp_gradient['colors']) > 2: + if len(self._exp_gradient['colors']) > 1: draw_polygon(self._rect, self._path.projected_points, gradient=self._exp_gradient) else: draw_polygon(self._rect, self._path.projected_points, rl.Color(255, 255, 255, 30)) diff --git a/system/ui/lib/shader_polygon.py b/system/ui/lib/shader_polygon.py index cfde81c554..39bc0d5aa4 100644 --- a/system/ui/lib/shader_polygon.py +++ b/system/ui/lib/shader_polygon.py @@ -39,6 +39,10 @@ vec4 getGradientColor(vec2 pos) { float t = clamp(dot(pos - gradientStart, normalizedDir) / gradientLength, 0.0, 1.0); if (gradientColorCount <= 1) return gradientColors[0]; + + // handle t before first / after last stop + if (t <= gradientStops[0]) return gradientColors[0]; + if (t >= gradientStops[gradientColorCount-1]) return gradientColors[gradientColorCount-1]; for (int i = 0; i < gradientColorCount - 1; i++) { if (t >= gradientStops[i] && t <= gradientStops[i+1]) { float segmentT = (t - gradientStops[i]) / (gradientStops[i+1] - gradientStops[i]); From 63441c048cb59178778aa670e17973324d2ed0d9 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 19 Aug 2025 22:30:48 -0700 Subject: [PATCH 50/64] test_onroad: relax first fid assertion (#36032) * fid * test * Revert "test" This reverts commit 38e6635dd0b0b9fb9c08bcc3a74b9283207b0c2f. * r * Revert "r" This reverts commit 4037a321f89af137a645345a0fffb73da6071c72. --- selfdrive/test/test_onroad.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py index 935da99c10..0149653c84 100644 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -333,20 +333,18 @@ class TestOnroad: assert np.all(eof_sof_diff > 0) assert np.all(eof_sof_diff < 50*1e6) - first_fid = {c: min(self.ts[c]['frameId']) for c in cams} + first_fid = {min(self.ts[c]['frameId']) for c in cams} + assert len(first_fid) == 1, "Cameras don't start on same frame ID" if cam.endswith('CameraState'): # camerad guarantees that all cams start on frame ID 0 # (note loggerd also needs to start up fast enough to catch it) - assert set(first_fid.values()) == {0, }, "Cameras don't start on frame ID 0" - else: - # encoder guarantees all cams start on the same frame ID - assert len(set(first_fid.values())) == 1, "Cameras don't start on same frame ID" + assert next(iter(first_fid)) < 100, "Cameras start on frame ID too high" # we don't do a full segment rotation, so these might not match exactly - last_fid = {c: max(self.ts[c]['frameId']) for c in cams} - assert max(last_fid.values()) - min(last_fid.values()) < 10 + last_fid = {max(self.ts[c]['frameId']) for c in cams} + assert max(last_fid) - min(last_fid) < 10 - start, end = min(first_fid.values()), min(last_fid.values()) + start, end = min(first_fid), min(last_fid) for i in range(end-start): ts = {c: round(self.ts[c]['timestampSof'][i]/1e6, 1) for c in cams} diff = (max(ts.values()) - min(ts.values())) From 8320934d91b78ddea2c4d714dc29e3d4c72dd196 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 19 Aug 2025 22:33:07 -0700 Subject: [PATCH 51/64] raylib: cleanup experimental mode gradient color calculations (#36035) * dfebug * simplify * come on man --- selfdrive/ui/onroad/model_renderer.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/selfdrive/ui/onroad/model_renderer.py b/selfdrive/ui/onroad/model_renderer.py index 684f2a8453..f81b0be66c 100644 --- a/selfdrive/ui/onroad/model_renderer.py +++ b/selfdrive/ui/onroad/model_renderer.py @@ -211,12 +211,11 @@ class ModelRenderer(Widget): lin_grad_point = 1 - (track_y - self._rect.y) / self._rect.height # speed up: 120, slow down: 0 - path_hue = max(min(60 + self._acceleration_x[i] * 35, 120), 0) - path_hue = int(path_hue * 100 + 0.5) / 100 + path_hue = np.clip(60 + self._acceleration_x[i] * 35, 0, 120) saturation = min(abs(self._acceleration_x[i] * 1.5), 1) - lightness = self._map_val(saturation, 0.0, 1.0, 0.95, 0.62) - alpha = self._map_val(lin_grad_point, 0.75 / 2.0, 0.75, 0.4, 0.0) + lightness = np.interp(saturation, [0.0, 1.0], [0.95, 0.62]) + alpha = np.interp(lin_grad_point, [0.75 / 2.0, 0.75], [0.4, 0.0]) # Use HSL to RGB conversion color = self._hsla_to_color(path_hue / 360.0, saturation, lightness, alpha) @@ -409,13 +408,6 @@ class ModelRenderer(Widget): return np.vstack((left_screen.T, right_screen[:, ::-1].T)).astype(np.float32) - @staticmethod - def _map_val(x, x0, x1, y0, y1): - x = np.clip(x, x0, x1) - ra = x1 - x0 - rb = y1 - y0 - return (x - x0) * rb / ra + y0 if ra != 0 else y0 - @staticmethod def _hsla_to_color(h, s, l, a): rgb = colorsys.hls_to_rgb(h, l, s) From 2ff707d82f53b01811ad184fbfc873c664188cc3 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 19 Aug 2025 22:37:55 -0700 Subject: [PATCH 52/64] Fix gradient point ignore --- selfdrive/ui/onroad/model_renderer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/onroad/model_renderer.py b/selfdrive/ui/onroad/model_renderer.py index f81b0be66c..932773755d 100644 --- a/selfdrive/ui/onroad/model_renderer.py +++ b/selfdrive/ui/onroad/model_renderer.py @@ -201,9 +201,9 @@ class ModelRenderer(Widget): i = 0 while i < max_len: - # Some points are out of frame + # Some points (screen space) are out of frame (rect space) track_y = self._path.projected_points[i][1] - if track_y < self._rect.y or track_y > self._rect.height: + if track_y < self._rect.y or track_y > (self._rect.y + self._rect.height): i += 1 continue From 154f65533554e7b55b6d9abdf22d57e5035b1bc5 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 20 Aug 2025 15:44:13 -0700 Subject: [PATCH 53/64] update release checklist --- release/README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/release/README.md b/release/README.md index 266d881d3c..170a2d65ca 100644 --- a/release/README.md +++ b/release/README.md @@ -3,7 +3,7 @@ ``` ## release checklist -**Go to staging** +### Go to staging - [ ] make a GitHub issue to track release - [ ] create release master branch - [ ] update RELEASES.md @@ -11,11 +11,12 @@ - [ ] build new userdata partition from `release3-staging` - [ ] post on Discord, tag `@release crew` -Updates to staging: -- [ ] either rebase on master or cherry-pick changes -- [ ] run this to update: `BRANCH=devel-staging release/build_devel.sh` +Updating staging: +1. either rebase on master or cherry-pick changes +2. run this to update: `BRANCH=devel-staging release/build_devel.sh` +3. build new userdata partition from `release3-staging` -**Go to release** +### Go to release - [ ] before going to release, test the following: - [ ] update from previous release -> new release - [ ] update from new release -> previous release From b4cc4ea8e2dc0c7cae0fe5407b868a71dc1c1af9 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 20 Aug 2025 15:44:23 -0700 Subject: [PATCH 54/64] Update README.md --- release/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/README.md b/release/README.md index 170a2d65ca..fb651fa05a 100644 --- a/release/README.md +++ b/release/README.md @@ -14,7 +14,7 @@ Updating staging: 1. either rebase on master or cherry-pick changes 2. run this to update: `BRANCH=devel-staging release/build_devel.sh` -3. build new userdata partition from `release3-staging` +3. build new userdata partition from `release3-staging` ### Go to release - [ ] before going to release, test the following: From cd9ec6b240abd6e2a2aa4afc29840180a332147f Mon Sep 17 00:00:00 2001 From: "kostas.pats" <35031825+kostas1507@users.noreply.github.com> Date: Wed, 20 Aug 2025 22:45:05 +0000 Subject: [PATCH 55/64] Compressed vipc name pick (#36036) * add custom vipc server name argument * Update compressed_vipc.py * add custom vipc server name argument + fixes * Update compressed_vipc.py --- tools/camerastream/compressed_vipc.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/camerastream/compressed_vipc.py b/tools/camerastream/compressed_vipc.py index b25b8b0cb7..4dc74272ea 100755 --- a/tools/camerastream/compressed_vipc.py +++ b/tools/camerastream/compressed_vipc.py @@ -107,7 +107,7 @@ def decoder(addr, vipc_server, vst, nvidia, W, H, debug=False): class CompressedVipc: - def __init__(self, addr, vision_streams, nvidia=False, debug=False): + def __init__(self, addr, vision_streams, server_name, nvidia=False, debug=False): print("getting frame sizes") os.environ["ZMQ"] = "1" messaging.reset_context() @@ -117,7 +117,7 @@ class CompressedVipc: os.environ.pop("ZMQ") messaging.reset_context() - self.vipc_server = VisionIpcServer("camerad") + self.vipc_server = VisionIpcServer(server_name) for vst in vision_streams: ed = sm[ENCODE_SOCKETS[vst]] self.vipc_server.create_buffers(vst, 4, ed.width, ed.height) @@ -144,6 +144,7 @@ if __name__ == "__main__": parser.add_argument("addr", help="Address of comma three") parser.add_argument("--nvidia", action="store_true", help="Use nvidia instead of ffmpeg") parser.add_argument("--cams", default="0,1,2", help="Cameras to decode") + parser.add_argument("--server", default="camerad", help="choose vipc server name") parser.add_argument("--silent", action="store_true", help="Suppress debug output") args = parser.parse_args() @@ -154,7 +155,7 @@ if __name__ == "__main__": ] vsts = [vision_streams[int(x)] for x in args.cams.split(",")] - cvipc = CompressedVipc(args.addr, vsts, args.nvidia, debug=(not args.silent)) + cvipc = CompressedVipc(args.addr, vsts, args.server, args.nvidia, debug=(not args.silent)) # register exit handler signal.signal(signal.SIGINT, lambda sig, frame: cvipc.kill()) From cea3572b7416fd7589a85e9572fb620976e7b163 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 21 Aug 2025 16:54:15 -0700 Subject: [PATCH 56/64] raylib: fix mouse scale for Widgets (#36040) fix mouse scale for mousestate --- system/ui/lib/application.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 718bf036fa..3f433e1fcb 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -67,7 +67,8 @@ class MouseEvent(NamedTuple): class MouseState: - def __init__(self): + def __init__(self, scale: float = 1.0): + self._scale = scale self._events: deque[MouseEvent] = deque(maxlen=MOUSE_THREAD_RATE) # bound event list self._prev_mouse_event: list[MouseEvent | None] = [None] * MAX_TOUCH_SLOTS @@ -102,8 +103,10 @@ class MouseState: def _handle_mouse_event(self): for slot in range(MAX_TOUCH_SLOTS): mouse_pos = rl.get_touch_position(slot) + x = mouse_pos.x / self._scale if self._scale != 1.0 else mouse_pos.x + y = mouse_pos.y / self._scale if self._scale != 1.0 else mouse_pos.y ev = MouseEvent( - MousePos(mouse_pos.x, mouse_pos.y), + MousePos(x, y), slot, rl.is_mouse_button_pressed(slot), rl.is_mouse_button_released(slot), @@ -133,7 +136,7 @@ class GuiApplication: self._trace_log_callback = None self._modal_overlay = ModalOverlay() - self._mouse = MouseState() + self._mouse = MouseState(self._scale) self._mouse_events: list[MouseEvent] = [] # Debug variables From c0a74f7a20da6375d710b92037bbf6143fe3822a Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 22 Aug 2025 01:55:01 -0700 Subject: [PATCH 57/64] raylib: change default tethering password --- system/ui/lib/wifi_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/ui/lib/wifi_manager.py b/system/ui/lib/wifi_manager.py index 178bbec43e..4cb741bc95 100644 --- a/system/ui/lib/wifi_manager.py +++ b/system/ui/lib/wifi_manager.py @@ -37,7 +37,7 @@ NM_DEVICE_IFACE = "org.freedesktop.NetworkManager.Device" NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT = 8 TETHERING_IP_ADDRESS = "192.168.43.1" -DEFAULT_TETHERING_PASSWORD = "12345678" +DEFAULT_TETHERING_PASSWORD = "swagswagcomma" # NetworkManager device states From ae3b74245f0685e70e5e76c193e99f7f175c1db0 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 22 Aug 2025 09:08:40 -0700 Subject: [PATCH 58/64] sgo is just o now! --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index dcf645ff61..f3af58f85b 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,6 @@ We have detailed instructions for [how to install the harness and device in a ca | `release3-staging` | openpilot-test.comma.ai | This is the staging branch for releases. Use it to get new releases slightly early. | | `nightly` | openpilot-nightly.comma.ai | This is the bleeding edge development branch. Do not expect this to be stable. | | `nightly-dev` | installer.comma.ai/commaai/nightly-dev | Same as nightly, but includes experimental development features for some cars. | -| `secretgoodopenpilot` | installer.comma.ai/commaai/secretgoodopenpilot | This is a preview branch from the autonomy team where new driving models get merged earlier than master. | To start developing openpilot ------ From ef2bb7f2fc07f4b94709cee752386be815eea8aa Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 22 Aug 2025 20:06:12 -0700 Subject: [PATCH 59/64] release: build orphaned branch (#36047) --- .github/workflows/release.yaml | 2 +- .github/workflows/selfdrive_tests.yaml | 2 +- release/{build_devel.sh => build_stripped.sh} | 23 ++++++++----------- 3 files changed, 11 insertions(+), 16 deletions(-) rename release/{build_devel.sh => build_stripped.sh} (83%) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 4065b0ef3e..0f4ce6cb3a 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -39,4 +39,4 @@ jobs: git config --global --add safe.directory '*' git lfs pull - name: Push master-ci - run: BRANCH=__nightly release/build_devel.sh + run: BRANCH=__nightly release/build_stripped.sh diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index aad772757d..cdafbbfede 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -52,7 +52,7 @@ jobs: command: git lfs pull - name: Build devel timeout-minutes: 1 - run: TARGET_DIR=$STRIPPED_DIR release/build_devel.sh + run: TARGET_DIR=$STRIPPED_DIR release/build_stripped.sh - uses: ./.github/workflows/setup-with-retry - name: Build openpilot and run checks timeout-minutes: ${{ ((steps.restore-scons-cache.outputs.cache-hit == 'true') && 10 || 30) }} # allow more time when we missed the scons cache diff --git a/release/build_devel.sh b/release/build_stripped.sh similarity index 83% rename from release/build_devel.sh rename to release/build_stripped.sh index c4ef280258..6f1a568c25 100755 --- a/release/build_devel.sh +++ b/release/build_stripped.sh @@ -17,29 +17,23 @@ rm -rf $TARGET_DIR mkdir -p $TARGET_DIR cd $TARGET_DIR cp -r $SOURCE_DIR/.git $TARGET_DIR -pre-commit uninstall || true -echo "[-] bringing __nightly and devel in sync T=$SECONDS" +echo "[-] setting up stripped branch sync T=$SECONDS" cd $TARGET_DIR -git fetch --depth 1 origin __nightly -git fetch --depth 1 origin devel - -git checkout -f --track origin/__nightly -git reset --hard __nightly -git checkout __nightly -git reset --hard origin/devel -git clean -xdff -git submodule foreach --recursive git clean -xdff -git lfs uninstall +# tmp branch +git checkout --orphan tmp # remove everything except .git echo "[-] erasing old openpilot T=$SECONDS" +git submodule deinit -f --all +git rm -rf --cached . find . -maxdepth 1 -not -path './.git' -not -name '.' -not -name '..' -exec rm -rf '{}' \; -# reset source tree +# cleanup before the copy cd $SOURCE_DIR git clean -xdff +git submodule foreach --recursive git clean -xdff # do the files copy echo "[-] copying files T=$SECONDS" @@ -48,6 +42,7 @@ cp -pR --parents $(./release/release_files.py) $TARGET_DIR/ # in the directory cd $TARGET_DIR +rm -rf .git/modules/ rm -f panda/board/obj/panda.bin.signed # include source commit hash and build date in commit @@ -86,7 +81,7 @@ fi if [ ! -z "$BRANCH" ]; then echo "[-] Pushing to $BRANCH T=$SECONDS" - git push -f origin __nightly:$BRANCH + git push -f origin tmp:$BRANCH fi echo "[-] done T=$SECONDS, ready at $TARGET_DIR" From 7ed8abb66c9b3fbbf93e35545cfac56f8d4013b5 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 22 Aug 2025 20:11:50 -0700 Subject: [PATCH 60/64] camerad: garbage collect CL files (#36046) --- system/camerad/sensors/ar0231_cl.h | 34 ----------------- system/camerad/sensors/os04c10_cl.h | 58 ----------------------------- system/camerad/sensors/ox03c10_cl.h | 47 ----------------------- 3 files changed, 139 deletions(-) delete mode 100644 system/camerad/sensors/ar0231_cl.h delete mode 100644 system/camerad/sensors/os04c10_cl.h delete mode 100644 system/camerad/sensors/ox03c10_cl.h diff --git a/system/camerad/sensors/ar0231_cl.h b/system/camerad/sensors/ar0231_cl.h deleted file mode 100644 index c79242543b..0000000000 --- a/system/camerad/sensors/ar0231_cl.h +++ /dev/null @@ -1,34 +0,0 @@ -#if SENSOR_ID == 1 - -#define VIGNETTE_PROFILE_8DT0MM - -#define BIT_DEPTH 12 -#define PV_MAX 4096 -#define BLACK_LVL 168 - -float4 normalize_pv(int4 parsed, float vignette_factor) { - float4 pv = (convert_float4(parsed) - BLACK_LVL) / (PV_MAX - BLACK_LVL); - return clamp(pv*vignette_factor, 0.0, 1.0); -} - -float3 color_correct(float3 rgb) { - float3 corrected = rgb.x * (float3)(1.82717181, -0.31231438, 0.07307673); - corrected += rgb.y * (float3)(-0.5743977, 1.36858544, -0.53183455); - corrected += rgb.z * (float3)(-0.25277411, -0.05627105, 1.45875782); - return corrected; -} - -float3 apply_gamma(float3 rgb, int expo_time) { - // tone mapping params - const float gamma_k = 0.75; - const float gamma_b = 0.125; - const float mp = 0.01; // ideally midpoint should be adaptive - const float rk = 9 - 100*mp; - - // poly approximation for s curve - return (rgb > mp) ? - ((rk * (rgb-mp) * (1-(gamma_k*mp+gamma_b)) * (1+1/(rk*(1-mp))) / (1+rk*(rgb-mp))) + gamma_k*mp + gamma_b) : - ((rk * (rgb-mp) * (gamma_k*mp+gamma_b) * (1+1/(rk*mp)) / (1-rk*(rgb-mp))) + gamma_k*mp + gamma_b); -} - -#endif diff --git a/system/camerad/sensors/os04c10_cl.h b/system/camerad/sensors/os04c10_cl.h deleted file mode 100644 index 3b5cf88839..0000000000 --- a/system/camerad/sensors/os04c10_cl.h +++ /dev/null @@ -1,58 +0,0 @@ -#if SENSOR_ID == 3 - -#define BGGR -#define VIGNETTE_PROFILE_4DT6MM - -#define BIT_DEPTH 12 -#define PV_MAX10 1023 -#define PV_MAX12 4095 -#define PV_MAX16 65536 // gamma curve is calibrated to 16bit -#define BLACK_LVL 48 - -float combine_dual_pvs(float lv, float sv, int expo_time) { - float svc = fmax(sv * expo_time, (float)(64 * (PV_MAX10 - BLACK_LVL))); - float svd = sv * fmin(expo_time, 8.0) / 8; - - if (expo_time > 64) { - if (lv < PV_MAX10 - BLACK_LVL) { - return lv / (PV_MAX16 - BLACK_LVL); - } else { - return (svc / 64) / (PV_MAX16 - BLACK_LVL); - } - } else { - if (lv > 32) { - return (lv * 64 / fmax(expo_time, 8.0)) / (PV_MAX16 - BLACK_LVL); - } else { - return svd / (PV_MAX16 - BLACK_LVL); - } - } -} - -float4 normalize_pv_hdr(int4 parsed, int4 short_parsed, float vignette_factor, int expo_time) { - float4 pl = convert_float4(parsed - BLACK_LVL); - float4 ps = convert_float4(short_parsed - BLACK_LVL); - float4 pv; - pv.s0 = combine_dual_pvs(pl.s0, ps.s0, expo_time); - pv.s1 = combine_dual_pvs(pl.s1, ps.s1, expo_time); - pv.s2 = combine_dual_pvs(pl.s2, ps.s2, expo_time); - pv.s3 = combine_dual_pvs(pl.s3, ps.s3, expo_time); - return clamp(pv*vignette_factor, 0.0, 1.0); -} - -float4 normalize_pv(int4 parsed, float vignette_factor) { - float4 pv = (convert_float4(parsed) - BLACK_LVL) / (PV_MAX12 - BLACK_LVL); - return clamp(pv*vignette_factor, 0.0, 1.0); -} - -float3 color_correct(float3 rgb) { - float3 corrected = rgb.x * (float3)(1.55361989, -0.268894615, -0.000593219); - corrected += rgb.y * (float3)(-0.421217301, 1.51883144, -0.69760146); - corrected += rgb.z * (float3)(-0.132402589, -0.249936825, 1.69819468); - return corrected; -} - -float3 apply_gamma(float3 rgb, int expo_time) { - return (10 * rgb) / (1 + 9 * rgb); -} - -#endif diff --git a/system/camerad/sensors/ox03c10_cl.h b/system/camerad/sensors/ox03c10_cl.h deleted file mode 100644 index c8cec7cf8a..0000000000 --- a/system/camerad/sensors/ox03c10_cl.h +++ /dev/null @@ -1,47 +0,0 @@ -#if SENSOR_ID == 2 - -#define VIGNETTE_PROFILE_8DT0MM - -#define BIT_DEPTH 12 -#define BLACK_LVL 64 - -float ox_lut_func(int x) { - if (x < 512) { - return x * 5.94873e-8; - } else if (512 <= x && x < 768) { - return 3.0458e-05 + (x-512) * 1.19913e-7; - } else if (768 <= x && x < 1536) { - return 6.1154e-05 + (x-768) * 2.38493e-7; - } else if (1536 <= x && x < 1792) { - return 0.0002448 + (x-1536) * 9.56930e-7; - } else if (1792 <= x && x < 2048) { - return 0.00048977 + (x-1792) * 1.91441e-6; - } else if (2048 <= x && x < 2304) { - return 0.00097984 + (x-2048) * 3.82937e-6; - } else if (2304 <= x && x < 2560) { - return 0.0019601 + (x-2304) * 7.659055e-6; - } else if (2560 <= x && x < 2816) { - return 0.0039207 + (x-2560) * 1.525e-5; - } else { - return 0.0078421 + (exp((x-2816)/273.0) - 1) * 0.0092421; - } -} - -float4 normalize_pv(int4 parsed, float vignette_factor) { - // PWL - float4 pv = {ox_lut_func(parsed.s0), ox_lut_func(parsed.s1), ox_lut_func(parsed.s2), ox_lut_func(parsed.s3)}; - return clamp(pv*vignette_factor*256.0, 0.0, 1.0); -} - -float3 color_correct(float3 rgb) { - float3 corrected = rgb.x * (float3)(1.5664815, -0.29808738, -0.03973474); - corrected += rgb.y * (float3)(-0.48672447, 1.41914433, -0.40295248); - corrected += rgb.z * (float3)(-0.07975703, -0.12105695, 1.44268722); - return corrected; -} - -float3 apply_gamma(float3 rgb, int expo_time) { - return -0.507089*exp(-12.54124638*rgb) + 0.9655*powr(rgb, 0.5) - 0.472597*rgb + 0.507089; -} - -#endif From 2b46e1450ab9ce42aae7f531924c00db7453c7a8 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 23 Aug 2025 00:49:19 -0700 Subject: [PATCH 61/64] raylib: simplify network state (#36049) * wtf * we never disabled unsupported networks * dont be a hero * i hate mypy * fix --- system/ui/widgets/network.py | 126 +++++++++++++++-------------------- 1 file changed, 55 insertions(+), 71 deletions(-) diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 4eac1214c2..0bb759a919 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -1,17 +1,17 @@ -from dataclasses import dataclass +from enum import IntEnum from functools import partial from threading import Lock -from typing import Literal +from typing import cast import pyray as rl from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.wifi_manager import NetworkInfo, WifiManagerCallbacks, WifiManagerWrapper, SecurityType from openpilot.system.ui.widgets import Widget -from openpilot.system.ui.widgets.button import ButtonStyle, Button, TextAlignment +from openpilot.system.ui.widgets.button import ButtonStyle, Button from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog from openpilot.system.ui.widgets.keyboard import Keyboard -from openpilot.system.ui.widgets.label import gui_label +from openpilot.system.ui.widgets.label import TextAlignment, gui_label NM_DEVICE_STATE_NEED_AUTH = 60 MIN_PASSWORD_LENGTH = 8 @@ -27,43 +27,20 @@ STRENGTH_ICONS = [ ] -@dataclass -class StateIdle: - action: Literal["idle"] = "idle" - - -@dataclass -class StateConnecting: - network: NetworkInfo - action: Literal["connecting"] = "connecting" - - -@dataclass -class StateNeedsAuth: - network: NetworkInfo - retry: bool - action: Literal["needs_auth"] = "needs_auth" - - -@dataclass -class StateShowForgetConfirm: - network: NetworkInfo - action: Literal["show_forget_confirm"] = "show_forget_confirm" - - -@dataclass -class StateForgetting: - network: NetworkInfo - action: Literal["forgetting"] = "forgetting" - - -UIState = StateIdle | StateConnecting | StateNeedsAuth | StateShowForgetConfirm | StateForgetting +class UIState(IntEnum): + IDLE = 0 + CONNECTING = 1 + NEEDS_AUTH = 2 + SHOW_FORGET_CONFIRM = 3 + FORGETTING = 4 class WifiManagerUI(Widget): def __init__(self, wifi_manager: WifiManagerWrapper): super().__init__() - self.state: UIState = StateIdle() + self.state: UIState = UIState.IDLE + self._state_network: NetworkInfo | None = None # for CONNECTING / NEEDS_AUTH / SHOW_FORGET_CONFIRM / FORGETTING + self._password_retry: bool = False # for NEEDS_AUTH self.btn_width: int = 200 self.scroll_panel = GuiScrollPanel() self.keyboard = Keyboard(max_text_size=MAX_PASSWORD_LENGTH, min_text_size=MIN_PASSWORD_LENGTH, show_password_toggle=True) @@ -93,17 +70,16 @@ class WifiManagerUI(Widget): gui_label(rect, "Scanning Wi-Fi networks...", 72, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER) return - match self.state: - case StateNeedsAuth(network, retry): - self.keyboard.set_title("Wrong password" if retry else "Enter password", f"for {network.ssid}") - self.keyboard.reset() - gui_app.set_modal_overlay(self.keyboard, lambda result: self._on_password_entered(network, result)) - case StateShowForgetConfirm(network): - self._confirm_dialog.set_text(f'Forget Wi-Fi Network "{network.ssid}"?') - self._confirm_dialog.reset() - gui_app.set_modal_overlay(self._confirm_dialog, callback=lambda result: self.on_forgot_confirm_finished(network, result)) - case _: - self._draw_network_list(rect) + if self.state == UIState.NEEDS_AUTH and self._state_network: + self.keyboard.set_title("Wrong password" if self._password_retry else "Enter password", f"for {self._state_network.ssid}") + self.keyboard.reset() + gui_app.set_modal_overlay(self.keyboard, lambda result: self._on_password_entered(cast(NetworkInfo, self._state_network), result)) + elif self.state == UIState.SHOW_FORGET_CONFIRM and self._state_network: + self._confirm_dialog.set_text(f'Forget Wi-Fi Network "{self._state_network.ssid}"?') + self._confirm_dialog.reset() + gui_app.set_modal_overlay(self._confirm_dialog, callback=lambda result: self.on_forgot_confirm_finished(self._state_network, result)) + else: + self._draw_network_list(rect) def _on_password_entered(self, network: NetworkInfo, result: int): if result == 1: @@ -113,13 +89,13 @@ class WifiManagerUI(Widget): if len(password) >= MIN_PASSWORD_LENGTH: self.connect_to_network(network, password) elif result == 0: - self.state = StateIdle() + self.state = UIState.IDLE def on_forgot_confirm_finished(self, network, result: int): if result == 1: self.forget_network(network) elif result == 0: - self.state = StateIdle() + self.state = UIState.IDLE def _draw_network_list(self, rect: rl.Rectangle): content_rect = rl.Rectangle(rect.x, rect.y, rect.width, len(self._networks) * ITEM_HEIGHT) @@ -147,17 +123,18 @@ class WifiManagerUI(Widget): security_icon_rect = rl.Rectangle(signal_icon_rect.x - spacing - ICON_SIZE, rect.y + (ITEM_HEIGHT - ICON_SIZE) / 2, ICON_SIZE, ICON_SIZE) status_text = "" - match self.state: - case StateConnecting(network=connecting): - if connecting.ssid == network.ssid: - self._networks_buttons[network.ssid].set_enabled(False) - status_text = "CONNECTING..." - case StateForgetting(network=forgetting): - if forgetting.ssid == network.ssid: - self._networks_buttons[network.ssid].set_enabled(False) - status_text = "FORGETTING..." - case _: - self._networks_buttons[network.ssid].set_enabled(True) + if self.state == UIState.CONNECTING and self._state_network: + if self._state_network.ssid == network.ssid: + self._networks_buttons[network.ssid].set_enabled(False) + status_text = "CONNECTING..." + elif self.state == UIState.FORGETTING and self._state_network: + if self._state_network.ssid == network.ssid: + self._networks_buttons[network.ssid].set_enabled(False) + status_text = "FORGETTING..." + elif network.security_type == SecurityType.UNSUPPORTED: + self._networks_buttons[network.ssid].set_enabled(False) + else: + self._networks_buttons[network.ssid].set_enabled(True) self._networks_buttons[network.ssid].render(ssid_rect) @@ -181,13 +158,16 @@ class WifiManagerUI(Widget): def _networks_buttons_callback(self, network): if self.scroll_panel.is_touch_valid(): if not network.is_saved and network.security_type != SecurityType.OPEN: - self.state = StateNeedsAuth(network, False) + self.state = UIState.NEEDS_AUTH + self._state_network = network + self._password_retry = False elif not network.is_connected: self.connect_to_network(network) def _forget_networks_buttons_callback(self, network): if self.scroll_panel.is_touch_valid(): - self.state = StateShowForgetConfirm(network) + self.state = UIState.SHOW_FORGET_CONFIRM + self._state_network = network def _draw_status_icon(self, rect, network: NetworkInfo): """Draw the status icon based on network's connection state""" @@ -212,14 +192,16 @@ class WifiManagerUI(Widget): rl.draw_texture_v(gui_app.texture(STRENGTH_ICONS[strength_level], ICON_SIZE, ICON_SIZE), rl.Vector2(rect.x, rect.y), rl.WHITE) def connect_to_network(self, network: NetworkInfo, password=''): - self.state = StateConnecting(network) + self.state = UIState.CONNECTING + self._state_network = network if network.is_saved and not password: self.wifi_manager.activate_connection(network.ssid) else: self.wifi_manager.connect_to_network(network.ssid, password) def forget_network(self, network: NetworkInfo): - self.state = StateForgetting(network) + self.state = UIState.FORGETTING + self._state_network = network network.is_saved = False self.wifi_manager.forget_connection(network.ssid) @@ -236,22 +218,24 @@ class WifiManagerUI(Widget): with self._lock: network = next((n for n in self._networks if n.ssid == ssid), None) if network: - self.state = StateNeedsAuth(network, True) + self.state = UIState.NEEDS_AUTH + self._state_network = network + self._password_retry = True def _on_activated(self): with self._lock: - if isinstance(self.state, StateConnecting): - self.state = StateIdle() + if self.state == UIState.CONNECTING: + self.state = UIState.IDLE def _on_forgotten(self, ssid): with self._lock: - if isinstance(self.state, StateForgetting): - self.state = StateIdle() + if self.state == UIState.FORGETTING: + self.state = UIState.IDLE def _on_connection_failed(self, ssid: str, error: str): with self._lock: - if isinstance(self.state, StateConnecting): - self.state = StateIdle() + if self.state == UIState.CONNECTING: + self.state = UIState.IDLE def main(): From dd7de180ea9ee95b1440788f6e018dc2cb82540f Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 23 Aug 2025 04:56:00 -0700 Subject: [PATCH 62/64] raylib: cache API token (#36050) * cache with time * safety * rm * clean up --- selfdrive/ui/lib/prime_state.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/selfdrive/ui/lib/prime_state.py b/selfdrive/ui/lib/prime_state.py index b6c0d88469..da2ff899dd 100644 --- a/selfdrive/ui/lib/prime_state.py +++ b/selfdrive/ui/lib/prime_state.py @@ -2,12 +2,15 @@ from enum import IntEnum import os import threading import time +from functools import lru_cache from openpilot.common.api import Api, api_get from openpilot.common.params import Params from openpilot.common.swaglog import cloudlog from openpilot.system.athena.registration import UNREGISTERED_DONGLE_ID +TOKEN_EXPIRY_HOURS = 2 + class PrimeType(IntEnum): UNKNOWN = -2, @@ -20,6 +23,12 @@ class PrimeType(IntEnum): PURPLE = 5, +@lru_cache(maxsize=1) +def get_token(dongle_id: str, t: int): + print('getting token') + return Api(dongle_id).get_token(expiry_hours=TOKEN_EXPIRY_HOURS) + + class PrimeState: FETCH_INTERVAL = 5.0 # seconds between API calls API_TIMEOUT = 10.0 # seconds for API requests @@ -49,13 +58,15 @@ class PrimeState: return try: - identity_token = Api(dongle_id).get_token() + identity_token = get_token(dongle_id, int(time.monotonic() / (TOKEN_EXPIRY_HOURS / 2 * 60 * 60))) response = api_get(f"v1.1/devices/{dongle_id}", timeout=self.API_TIMEOUT, access_token=identity_token) if response.status_code == 200: data = response.json() is_paired = data.get("is_paired", False) prime_type = data.get("prime_type", 0) self.set_type(PrimeType(prime_type) if is_paired else PrimeType.UNPAIRED) + elif response.status_code == 401: + get_token.cache_clear() except Exception as e: cloudlog.error(f"Failed to fetch prime status: {e}") From f89796033c87f44af1aa0bac9613c7067b3f7650 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 24 Aug 2025 15:52:38 -0700 Subject: [PATCH 63/64] [bot] Update translations (#1179) Update translations Co-authored-by: github-actions[bot] --- selfdrive/ui/translations/main_ar.ts | 10 ---------- selfdrive/ui/translations/main_de.ts | 10 ---------- selfdrive/ui/translations/main_es.ts | 10 ---------- selfdrive/ui/translations/main_fr.ts | 10 ---------- selfdrive/ui/translations/main_ja.ts | 10 ---------- selfdrive/ui/translations/main_ko.ts | 12 ------------ selfdrive/ui/translations/main_pt-BR.ts | 10 ---------- selfdrive/ui/translations/main_th.ts | 10 ---------- selfdrive/ui/translations/main_tr.ts | 10 ---------- selfdrive/ui/translations/main_zh-CHS.ts | 10 ---------- selfdrive/ui/translations/main_zh-CHT.ts | 10 ---------- 11 files changed, 112 deletions(-) diff --git a/selfdrive/ui/translations/main_ar.ts b/selfdrive/ui/translations/main_ar.ts index bebddf259e..f72ea8ee83 100644 --- a/selfdrive/ui/translations/main_ar.ts +++ b/selfdrive/ui/translations/main_ar.ts @@ -2248,16 +2248,6 @@ Warning: You are on a metered connection! Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. - - Record Audio Feedback with LKAS button - - - - Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. - -Note that this feature is only compatible with select cars. - - Enable sunnypilot diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index 1e477553e6..193e2fd895 100644 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -2230,16 +2230,6 @@ Warning: You are on a metered connection! Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. - - Record Audio Feedback with LKAS button - - - - Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. - -Note that this feature is only compatible with select cars. - - Enable sunnypilot diff --git a/selfdrive/ui/translations/main_es.ts b/selfdrive/ui/translations/main_es.ts index e6c22bd143..1d1723cde7 100644 --- a/selfdrive/ui/translations/main_es.ts +++ b/selfdrive/ui/translations/main_es.ts @@ -2232,16 +2232,6 @@ Warning: You are on a metered connection! Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. Graba y almacena el audio del micrófono mientras conduces. El audio se incluirá en el video de la cámara del tablero en comma connect. - - Record Audio Feedback with LKAS button - - - - Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. - -Note that this feature is only compatible with select cars. - - Enable sunnypilot diff --git a/selfdrive/ui/translations/main_fr.ts b/selfdrive/ui/translations/main_fr.ts index f70c5eeebb..959321bb4b 100644 --- a/selfdrive/ui/translations/main_fr.ts +++ b/selfdrive/ui/translations/main_fr.ts @@ -2228,16 +2228,6 @@ Warning: You are on a metered connection! Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. - - Record Audio Feedback with LKAS button - - - - Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. - -Note that this feature is only compatible with select cars. - - Enable sunnypilot diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index b67e0a9709..28b5b00e51 100644 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -2227,16 +2227,6 @@ Warning: You are on a metered connection! Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. 運転中にマイク音声を録音・保存します。音声は comma connect のドライブレコーダー映像に含まれます。 - - Record Audio Feedback with LKAS button - - - - Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. - -Note that this feature is only compatible with select cars. - - Enable sunnypilot diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index 2fdd601e6a..d69fda88b0 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -2241,18 +2241,6 @@ Warning: You are on a metered connection! Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. 운전 중 마이크 오디오를 녹음하고 저장합니다. 이 오디오는 comma connect의 대시캠 영상에 포함됩니다. - - Record Audio Feedback with LKAS button - LKAS 버튼으로 오디오 피드백 녹음 - - - Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. - -Note that this feature is only compatible with select cars. - LKAS 버튼을 눌러 openpilot 팀과 주행 피드백을 녹음하고 공유하세요. 이 기능을 비활성화하면, 해당 버튼은 북마크 버튼 역할을 합니다. 이 이벤트는 comma connect에서 강조되며, 해당 구간 영상은 기기 저장소에 보존됩니다. - -참고: 이 기능은 일부 차량에서만 호환됩니다. - Enable sunnypilot sunnypilot 사용 diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index c78bb9255c..c5171b19f7 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -2232,16 +2232,6 @@ Warning: You are on a metered connection! Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. Grave e armazene o áudio do microfone enquanto estiver dirigindo. O áudio será incluído ao vídeo dashcam no comma connect. - - Record Audio Feedback with LKAS button - - - - Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. - -Note that this feature is only compatible with select cars. - - Enable sunnypilot diff --git a/selfdrive/ui/translations/main_th.ts b/selfdrive/ui/translations/main_th.ts index 2dbf3a6a05..3bd2d84a92 100644 --- a/selfdrive/ui/translations/main_th.ts +++ b/selfdrive/ui/translations/main_th.ts @@ -2223,16 +2223,6 @@ Warning: You are on a metered connection! Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. - - Record Audio Feedback with LKAS button - - - - Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. - -Note that this feature is only compatible with select cars. - - Enable sunnypilot diff --git a/selfdrive/ui/translations/main_tr.ts b/selfdrive/ui/translations/main_tr.ts index 5f06ef5cf6..db9c15580a 100644 --- a/selfdrive/ui/translations/main_tr.ts +++ b/selfdrive/ui/translations/main_tr.ts @@ -2222,16 +2222,6 @@ Warning: You are on a metered connection! Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. - - Record Audio Feedback with LKAS button - - - - Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. - -Note that this feature is only compatible with select cars. - - Enable sunnypilot diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index a6f3e9bf0c..2316360891 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -2227,16 +2227,6 @@ Warning: You are on a metered connection! Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. 在驾驶时录制并存储麦克风音频。该音频将会包含在 comma connect 的行车记录仪视频中。 - - Record Audio Feedback with LKAS button - - - - Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. - -Note that this feature is only compatible with select cars. - - Enable sunnypilot diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index a57fecc7de..16bb393dea 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -2227,16 +2227,6 @@ Warning: You are on a metered connection! Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. 在駕駛時錄製並儲存麥克風音訊。此音訊將會收錄在 comma connect 的行車記錄器影片中。 - - Record Audio Feedback with LKAS button - - - - Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. - -Note that this feature is only compatible with select cars. - - Enable sunnypilot From 7c6d887187497c2090b38bd356e2943b56874875 Mon Sep 17 00:00:00 2001 From: James Vecellio-Grant <159560811+Discountchubbs@users.noreply.github.com> Date: Sun, 24 Aug 2025 19:08:55 -0700 Subject: [PATCH 64/64] modeld_v2: infer model shapes from inputs (#1162) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * modeld_v2: dynamify temporal buffer management. * skip redundant reshaping and flattening. * simplify MHP checks for lead and plan * modeld_v2: add unit tests for buffer logic and refactor index mapping * Let’s possibly fail a test :) * Update test_buffer_logic_inspect.py * Update test_buffer_logic_inspect.py * modeld_v2: better temporal mapping for non-split * Bump to 10 I guess * Downgrade CURRENT_SELECTOR_VERSION to 9 * red diff ya know? * add dynamic buffer update tests and compare against legacy logic. Cover modelState.init and modelState.run * send * Revert "send" This reverts commit 9e6c95fbfde134eeba952da6eef012baa0396fa0. * format --------- Co-authored-by: Jason Wen --- sunnypilot/modeld_v2/modeld.py | 86 +++--- .../modeld_v2/parse_model_outputs_split.py | 35 ++- .../tests/test_buffer_logic_inspect.py | 256 ++++++++++++++++++ .../runners/tinygrad/tinygrad_runner.py | 2 +- 4 files changed, 308 insertions(+), 71 deletions(-) create mode 100644 sunnypilot/modeld_v2/tests/test_buffer_logic_inspect.py diff --git a/sunnypilot/modeld_v2/modeld.py b/sunnypilot/modeld_v2/modeld.py index 2dc1026c01..1a420e6f1a 100755 --- a/sunnypilot/modeld_v2/modeld.py +++ b/sunnypilot/modeld_v2/modeld.py @@ -69,24 +69,26 @@ class ModelState(ModelStateBase): # img buffers are managed in openCL transform code self.numpy_inputs = {} + self.temporal_buffers = {} + self.temporal_idxs_map = {} for key, shape in self.model_runner.input_shapes.items(): if key not in self.frames: # Managed by opencl self.numpy_inputs[key] = np.zeros(shape, dtype=np.float32) - - if self.model_runner.is_20hz_3d: # split models - self.full_features_buffer = np.zeros((1, self.constants.FULL_HISTORY_BUFFER_LEN, self.constants.FEATURE_LEN), dtype=np.float32) - self.full_desire = np.zeros((1, self.constants.FULL_HISTORY_BUFFER_LEN, self.constants.DESIRE_LEN), dtype=np.float32) - self.full_prev_desired_curv = np.zeros((1, self.constants.FULL_HISTORY_BUFFER_LEN, self.constants.PREV_DESIRED_CURV_LEN), dtype=np.float32) - self.temporal_idxs = slice(-1-(self.constants.TEMPORAL_SKIP*(self.constants.INPUT_HISTORY_BUFFER_LEN-1)), None, self.constants.TEMPORAL_SKIP) - elif self.model_runner.is_20hz and not self.model_runner.is_20hz_3d: - self.full_features_buffer = np.zeros((self.constants.FULL_HISTORY_BUFFER_LEN + 1, self.constants.FEATURE_LEN), dtype=np.float32) - self.full_desire = np.zeros((self.constants.FULL_HISTORY_BUFFER_LEN + 1, self.constants.DESIRE_LEN), dtype=np.float32) - num_elements = self.numpy_inputs['features_buffer'].shape[1] - step_size = int(-100 / num_elements) - self.temporal_idxs = np.arange(step_size, step_size * (num_elements + 1), step_size)[::-1] - self.desire_reshape_dims = (self.numpy_inputs['desire'].shape[0], self.numpy_inputs['desire'].shape[1], -1, - self.numpy_inputs['desire'].shape[2]) + # Temporal input: shape is [batch, history, features] + if len(shape) == 3 and shape[1] > 1: + buffer_history_len = max(100, (shape[1] * 4 if shape[1] < 100 else shape[1])) # Allow for higher history buffers in the future + feature_len = shape[2] + self.temporal_buffers[key] = np.zeros((1, buffer_history_len, feature_len), dtype=np.float32) + features_buffer_shape = self.model_runner.input_shapes.get('features_buffer') + if shape[1] in (24, 25) and features_buffer_shape is not None and features_buffer_shape[1] == 24: # 20Hz + step = int(-buffer_history_len / shape[1]) + self.temporal_idxs_map[key] = np.arange(step, step * (shape[1] + 1), step)[::-1] + elif shape[1] == 25: # Split + skip = buffer_history_len // shape[1] + self.temporal_idxs_map[key] = np.arange(buffer_history_len)[-1 - (skip * (shape[1] - 1))::skip] + elif shape[1] == buffer_history_len: # non20hz + self.temporal_idxs_map[key] = np.arange(buffer_history_len) @property def mlsim(self) -> bool: @@ -98,19 +100,16 @@ class ModelState(ModelStateBase): inputs['desire'][0] = 0 new_desire = np.where(inputs['desire'] - self.prev_desire > .99, inputs['desire'], 0) self.prev_desire[:] = inputs['desire'] + self.temporal_buffers['desire'][0,:-1] = self.temporal_buffers['desire'][0,1:] + self.temporal_buffers['desire'][0,-1] = new_desire - if self.model_runner.is_20hz_3d: # split models - self.full_desire[0,:-1] = self.full_desire[0,1:] - self.full_desire[0,-1] = new_desire - self.numpy_inputs['desire'][:] = self.full_desire.reshape((1, self.constants.INPUT_HISTORY_BUFFER_LEN, self.constants.TEMPORAL_SKIP, -1)).max(axis=2) - elif self.model_runner.is_20hz and not self.model_runner.is_20hz_3d: # 20hz supercombo - self.full_desire[:-1] = self.full_desire[1:] - self.full_desire[-1] = new_desire - self.numpy_inputs['desire'][:] = self.full_desire.reshape(self.desire_reshape_dims).max(axis=2) - else: # not 20hz - length = inputs['desire'].shape[0] - self.numpy_inputs['desire'][0, :-1] = self.numpy_inputs['desire'][0, 1:] - self.numpy_inputs['desire'][0, -1, :length] = new_desire[:length] + # Roll buffer and assign based on desire.shape[1] value + if self.temporal_buffers['desire'].shape[1] > self.numpy_inputs['desire'].shape[1]: + skip = self.temporal_buffers['desire'].shape[1] // self.numpy_inputs['desire'].shape[1] + self.numpy_inputs['desire'][:] = ( + self.temporal_buffers['desire'][0].reshape(self.numpy_inputs['desire'].shape[0], self.numpy_inputs['desire'].shape[1], skip, -1).max(axis=2)) + else: + self.numpy_inputs['desire'][:] = self.temporal_buffers['desire'][0, self.temporal_idxs_map['desire']] for key in self.numpy_inputs: if key in inputs and key not in ['desire']: @@ -127,42 +126,27 @@ class ModelState(ModelStateBase): # Run model inference outputs = self.model_runner.run_model() - if self.model_runner.is_20hz_3d: # split models - self.full_features_buffer[0, :-1] = self.full_features_buffer[0, 1:] - self.full_features_buffer[0, -1] = outputs['hidden_state'][0, :] - self.numpy_inputs['features_buffer'][:] = self.full_features_buffer[0, self.temporal_idxs] - elif self.model_runner.is_20hz and not self.model_runner.is_20hz_3d: # 20hz supercombo - self.full_features_buffer[:-1] = self.full_features_buffer[1:] - self.full_features_buffer[-1] = outputs['hidden_state'][0, :] - self.numpy_inputs['features_buffer'][:] = self.full_features_buffer[self.temporal_idxs] - else: # not 20hz - feature_len = outputs['hidden_state'].shape[1] - self.numpy_inputs['features_buffer'][0, :-1] = self.numpy_inputs['features_buffer'][0, 1:] - self.numpy_inputs['features_buffer'][0, -1, :feature_len] = outputs['hidden_state'][0, :feature_len] + # Update features_buffer + self.temporal_buffers['features_buffer'][0, :-1] = self.temporal_buffers['features_buffer'][0, 1:] + self.temporal_buffers['features_buffer'][0, -1] = outputs['hidden_state'][0, :] + self.numpy_inputs['features_buffer'][:] = self.temporal_buffers['features_buffer'][0, self.temporal_idxs_map['features_buffer']] if "desired_curvature" in outputs: input_name_prev = None - if "prev_desired_curvs" in self.numpy_inputs.keys(): input_name_prev = 'prev_desired_curvs' elif "prev_desired_curv" in self.numpy_inputs.keys(): input_name_prev = 'prev_desired_curv' - - if input_name_prev is not None: + if input_name_prev and input_name_prev in self.temporal_buffers: self.process_desired_curvature(outputs, input_name_prev) return outputs def process_desired_curvature(self, outputs, input_name_prev): - if self.model_runner.is_20hz_3d: # split models - self.full_prev_desired_curv[0,:-1] = self.full_prev_desired_curv[0,1:] - self.full_prev_desired_curv[0,-1,:] = outputs['desired_curvature'][0, :] - self.numpy_inputs[input_name_prev][:] = self.full_prev_desired_curv[0, self.temporal_idxs] - if self.mlsim: - self.numpy_inputs[input_name_prev][:] = 0*self.full_prev_desired_curv[0, self.temporal_idxs] - else: - length = outputs['desired_curvature'][0].size - self.numpy_inputs[input_name_prev][0, :-length, 0] = self.numpy_inputs[input_name_prev][0, length:, 0] - self.numpy_inputs[input_name_prev][0, -length:, 0] = outputs['desired_curvature'][0] + self.temporal_buffers[input_name_prev][0,:-1] = self.temporal_buffers[input_name_prev][0,1:] + self.temporal_buffers[input_name_prev][0,-1,:] = outputs['desired_curvature'][0, :] + self.numpy_inputs[input_name_prev][:] = self.temporal_buffers[input_name_prev][0, self.temporal_idxs_map[input_name_prev]] + if self.mlsim: + self.numpy_inputs[input_name_prev][:] = 0*self.temporal_buffers[input_name_prev][0, self.temporal_idxs_map[input_name_prev]] def get_action_from_model(self, model_output: dict[str, np.ndarray], prev_action: log.ModelDataV2.Action, lat_action_t: float, long_action_t: float, v_ego: float) -> log.ModelDataV2.Action: diff --git a/sunnypilot/modeld_v2/parse_model_outputs_split.py b/sunnypilot/modeld_v2/parse_model_outputs_split.py index ba8592ed18..9cf321a1b6 100644 --- a/sunnypilot/modeld_v2/parse_model_outputs_split.py +++ b/sunnypilot/modeld_v2/parse_model_outputs_split.py @@ -1,6 +1,5 @@ import numpy as np from openpilot.sunnypilot.models.split_model_constants import SplitModelConstants -from openpilot.sunnypilot.models.helpers import get_active_bundle def safe_exp(x, out=None): @@ -25,8 +24,6 @@ def softmax(x, axis=-1): class Parser: def __init__(self, ignore_missing=False): self.ignore_missing = ignore_missing - model_bundle = get_active_bundle() - self.generation = model_bundle.generation if model_bundle is not None else None def check_missing(self, outs, name): if name not in outs and not self.ignore_missing: @@ -91,26 +88,26 @@ class Parser: outs[name] = pred_mu_final.reshape(final_shape) outs[name + '_stds'] = pred_std_final.reshape(final_shape) - def _parse_plan_mhp(self, outs): - self.parse_mdn('plan', outs, in_N=SplitModelConstants.PLAN_MHP_N, out_N=SplitModelConstants.PLAN_MHP_SELECTION, - out_shape=(SplitModelConstants.IDX_N,SplitModelConstants.PLAN_WIDTH)) + def is_mhp(self, outs, name, shape): + if self.check_missing(outs, name): + return False + if outs[name].shape[1] == 2 * shape: + return False + return True def parse_dynamic_outputs(self, outs: dict[str, np.ndarray]) -> None: if 'lead' in outs: - if self.generation >= 12 and \ - outs['lead'].shape[1] == 2 * SplitModelConstants.LEAD_MHP_SELECTION * SplitModelConstants.LEAD_TRAJ_LEN * SplitModelConstants.LEAD_WIDTH: - self.parse_mdn('lead', outs, in_N=0, out_N=0, - out_shape=(SplitModelConstants.LEAD_MHP_SELECTION, SplitModelConstants.LEAD_TRAJ_LEN, SplitModelConstants.LEAD_WIDTH)) - else: - self.parse_mdn('lead', outs, in_N=SplitModelConstants.LEAD_MHP_N, out_N=SplitModelConstants.LEAD_MHP_SELECTION, - out_shape=(SplitModelConstants.LEAD_TRAJ_LEN, SplitModelConstants.LEAD_WIDTH)) + lead_mhp = self.is_mhp(outs, 'lead', + SplitModelConstants.LEAD_MHP_SELECTION * SplitModelConstants.LEAD_TRAJ_LEN * SplitModelConstants.LEAD_WIDTH) + lead_in_N, lead_out_N = (SplitModelConstants.LEAD_MHP_N, SplitModelConstants.LEAD_MHP_SELECTION) if lead_mhp else (0, 0) + lead_out_shape = (SplitModelConstants.LEAD_TRAJ_LEN, SplitModelConstants.LEAD_WIDTH) if lead_mhp else \ + (SplitModelConstants.LEAD_MHP_SELECTION, SplitModelConstants.LEAD_TRAJ_LEN, SplitModelConstants.LEAD_WIDTH) + self.parse_mdn('lead', outs, in_N=lead_in_N, out_N=lead_out_N, out_shape=lead_out_shape) if 'plan' in outs: - if self.generation >= 12 and \ - outs['plan'].shape[1] == 2 * SplitModelConstants.IDX_N * SplitModelConstants.PLAN_WIDTH: - self.parse_mdn('plan', outs, in_N=0, out_N=0, - out_shape=(SplitModelConstants.IDX_N, SplitModelConstants.PLAN_WIDTH)) - else: - self._parse_plan_mhp(outs) + plan_mhp = self.is_mhp(outs, 'plan', SplitModelConstants.IDX_N * SplitModelConstants.PLAN_WIDTH) + plan_in_N, plan_out_N = (SplitModelConstants.PLAN_MHP_N, SplitModelConstants.PLAN_MHP_SELECTION) if plan_mhp else (0, 0) + self.parse_mdn('plan', outs, in_N=plan_in_N, out_N=plan_out_N, + out_shape=(SplitModelConstants.IDX_N, SplitModelConstants.PLAN_WIDTH)) def split_outputs(self, outs: dict[str, np.ndarray]) -> None: if 'desired_curvature' in outs: diff --git a/sunnypilot/modeld_v2/tests/test_buffer_logic_inspect.py b/sunnypilot/modeld_v2/tests/test_buffer_logic_inspect.py new file mode 100644 index 0000000000..8a0cfd97c8 --- /dev/null +++ b/sunnypilot/modeld_v2/tests/test_buffer_logic_inspect.py @@ -0,0 +1,256 @@ +import numpy as np +import pytest +from typing import Any + +import openpilot.sunnypilot.models.helpers as helpers +import openpilot.sunnypilot.models.runners.helpers as runner_helpers +import openpilot.sunnypilot.modeld_v2.modeld as modeld_module + +ModelState = modeld_module.ModelState + + +# These are the shapes extracted/loaded from the model onnx +SHAPE_MODE_PARAMS = [ + ({'desire': (1, 25, 8), 'features_buffer': (1, 25, 512), 'prev_desired_curv': (1, 25, 1)}, 'split'), + ({'desire': (1, 25, 8), 'features_buffer': (1, 24, 512), 'prev_desired_curv': (1, 25, 1)}, '20hz'), + ({'desire': (1, 100, 8), 'features_buffer': (1, 99, 512), 'prev_desired_curv': (1, 100, 1)}, 'non20hz'), +] + + +# This creates a dummy runner, override, and bundle instance for the tests to run, without actually trying to load a physical model. +class DummyOverride: + def __init__(self, key: str, value: str) -> None: + self.key = key + self.value = value + + +class DummyBundle: + def __init__(self) -> None: + self.overrides = [DummyOverride('lat', '.1'), DummyOverride('long', '.3')] + self.generation = 10 # default to non-mlsim for buffer-update tests, as raising to 11 here will zero curvature buffer + + +class DummyModelRunner: + def __init__(self, input_shapes: dict[str, tuple[int, int, int]], constants: Any = None) -> None: + self.input_shapes = input_shapes + self.constants = constants or type('C', (), { + 'FULL_HISTORY_BUFFER_LEN': 100, + 'FEATURE_LEN': 512, + 'DESIRE_LEN': 8, + 'PREV_DESIRED_CURV_LEN': 1, + 'INPUT_HISTORY_BUFFER_LEN': 25, + 'TEMPORAL_SKIP': 4, + })() + self.vision_input_names: list[str] = [] + shape = input_shapes.get('desire', (1, 0, 0)) # [batch, history, features] + if shape[1] == 25: + self.is_20hz = True + else: + self.is_20hz = False + + # Minimal prepare/run methods so ModelState can be run without actually running the model + def prepare_inputs(self, imgs_cl, numpy_inputs, frames): + return None + + def run_model(self): + return { + 'hidden_state': np.zeros((1, self.constants.FEATURE_LEN), dtype=np.float32), + 'desired_curvature': np.zeros((1, 1), dtype=np.float32), + } + + +@pytest.fixture +def shapes(request): + return request.param + + +@pytest.fixture +def bundle() -> DummyBundle: + return DummyBundle() + + +@pytest.fixture +def runner(shapes) -> DummyModelRunner: + return DummyModelRunner(shapes) + + +@pytest.fixture +def apply_patches(monkeypatch: pytest.MonkeyPatch, bundle: DummyBundle, runner: DummyModelRunner): + monkeypatch.setattr(helpers, 'get_active_bundle', lambda params=None: bundle, raising=False) + monkeypatch.setattr(runner_helpers, 'get_model_runner', lambda: runner, raising=False) + monkeypatch.setattr(modeld_module, 'get_model_runner', lambda: runner, raising=False) + monkeypatch.setattr(modeld_module, 'get_active_bundle', lambda params=None: bundle, raising=False) + + +# These are expected shapes and indices based on the time the model was presented +def get_expected_indices(shape, constants, mode, key=None): + if mode == 'split': + start = -1 - (constants.TEMPORAL_SKIP * (constants.INPUT_HISTORY_BUFFER_LEN - 1)) + arr = np.arange(constants.FULL_HISTORY_BUFFER_LEN) + idxs = arr[start::constants.TEMPORAL_SKIP] + return idxs + elif mode == '20hz': + num_elements = shape[1] + step_size = int(-100 / num_elements) + idxs = np.arange(step_size, step_size * (num_elements + 1), step_size)[::-1] + return idxs + elif mode == 'non20hz': + if key and shape[1] == constants.FULL_HISTORY_BUFFER_LEN: + return np.arange(constants.FULL_HISTORY_BUFFER_LEN) + return None + return None + + +@pytest.mark.parametrize("shapes,mode", SHAPE_MODE_PARAMS, indirect=["shapes"]) +def test_buffer_shapes_and_indices(shapes, mode, apply_patches): + state = ModelState(None) + constants = DummyModelRunner(shapes).constants + for key in shapes: + buf = state.temporal_buffers.get(key, None) + idxs = state.temporal_idxs_map.get(key, None) + # Buffer shape logic + if mode == 'split': + expected_shape = (1, constants.FULL_HISTORY_BUFFER_LEN, shapes[key][2]) + expected_idxs = get_expected_indices(shapes[key], constants, 'split', key) + elif mode == '20hz': + expected_shape = (1, constants.FULL_HISTORY_BUFFER_LEN, shapes[key][2]) + expected_idxs = get_expected_indices(shapes[key], constants, '20hz', key) + elif mode == 'non20hz': + if key == 'features_buffer': + expected_shape = (1, shapes[key][1]*4, shapes[key][2]) + else: + expected_shape = (1, shapes[key][1], shapes[key][2]) + expected_idxs = get_expected_indices(shapes[key], constants, 'non20hz', key) + + assert buf is not None, f"{key}: buffer not found" + assert buf.shape == expected_shape, f"{key}: buffer shape {buf.shape} != expected {expected_shape}" + if expected_idxs is not None: + assert np.all(idxs == expected_idxs), f"{key}: buffer idxs {idxs} != expected {expected_idxs}" + else: + assert idxs is None or idxs.size == 0, f"{key}: buffer idxs should be None or empty" + + +def legacy_buffer_update(buf, new_val, mode, key, constants, idxs): + # This is what we compare the new dynamic logic to, to ensure it does the same thing + if mode == 'split': + if key == 'desire': + buf[0,:-1] = buf[0,1:] + buf[0,-1] = new_val + return buf.reshape((1, constants.INPUT_HISTORY_BUFFER_LEN, constants.TEMPORAL_SKIP, -1)).max(axis=2) + elif key == 'features_buffer': + buf[0,:-1] = buf[0,1:] + buf[0,-1] = new_val + return buf[0, idxs] + elif key == 'prev_desired_curv': + buf[0,:-1] = buf[0,1:] + buf[0,-1,:] = new_val + return buf[0, idxs] + elif mode == '20hz': + if key == 'desire': + buf[:-1] = buf[1:] + buf[-1] = new_val + reshape_dims = (1, buf.shape[1], -1, buf.shape[2]) + reshaped = buf.reshape(reshape_dims).max(axis=2) + # Slice to last shape[1] elements to match model input shape + input_len = reshaped.shape[1] + model_input_len = 25 # For 20hz mode, desire shape[1] is 25 + if input_len > model_input_len: + reshaped = reshaped[:, -model_input_len:, :] + return reshaped + elif key == 'features_buffer': + buffer_history_len = buf.shape[1] + legacy_buf = np.zeros((buffer_history_len, buf.shape[2]), dtype=np.float32) + legacy_buf[:] = buf[0] + legacy_buf[:-1] = legacy_buf[1:] + legacy_buf[-1] = new_val + return legacy_buf[idxs] + elif key == 'prev_desired_curv': + buffer_history_len = buf.shape[1] + legacy_buf = np.zeros((buffer_history_len, buf.shape[2]), dtype=np.float32) + legacy_buf[:] = buf[0] + legacy_buf[:-1] = legacy_buf[1:] + legacy_buf[-1,:] = new_val + return legacy_buf[idxs] + elif mode == 'non20hz': + if key == 'desire': + length = new_val.shape[0] + buf[0,:-1,:length] = buf[0,1:,:length] + buf[0,-1,:length] = new_val[:length] + return buf[0] + elif key == 'features_buffer': + feature_len = new_val.shape[0] + buf[0,:-1,:feature_len] = buf[0,1:,:feature_len] + buf[0,-1,:feature_len] = new_val[:feature_len] + return buf[0] + elif key == 'prev_desired_curv': + length = new_val.shape[0] + buf[0,:-length,0] = buf[0,length:,0] + buf[0,-length:,0] = new_val[:length] + return buf[0] + return None + + +def dynamic_buffer_update(state, key, new_val, mode): + if key == 'desire': + state.temporal_buffers['desire'][0,:-1] = state.temporal_buffers['desire'][0,1:] + state.temporal_buffers['desire'][0,-1] = new_val + if state.temporal_buffers['desire'].shape[1] > state.numpy_inputs['desire'].shape[1]: + skip = state.temporal_buffers['desire'].shape[1] // state.numpy_inputs['desire'].shape[1] + return state.temporal_buffers['desire'][0].reshape( + state.numpy_inputs['desire'].shape[0], state.numpy_inputs['desire'].shape[1], skip, -1 + ).max(axis=2) + else: + return state.temporal_buffers['desire'][0, state.temporal_idxs_map['desire']] + + inputs = {'desire': np.zeros((1, state.constants.DESIRE_LEN), dtype=np.float32)} + for k, tb in state.temporal_buffers.items(): + if k in state.temporal_idxs_map: + continue + buf_len = tb.shape[1] + if k in state.numpy_inputs: + out_len = state.numpy_inputs[k].shape[1] + if out_len <= buf_len: + state.temporal_idxs_map[k] = np.arange(buf_len)[-out_len:] + else: + state.temporal_idxs_map[k] = np.arange(buf_len) + else: + state.temporal_idxs_map[k] = np.arange(buf_len) + + if key == 'features_buffer': + def run_model_stub(): + return { + 'hidden_state': np.asarray(new_val, dtype=np.float32).reshape(1, -1), + } + state.model_runner.run_model = run_model_stub + state.run({}, {}, inputs, prepare_only=False) + return state.numpy_inputs['features_buffer'][0] + + if key == 'prev_desired_curv': + def run_model_stub(): + return { + 'hidden_state': np.zeros((1, state.constants.FEATURE_LEN), dtype=np.float32), + 'desired_curvature': np.asarray(new_val, dtype=np.float32).reshape(1, -1), + } + state.model_runner.run_model = run_model_stub + state.run({}, {}, inputs, prepare_only=False) + return state.numpy_inputs['prev_desired_curv'][0] + return None + + +@pytest.mark.parametrize("shapes,mode", SHAPE_MODE_PARAMS, indirect=["shapes"]) +@pytest.mark.parametrize("key", ["desire", "features_buffer", "prev_desired_curv"]) +def test_buffer_update_equivalence(shapes, mode, key, apply_patches): + state = ModelState(None) + constants = DummyModelRunner(shapes).constants + buf = state.temporal_buffers.get(key, None) + idxs = state.temporal_idxs_map.get(key, None) + input_shape = shapes[key] + for step in range(20): # multiple steps to ensure history is built up + new_val = np.full((input_shape[2],), step, dtype=np.float32) + expected = legacy_buffer_update(buf, new_val, mode, key, constants, idxs) + actual = dynamic_buffer_update(state, key, new_val, mode) + # Model returns the reduced numpy_inputs history, compare the last n entries so the test is checking the same slices. + if expected is not None and actual is not None and expected.shape != actual.shape: + if expected.ndim == 2 and actual.ndim == 2 and expected.shape[1] == actual.shape[1]: + expected = expected[-actual.shape[0]:] + assert np.allclose(actual, expected), f"{mode} {key}: dynamic buffer update does not match legacy logic" diff --git a/sunnypilot/models/runners/tinygrad/tinygrad_runner.py b/sunnypilot/models/runners/tinygrad/tinygrad_runner.py index 270890cace..2800179fb2 100644 --- a/sunnypilot/models/runners/tinygrad/tinygrad_runner.py +++ b/sunnypilot/models/runners/tinygrad/tinygrad_runner.py @@ -83,7 +83,7 @@ class TinygradRunner(ModelRunner, SupercomboTinygrad, PolicyTinygrad, VisionTiny def _run_model(self) -> NumpyDict: """Runs the Tinygrad model inference and parses the outputs.""" - outputs = self.model_run(**self.inputs).numpy().flatten() + outputs = self.model_run(**self.inputs).contiguous().realize().uop.base.buffer.numpy() return self._parse_outputs(outputs) def _parse_outputs(self, model_outputs: np.ndarray) -> NumpyDict: