Merge branch 'upstream/openpilot/master' into sync-20251114

# Conflicts:
#	.github/workflows/ci_weekly_run.yaml
#	.github/workflows/raylib_ui_preview.yaml
#	.github/workflows/tests.yaml
#	.gitmodules
#	README.md
#	SConstruct
#	common/api.py
#	common/params_keys.h
#	docs/CARS.md
#	msgq_repo
#	opendbc_repo
#	panda
#	selfdrive/car/tests/test_car_interfaces.py
#	selfdrive/controls/controlsd.py
#	selfdrive/controls/lib/latcontrol.py
#	selfdrive/controls/lib/latcontrol_angle.py
#	selfdrive/controls/lib/latcontrol_pid.py
#	selfdrive/controls/lib/latcontrol_torque.py
#	selfdrive/controls/tests/test_latcontrol.py
#	selfdrive/monitoring/helpers.py
#	selfdrive/ui/SConscript
#	selfdrive/ui/main.cc
#	selfdrive/ui/qt/body.h
#	selfdrive/ui/qt/home.cc
#	selfdrive/ui/qt/home.h
#	selfdrive/ui/qt/network/networking.cc
#	selfdrive/ui/qt/network/networking.h
#	selfdrive/ui/qt/network/wifi_manager.cc
#	selfdrive/ui/qt/offroad/developer_panel.cc
#	selfdrive/ui/qt/offroad/developer_panel.h
#	selfdrive/ui/qt/offroad/experimental_mode.cc
#	selfdrive/ui/qt/offroad/firehose.cc
#	selfdrive/ui/qt/offroad/firehose.h
#	selfdrive/ui/qt/offroad/onboarding.cc
#	selfdrive/ui/qt/offroad/onboarding.h
#	selfdrive/ui/qt/offroad/settings.cc
#	selfdrive/ui/qt/offroad/settings.h
#	selfdrive/ui/qt/offroad/software_settings.cc
#	selfdrive/ui/qt/onroad/alerts.cc
#	selfdrive/ui/qt/onroad/annotated_camera.h
#	selfdrive/ui/qt/onroad/buttons.cc
#	selfdrive/ui/qt/onroad/buttons.h
#	selfdrive/ui/qt/onroad/driver_monitoring.cc
#	selfdrive/ui/qt/onroad/hud.cc
#	selfdrive/ui/qt/onroad/hud.h
#	selfdrive/ui/qt/onroad/model.cc
#	selfdrive/ui/qt/onroad/model.h
#	selfdrive/ui/qt/onroad/onroad_home.cc
#	selfdrive/ui/qt/onroad/onroad_home.h
#	selfdrive/ui/qt/request_repeater.h
#	selfdrive/ui/qt/sidebar.cc
#	selfdrive/ui/qt/sidebar.h
#	selfdrive/ui/qt/util.cc
#	selfdrive/ui/qt/widgets/cameraview.h
#	selfdrive/ui/qt/widgets/controls.cc
#	selfdrive/ui/qt/widgets/controls.h
#	selfdrive/ui/qt/widgets/input.cc
#	selfdrive/ui/qt/widgets/input.h
#	selfdrive/ui/qt/widgets/prime.cc
#	selfdrive/ui/qt/widgets/prime.h
#	selfdrive/ui/qt/widgets/ssh_keys.h
#	selfdrive/ui/qt/widgets/toggle.h
#	selfdrive/ui/qt/widgets/wifi.cc
#	selfdrive/ui/qt/widgets/wifi.h
#	selfdrive/ui/qt/window.cc
#	selfdrive/ui/qt/window.h
#	selfdrive/ui/tests/cycle_offroad_alerts.py
#	selfdrive/ui/tests/test_ui/run.py
#	selfdrive/ui/translations/main_ar.ts
#	selfdrive/ui/translations/main_de.ts
#	selfdrive/ui/translations/main_es.ts
#	selfdrive/ui/translations/main_fr.ts
#	selfdrive/ui/translations/main_ja.ts
#	selfdrive/ui/translations/main_ko.ts
#	selfdrive/ui/translations/main_nl.ts
#	selfdrive/ui/translations/main_pl.ts
#	selfdrive/ui/translations/main_pt-BR.ts
#	selfdrive/ui/translations/main_th.ts
#	selfdrive/ui/translations/main_tr.ts
#	selfdrive/ui/translations/main_zh-CHS.ts
#	selfdrive/ui/translations/main_zh-CHT.ts
#	selfdrive/ui/ui.cc
#	selfdrive/ui/ui.h
#	system/manager/build.py
#	system/version.py
This commit is contained in:
Jason Wen
2025-11-16 02:00:29 -05:00
496 changed files with 22367 additions and 59319 deletions
+3 -9
View File
@@ -4,18 +4,12 @@ common_libs = [
'params.cc',
'swaglog.cc',
'util.cc',
'watchdog.cc',
'ratekeeper.cc'
]
_common = env.Library('common', common_libs, LIBS="json11")
files = [
'ratekeeper.cc',
'clutil.cc',
]
_gpucommon = env.Library('gpucommon', files)
Export('_common', '_gpucommon')
_common = env.Library('common', common_libs, LIBS="json11")
Export('_common')
if GetOption('extras'):
env.Program('tests/test_common',
+6 -2
View File
@@ -14,9 +14,13 @@ class Api:
def post(self, *args, **kwargs):
return self.service.post(*args, **kwargs)
def get_token(self, expiry_hours=1):
return self.service.get_token(expiry_hours)
def get_token(self, payload_extra=None, expiry_hours=1):
return self.service.get_token(payload_extra, expiry_hours)
def api_get(endpoint, method='GET', timeout=None, access_token=None, **params):
return CommaConnectApi(None).api_get(endpoint, method, timeout, access_token, **params)
def get_key_pair():
return CommaConnectApi(None).get_key_pair()
+20 -6
View File
@@ -1,18 +1,22 @@
import jwt
import os
import requests
import unicodedata
from datetime import datetime, timedelta, UTC
from openpilot.system.hardware.hw import Paths
from openpilot.system.version import get_version
# name : jwt signature algorithm
KEYS = {"id_rsa" : "RS256",
"id_ecdsa" : "ES256"}
class BaseApi:
def __init__(self, dongle_id, api_host, user_agent="openpilot-"):
self.dongle_id = dongle_id
self.api_host = api_host
self.user_agent = user_agent
with open(f'{Paths.persist_root()}/comma/id_rsa') as f:
self.private_key = f.read()
self.jwt_algorithm, self.private_key, _ = self.get_key_pair()
def get(self, *args, **kwargs):
return self.request('GET', *args, **kwargs)
@@ -23,7 +27,7 @@ class BaseApi:
def request(self, method, endpoint, timeout=None, access_token=None, **params):
return self.api_get(endpoint, method=method, timeout=timeout, access_token=access_token, **params)
def _get_token(self, expiry_hours=1, **extra_payload):
def _get_token(self, payload_extra=None, expiry_hours=1, **extra_payload):
now = datetime.now(UTC).replace(tzinfo=None)
payload = {
'identity': self.dongle_id,
@@ -32,13 +36,15 @@ class BaseApi:
'exp': now + timedelta(hours=expiry_hours),
**extra_payload
}
token = jwt.encode(payload, self.private_key, algorithm='RS256')
if payload_extra is not None:
payload.update(payload_extra)
token = jwt.encode(payload, self.private_key, algorithm=self.jwt_algorithm)
if isinstance(token, bytes):
token = token.decode('utf8')
return token
def get_token(self, expiry_hours=1):
return self._get_token(expiry_hours)
def get_token(self, payload_extra=None, expiry_hours=1):
return self._get_token(payload_extra, expiry_hours)
def remove_non_ascii_chars(self, text):
normalized_text = unicodedata.normalize('NFD', text)
@@ -54,3 +60,11 @@ class BaseApi:
headers['User-Agent'] = self.user_agent + version
return requests.request(method, f"{self.api_host}/{endpoint}", timeout=timeout, headers=headers, json=json, params=params)
@staticmethod
def get_key_pair():
for key in KEYS:
if os.path.isfile(Paths.persist_root() + f'/comma/{key}') and os.path.isfile(Paths.persist_root() + f'/comma/{key}.pub'):
with open(Paths.persist_root() + f'/comma/{key}') as private, open(Paths.persist_root() + f'/comma/{key}.pub') as public:
return KEYS[key], private.read(), public.read()
return None, None, None
-9
View File
@@ -1,9 +0,0 @@
# remove all keys that end in DEPRECATED
def strip_deprecated_keys(d):
for k in list(d.keys()):
if isinstance(k, str):
if k.endswith('DEPRECATED'):
d.pop(k)
elif isinstance(d[k], dict):
strip_deprecated_keys(d[k])
return d
+1 -1
View File
@@ -1,6 +1,6 @@
from functools import cache
import subprocess
from openpilot.common.run import run_cmd, run_cmd_default
from openpilot.common.utils import run_cmd, run_cmd_default
@cache
+1 -1
View File
@@ -1 +1 @@
#define DEFAULT_MODEL "Firehose (Default)"
#define DEFAULT_MODEL "The Cool People (Default)"
+3 -1
View File
@@ -66,7 +66,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"IsTakingSnapshot", {CLEAR_ON_MANAGER_START, BOOL}},
{"IsTestedBranch", {CLEAR_ON_MANAGER_START, BOOL}},
{"JoystickDebugMode", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
{"LanguageSetting", {PERSISTENT | BACKUP, STRING, "main_en"}},
{"LanguageSetting", {PERSISTENT | BACKUP, STRING, "en"}},
{"LastAthenaPingTime", {CLEAR_ON_MANAGER_START, INT}},
{"LastGPSPosition", {PERSISTENT, STRING}},
{"LastManagerExitReason", {CLEAR_ON_MANAGER_START, STRING}},
@@ -97,6 +97,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"Offroad_TemperatureTooHigh", {CLEAR_ON_MANAGER_START, JSON}},
{"Offroad_UnregisteredHardware", {CLEAR_ON_MANAGER_START, JSON}},
{"Offroad_UpdateFailed", {CLEAR_ON_MANAGER_START, JSON}},
{"Offroad_DriverMonitoringUncertain", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}},
{"OnroadCycleRequested", {CLEAR_ON_MANAGER_START, BOOL}},
{"OpenpilotEnabledToggle", {PERSISTENT | BACKUP, BOOL, "1"}},
{"PandaHeartbeatLost", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
@@ -108,6 +109,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"RecordFront", {PERSISTENT | BACKUP, BOOL}},
{"RecordFrontLock", {PERSISTENT, BOOL}}, // for the internal fleet
{"SecOCKey", {PERSISTENT | DONT_LOG | BACKUP, STRING}},
{"ShowDebugInfo", {PERSISTENT, BOOL}},
{"RouteCount", {PERSISTENT, INT, "0"}},
{"SnoozeUpdate", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
{"SshEnabled", {PERSISTENT | BACKUP, BOOL}},
+6 -7
View File
@@ -2,11 +2,10 @@ import numpy as np
from numbers import Number
class PIDController:
def __init__(self, k_p, k_i, k_f=0., k_d=0., pos_limit=1e308, neg_limit=-1e308, rate=100):
def __init__(self, k_p, k_i, k_d=0., pos_limit=1e308, neg_limit=-1e308, rate=100):
self._k_p = k_p
self._k_i = k_i
self._k_d = k_d
self.k_f = k_f # feedforward gain
if isinstance(self._k_p, Number):
self._k_p = [[0], [self._k_p]]
if isinstance(self._k_i, Number):
@@ -16,7 +15,7 @@ class PIDController:
self.set_limits(pos_limit, neg_limit)
self.i_rate = 1.0 / rate
self.i_dt = 1.0 / rate
self.speed = 0.0
self.reset()
@@ -46,12 +45,12 @@ class PIDController:
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
self.f = feedforward * self.k_f
self.d = error_rate * self.k_d
self.p = self.k_p * float(error)
self.d = self.k_d * error_rate
self.f = feedforward
if not freeze_integrator:
i = self.i + error * self.k_i * self.i_rate
i = self.i + self.k_i * self.i_dt * error
# Don't allow windup if already clipping
test_control = self.p + i + self.d + self.f
-30
View File
@@ -1,30 +0,0 @@
import time
import functools
from openpilot.common.swaglog import cloudlog
def retry(attempts=3, delay=1.0, ignore_failure=False):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for _ in range(attempts):
try:
return func(*args, **kwargs)
except Exception:
cloudlog.exception(f"{func.__name__} failed, trying again")
time.sleep(delay)
if ignore_failure:
cloudlog.error(f"{func.__name__} failed after retry")
else:
raise Exception(f"{func.__name__} failed after retry")
return wrapper
return decorator
if __name__ == "__main__":
@retry(attempts=10)
def abc():
raise ValueError("abc failed :(")
abc()
-28
View File
@@ -1,28 +0,0 @@
import subprocess
from contextlib import contextmanager
from subprocess import Popen, PIPE, TimeoutExpired
def run_cmd(cmd: list[str], cwd=None, env=None) -> str:
return subprocess.check_output(cmd, encoding='utf8', cwd=cwd, env=env).strip()
def run_cmd_default(cmd: list[str], default: str = "", cwd=None, env=None) -> str:
try:
return run_cmd(cmd, cwd=cwd, env=env)
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()
+1 -1
View File
@@ -1,7 +1,7 @@
import os
from uuid import uuid4
from openpilot.common.file_helpers import atomic_write_in_dir
from openpilot.common.utils import atomic_write_in_dir
class TestFileHelpers:
+62 -2
View File
@@ -2,9 +2,14 @@ import io
import os
import tempfile
import contextlib
import subprocess
import time
import functools
from subprocess import Popen, PIPE, TimeoutExpired
import zstandard as zstd
from openpilot.common.swaglog import cloudlog
LOG_COMPRESSION_LEVEL = 10 # little benefit up to level 15. level ~17 is a small step change
LOG_COMPRESSION_LEVEL = 10 # little benefit up to level 15. level ~17 is a small step change
class CallbackReader:
@@ -27,7 +32,7 @@ class CallbackReader:
@contextlib.contextmanager
def atomic_write_in_dir(path: str, mode: str = 'w', buffering: int = -1, encoding: str = None, newline: str = None,
def atomic_write_in_dir(path: str, mode: str = 'w', buffering: int = -1, encoding: str | None = None, newline: str | None = None,
overwrite: bool = False):
"""Write to a file atomically using a temporary file in the same directory as the destination file."""
dir_name = os.path.dirname(path)
@@ -56,3 +61,58 @@ def get_upload_stream(filepath: str, should_compress: bool) -> tuple[io.Buffered
compressed_size = compressed_stream.tell()
compressed_stream.seek(0)
return compressed_stream, compressed_size
# remove all keys that end in DEPRECATED
def strip_deprecated_keys(d):
for k in list(d.keys()):
if isinstance(k, str):
if k.endswith('DEPRECATED'):
d.pop(k)
elif isinstance(d[k], dict):
strip_deprecated_keys(d[k])
return d
def run_cmd(cmd: list[str], cwd=None, env=None) -> str:
return subprocess.check_output(cmd, encoding='utf8', cwd=cwd, env=env).strip()
def run_cmd_default(cmd: list[str], default: str = "", cwd=None, env=None) -> str:
try:
return run_cmd(cmd, cwd=cwd, env=env)
except subprocess.CalledProcessError:
return default
@contextlib.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()
def retry(attempts=3, delay=1.0, ignore_failure=False):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for _ in range(attempts):
try:
return func(*args, **kwargs)
except Exception:
cloudlog.exception(f"{func.__name__} failed, trying again")
time.sleep(delay)
if ignore_failure:
cloudlog.error(f"{func.__name__} failed after retry")
else:
raise Exception(f"{func.__name__} failed after retry")
return wrapper
return decorator
+1 -1
View File
@@ -1 +1 @@
#define COMMA_VERSION "0.10.1"
#define COMMA_VERSION "0.10.2"
-12
View File
@@ -1,12 +0,0 @@
#include <string>
#include "common/watchdog.h"
#include "common/util.h"
#include "system/hardware/hw.h"
const std::string watchdog_fn_prefix = Path::shm_path() + "/wd_"; // + <pid>
bool watchdog_kick(uint64_t ts) {
static std::string fn = watchdog_fn_prefix + std::to_string(getpid());
return util::write_file(fn.c_str(), &ts, sizeof(ts), O_WRONLY | O_CREAT) > 0;
}
-5
View File
@@ -1,5 +0,0 @@
#pragma once
#include <cstdint>
bool watchdog_kick(uint64_t ts);
-22
View File
@@ -1,22 +0,0 @@
import os
import time
import struct
from openpilot.system.hardware.hw import Paths
WATCHDOG_FN = f"{Paths.shm_path()}/wd_"
_LAST_KICK = 0.0
def kick_watchdog():
global _LAST_KICK
current_time = time.monotonic()
if current_time - _LAST_KICK < 1.0:
return
try:
with open(f"{WATCHDOG_FN}{os.getpid()}", 'wb') as f:
f.write(struct.pack('<Q', int(current_time * 1e9)))
f.flush()
_LAST_KICK = current_time
except OSError:
pass