diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index 8b1f256bfb..d1dff147f2 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -117,7 +117,7 @@ jobs: - name: Merge x64 and arm64 tags run: | export PUSH_IMAGE=true - selfdrive/test/docker_tag_multiarch.sh base x86_64 aarch64 + scripts/retry.sh selfdrive/test/docker_tag_multiarch.sh base x86_64 aarch64 static_analysis: name: static analysis diff --git a/.gitignore b/.gitignore index 3e91531d08..3da75aaea4 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,6 @@ venv/ .overlay_init .overlay_consistent .sconsign.dblite -.vscode* model2.png a.out .hypothesis diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f98bca6060..6db68e55bc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,7 +10,7 @@ repos: - id: check-ast exclude: '^(third_party)/' - id: check-json - exclude: '.devcontainer/devcontainer.json' # this supports JSON with comments + exclude: '.devcontainer/devcontainer.json|.vscode/' # these support JSON with comments - id: check-toml - id: check-xml - id: check-yaml @@ -32,6 +32,11 @@ repos: # if you've got a short variable name that's getting flagged, add it here - -L bu,ro,te,ue,alo,hda,ois,nam,nams,ned,som,parm,setts,inout,warmup,bumb,nd,sie,preints - --builtins clear,rare,informal,usage,code,names,en-GB_to_en-US +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.2.2 + hooks: + - id: ruff + exclude: '^(third_party/)|(cereal/)|(panda/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(teleoprtc/)|(teleoprtc_repo/)' - repo: local hooks: - id: mypy @@ -43,11 +48,6 @@ repos: - --local-partial-types - --explicit-package-bases exclude: '^(third_party/)|(cereal/)|(opendbc/)|(panda/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(teleoprtc/)|(teleoprtc_repo/)|(xx/)' -- repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.2.2 - hooks: - - id: ruff - exclude: '^(third_party/)|(cereal/)|(panda/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(teleoprtc/)|(teleoprtc_repo/)' - repo: local hooks: - id: cppcheck @@ -91,7 +91,7 @@ repos: pass_filenames: false files: 'selfdrive/ui/translations/*' - repo: https://github.com/python-poetry/poetry - rev: '1.7.0' + rev: '1.8.0' hooks: - id: poetry-check name: validate poetry lock diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000000..458312fc88 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "ms-python.python", + "ms-vscode.cpptools", + "elagil.pre-commit-helper", + "charliermarsh.ruff", + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000..3b3953c3f4 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,47 @@ +{ + "version": "0.2.0", + "inputs": [ + { + "id": "python_process", + "type": "pickString", + "description": "Select the process to debug", + "options": [ + "selfdrive/controls/controlsd.py", + "selfdrive/navd/navd.py", + "system/timed/timed.py", + "tools/sim/run_bridge.py" + ] + }, + { + "id": "cpp_process", + "type": "pickString", + "description": "Select the process to debug", + "options": [ + "selfdrive/ui/ui" + ] + }, + { + "id": "args", + "description": "Arguments to pass to the process", + "type": "promptString" + } + ], + "configurations": [ + { + "name": "Python: openpilot Process", + "type": "debugpy", + "request": "launch", + "program": "${input:python_process}", + "console": "integratedTerminal", + "justMyCode": true, + "args": "${input:args}" + }, + { + "name": "C++: openpilot Process", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/${input:cpp_process}", + "cwd": "${workspaceFolder}", + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..daf74ca777 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,16 @@ +{ + "editor.tabSize": 2, + "editor.insertSpaces": true, + "editor.renderWhitespace": "trailing", + "files.trimTrailingWhitespace": true, + "search.exclude": { + "**/.git": true, + "**/.venv": true, + "**/__pycache__": true + }, + "files.exclude": { + "**/.git": true, + "**/.venv": true, + "**/__pycache__": true + } +} \ No newline at end of file diff --git a/RELEASES.md b/RELEASES.md index 096fa691ac..d0fd530db1 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,6 +1,7 @@ Version 0.9.7 (2024-XX-XX) ======================== * New driving model +* Support for many hybrid Ford models Version 0.9.6 (2024-02-27) ======================== diff --git a/SConstruct b/SConstruct index 9dab1f4764..9c8c0f8be4 100644 --- a/SConstruct +++ b/SConstruct @@ -96,8 +96,6 @@ lenv = { rpath = lenv["LD_LIBRARY_PATH"].copy() if arch == "larch64": - lenv["LD_LIBRARY_PATH"] += ['/data/data/com.termux/files/usr/lib'] - cpppath = [ "#third_party/opencl/include", ] @@ -361,13 +359,6 @@ Export('common', 'gpucommon') # Build cereal and messaging SConscript(['cereal/SConscript']) -cereal = [File('#cereal/libcereal.a')] -messaging = [File('#cereal/libmessaging.a')] -visionipc = [File('#cereal/libvisionipc.a')] -messaging_python = [File('#cereal/messaging/messaging_pyx.so')] - -Export('cereal', 'messaging', 'messaging_python', 'visionipc') - # Build other submodules SConscript([ 'body/board/SConscript', diff --git a/cereal b/cereal index b891fcca7a..d4c43a0865 160000 --- a/cereal +++ b/cereal @@ -1 +1 @@ -Subproject commit b891fcca7abc7a25e7322afa8b4ab381d695564c +Subproject commit d4c43a0865fe2d9c7c765f62e31233c9a7d33ebc diff --git a/common/file_helpers.py b/common/file_helpers.py index dea298a529..29ad219c07 100644 --- a/common/file_helpers.py +++ b/common/file_helpers.py @@ -1,7 +1,6 @@ import os import tempfile import contextlib -from typing import Optional class CallbackReader: @@ -24,7 +23,7 @@ class CallbackReader: @contextlib.contextmanager -def atomic_write_in_dir(path: str, mode: str = 'w', buffering: int = -1, encoding: Optional[str] = None, newline: Optional[str] = None, +def atomic_write_in_dir(path: str, mode: str = 'w', buffering: int = -1, encoding: str = None, newline: str = 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) diff --git a/common/gpio.py b/common/gpio.py index 88a9479a60..68932cb87a 100644 --- a/common/gpio.py +++ b/common/gpio.py @@ -1,6 +1,5 @@ import os from functools import lru_cache -from typing import Optional, List def gpio_init(pin: int, output: bool) -> None: try: @@ -16,7 +15,7 @@ def gpio_set(pin: int, high: bool) -> None: except Exception as e: print(f"Failed to set gpio {pin} value: {e}") -def gpio_read(pin: int) -> Optional[bool]: +def gpio_read(pin: int) -> bool | None: val = None try: with open(f"/sys/class/gpio/gpio{pin}/value", 'rb') as f: @@ -37,7 +36,7 @@ def gpio_export(pin: int) -> None: print(f"Failed to export gpio {pin}") @lru_cache(maxsize=None) -def get_irq_action(irq: int) -> List[str]: +def get_irq_action(irq: int) -> list[str]: try: with open(f"/sys/kernel/irq/{irq}/actions") as f: actions = f.read().strip().split(',') @@ -45,7 +44,7 @@ def get_irq_action(irq: int) -> List[str]: except FileNotFoundError: return [] -def get_irqs_for_action(action: str) -> List[str]: +def get_irqs_for_action(action: str) -> list[str]: ret = [] with open("/proc/interrupts") as f: for l in f.readlines(): diff --git a/common/mock/__init__.py b/common/mock/__init__.py index e0dbf45c74..8c86bbd394 100644 --- a/common/mock/__init__.py +++ b/common/mock/__init__.py @@ -6,7 +6,6 @@ example in common/tests/test_mock.py import functools import threading -from typing import List, Union from cereal.messaging import PubMaster from cereal.services import SERVICE_LIST from openpilot.common.mock.generators import generate_liveLocationKalman @@ -18,7 +17,7 @@ MOCK_GENERATOR = { } -def generate_messages_loop(services: List[str], done: threading.Event): +def generate_messages_loop(services: list[str], done: threading.Event): pm = PubMaster(services) rk = Ratekeeper(100) i = 0 @@ -32,7 +31,7 @@ def generate_messages_loop(services: List[str], done: threading.Event): rk.keep_time() -def mock_messages(services: Union[List[str], str]): +def mock_messages(services: list[str] | str): if isinstance(services, str): services = [services] diff --git a/common/params.cc b/common/params.cc index 1da7e20d14..195f4e0a11 100644 --- a/common/params.cc +++ b/common/params.cc @@ -209,7 +209,6 @@ std::unordered_map keys = { {"UpdaterLastFetchTime", PERSISTENT}, {"Version", PERSISTENT}, {"VisionRadarToggle", PERSISTENT}, - {"WheeledBody", PERSISTENT}, {"AccMadsCombo", PERSISTENT}, {"AmapKey1", PERSISTENT}, diff --git a/common/prefix.py b/common/prefix.py index d027e3e5a1..4059ac09e2 100644 --- a/common/prefix.py +++ b/common/prefix.py @@ -2,14 +2,13 @@ import os import shutil import uuid -from typing import Optional from openpilot.common.params import Params from openpilot.system.hardware.hw import Paths from openpilot.system.hardware.hw import DEFAULT_DOWNLOAD_CACHE_ROOT class OpenpilotPrefix: - def __init__(self, prefix: Optional[str] = None, clean_dirs_on_exit: bool = True, shared_download_cache: bool = False): + def __init__(self, prefix: str = None, clean_dirs_on_exit: bool = True, shared_download_cache: bool = False): self.prefix = prefix if prefix else str(uuid.uuid4().hex[0:15]) self.msgq_path = os.path.join('/dev/shm', self.prefix) self.clean_dirs_on_exit = clean_dirs_on_exit diff --git a/common/realtime.py b/common/realtime.py index d9168b09bd..49a38a8ff5 100644 --- a/common/realtime.py +++ b/common/realtime.py @@ -5,7 +5,6 @@ import time import threading import psutil from collections import deque -from typing import Optional, List, Union from setproctitle import getproctitle @@ -35,12 +34,12 @@ def set_realtime_priority(level: int) -> None: os.sched_setscheduler(0, os.SCHED_FIFO, os.sched_param(level)) -def set_core_affinity(cores: List[int]) -> None: +def set_core_affinity(cores: list[int]) -> None: if not PC: os.sched_setaffinity(0, cores) -def config_realtime_process(cores: Union[int, List[int]], priority: int) -> None: +def config_realtime_process(cores: int | list[int], priority: int) -> None: gc.disable() set_realtime_priority(priority) c = cores if isinstance(cores, list) else [cores, ] @@ -56,7 +55,7 @@ def set_thread_affinity(thread: threading.Thread, cores: List[int]) -> None: class Ratekeeper: - def __init__(self, rate: float, print_delay_threshold: Optional[float] = 0.0) -> None: + def __init__(self, rate: float, print_delay_threshold: float | None = 0.0) -> None: """Rate in Hz for ratekeeping. print_delay_threshold must be nonnegative.""" self._interval = 1. / rate self._next_frame_time = time.monotonic() + self._interval diff --git a/common/transformations/orientation.py b/common/transformations/orientation.py index ce4378738d..86e6a6c347 100644 --- a/common/transformations/orientation.py +++ b/common/transformations/orientation.py @@ -1,5 +1,5 @@ import numpy as np -from typing import Callable +from collections.abc import Callable from openpilot.common.transformations.transformations import (ecef_euler_from_ned_single, euler2quat_single, diff --git a/docs/CARS.md b/docs/CARS.md index 6d3ee71659..7ad12189cb 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. -# 279 Supported Cars +# 288 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Hardware Needed
 |Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| @@ -37,11 +37,19 @@ A supported vehicle is one that just works when you install a comma device. All |Dodge|Durango 2020-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Bronco Sport 2021-22|Co-Pilot360 Assist+|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Escape 2020-22|Co-Pilot360 Assist+|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Ford|Escape Hybrid 2020-22|Co-Pilot360 Assist+|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Ford|Escape Plug-in Hybrid 2020-22|Co-Pilot360 Assist+|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Explorer 2020-23|Co-Pilot360 Assist+|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Ford|Explorer Hybrid 2020-23|Co-Pilot360 Assist+|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Focus 2018[3](#footnotes)|Adaptive Cruise Control with Lane Centering|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Ford|Focus Hybrid 2018[3](#footnotes)|Adaptive Cruise Control with Lane Centering|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Kuga 2020-22|Adaptive Cruise Control with Lane Centering|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Ford|Kuga Hybrid 2020-22|Adaptive Cruise Control with Lane Centering|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Ford|Kuga Plug-in Hybrid 2020-22|Adaptive Cruise Control with Lane Centering|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Maverick 2022|LARIAT Luxury|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Maverick 2023|Co-Pilot360 Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Ford|Maverick Hybrid 2022|LARIAT Luxury|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Ford|Maverick Hybrid 2023|Co-Pilot360 Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Genesis|G70 2018-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai F connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Genesis|G70 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai F connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Genesis|G80 2017|All|Stock|19 mph|37 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai J connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -177,7 +185,8 @@ A supported vehicle is one that just works when you install a comma device. All |Lexus|RX Hybrid 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 RJ45 cable (7 ft)
- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Lexus|RX Hybrid 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 RJ45 cable (7 ft)
- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Lexus|UX Hybrid 2019-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 RJ45 cable (7 ft)
- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Lincoln|Aviator 2020-21|Co-Pilot360 Plus|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Lincoln|Aviator 2020-23|Co-Pilot360 Plus|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Lincoln|Aviator Plug-in Hybrid 2020-23|Co-Pilot360 Plus|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |MAN|eTGE 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 J533 connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |MAN|TGE 2017-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 J533 connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Mazda|CX-5 2022-24|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Mazda connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 30f4e0dfd3..755ca82220 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -62,4 +62,4 @@ A good pull request has all of the following: * Consider opting into driver camera uploads to improve the driver monitoring model. * Connect your device to Wi-Fi regularly, so that we can pull data for training better driving models. * Run the `nightly` branch and report issues. This branch is like `master` but it's built just like a release. -* Annotate images in the [comma10k dateset](https://github.com/commaai/comma10k). +* Annotate images in the [comma10k dataset](https://github.com/commaai/comma10k). diff --git a/launch_env.sh b/launch_env.sh index 0c72c54581..6859afb0d4 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="9.6" + export AGNOS_VERSION="9.7" fi export STAGING_ROOT="/data/safe_staging" diff --git a/opendbc b/opendbc index e8e8ed7222..ecffe0cc98 160000 --- a/opendbc +++ b/opendbc @@ -1 +1 @@ -Subproject commit e8e8ed72221e161cc2952ce09deb776673ffbff6 +Subproject commit ecffe0cc9881d7db1dc87a1569a3534816bd7db5 diff --git a/openpilot/__init__.py b/openpilot/__init__.py index b28b04f643..e69de29bb2 100644 --- a/openpilot/__init__.py +++ b/openpilot/__init__.py @@ -1,3 +0,0 @@ - - - diff --git a/panda b/panda index ec91ad7f06..87391d0067 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit ec91ad7f061f798786c082ad82006c76d6cabfef +Subproject commit 87391d0067bef069302f6da9a2c1200b16124e6a diff --git a/poetry.lock b/poetry.lock index 9972cd7732..588186d138 100644 --- a/poetry.lock +++ b/poetry.lock @@ -751,63 +751,63 @@ test = ["pytest", "pytest-timeout"] [[package]] name = "coverage" -version = "7.4.1" +version = "7.4.3" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:077d366e724f24fc02dbfe9d946534357fda71af9764ff99d73c3c596001bbd7"}, - {file = "coverage-7.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0193657651f5399d433c92f8ae264aff31fc1d066deee4b831549526433f3f61"}, - {file = "coverage-7.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d17bbc946f52ca67adf72a5ee783cd7cd3477f8f8796f59b4974a9b59cacc9ee"}, - {file = "coverage-7.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3277f5fa7483c927fe3a7b017b39351610265308f5267ac6d4c2b64cc1d8d25"}, - {file = "coverage-7.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dceb61d40cbfcf45f51e59933c784a50846dc03211054bd76b421a713dcdf19"}, - {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6008adeca04a445ea6ef31b2cbaf1d01d02986047606f7da266629afee982630"}, - {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c61f66d93d712f6e03369b6a7769233bfda880b12f417eefdd4f16d1deb2fc4c"}, - {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b9bb62fac84d5f2ff523304e59e5c439955fb3b7f44e3d7b2085184db74d733b"}, - {file = "coverage-7.4.1-cp310-cp310-win32.whl", hash = "sha256:f86f368e1c7ce897bf2457b9eb61169a44e2ef797099fb5728482b8d69f3f016"}, - {file = "coverage-7.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:869b5046d41abfea3e381dd143407b0d29b8282a904a19cb908fa24d090cc018"}, - {file = "coverage-7.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b8ffb498a83d7e0305968289441914154fb0ef5d8b3157df02a90c6695978295"}, - {file = "coverage-7.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3cacfaefe6089d477264001f90f55b7881ba615953414999c46cc9713ff93c8c"}, - {file = "coverage-7.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d6850e6e36e332d5511a48a251790ddc545e16e8beaf046c03985c69ccb2676"}, - {file = "coverage-7.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18e961aa13b6d47f758cc5879383d27b5b3f3dcd9ce8cdbfdc2571fe86feb4dd"}, - {file = "coverage-7.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfd1e1b9f0898817babf840b77ce9fe655ecbe8b1b327983df485b30df8cc011"}, - {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6b00e21f86598b6330f0019b40fb397e705135040dbedc2ca9a93c7441178e74"}, - {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:536d609c6963c50055bab766d9951b6c394759190d03311f3e9fcf194ca909e1"}, - {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7ac8f8eb153724f84885a1374999b7e45734bf93a87d8df1e7ce2146860edef6"}, - {file = "coverage-7.4.1-cp311-cp311-win32.whl", hash = "sha256:f3771b23bb3675a06f5d885c3630b1d01ea6cac9e84a01aaf5508706dba546c5"}, - {file = "coverage-7.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:9d2f9d4cc2a53b38cabc2d6d80f7f9b7e3da26b2f53d48f05876fef7956b6968"}, - {file = "coverage-7.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f68ef3660677e6624c8cace943e4765545f8191313a07288a53d3da188bd8581"}, - {file = "coverage-7.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23b27b8a698e749b61809fb637eb98ebf0e505710ec46a8aa6f1be7dc0dc43a6"}, - {file = "coverage-7.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e3424c554391dc9ef4a92ad28665756566a28fecf47308f91841f6c49288e66"}, - {file = "coverage-7.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0860a348bf7004c812c8368d1fc7f77fe8e4c095d661a579196a9533778e156"}, - {file = "coverage-7.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe558371c1bdf3b8fa03e097c523fb9645b8730399c14fe7721ee9c9e2a545d3"}, - {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3468cc8720402af37b6c6e7e2a9cdb9f6c16c728638a2ebc768ba1ef6f26c3a1"}, - {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:02f2edb575d62172aa28fe00efe821ae31f25dc3d589055b3fb64d51e52e4ab1"}, - {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ca6e61dc52f601d1d224526360cdeab0d0712ec104a2ce6cc5ccef6ed9a233bc"}, - {file = "coverage-7.4.1-cp312-cp312-win32.whl", hash = "sha256:ca7b26a5e456a843b9b6683eada193fc1f65c761b3a473941efe5a291f604c74"}, - {file = "coverage-7.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:85ccc5fa54c2ed64bd91ed3b4a627b9cce04646a659512a051fa82a92c04a448"}, - {file = "coverage-7.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8bdb0285a0202888d19ec6b6d23d5990410decb932b709f2b0dfe216d031d218"}, - {file = "coverage-7.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:918440dea04521f499721c039863ef95433314b1db00ff826a02580c1f503e45"}, - {file = "coverage-7.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:379d4c7abad5afbe9d88cc31ea8ca262296480a86af945b08214eb1a556a3e4d"}, - {file = "coverage-7.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b094116f0b6155e36a304ff912f89bbb5067157aff5f94060ff20bbabdc8da06"}, - {file = "coverage-7.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2f5968608b1fe2a1d00d01ad1017ee27efd99b3437e08b83ded9b7af3f6f766"}, - {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:10e88e7f41e6197ea0429ae18f21ff521d4f4490aa33048f6c6f94c6045a6a75"}, - {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a4a3907011d39dbc3e37bdc5df0a8c93853c369039b59efa33a7b6669de04c60"}, - {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d224f0c4c9c98290a6990259073f496fcec1b5cc613eecbd22786d398ded3ad"}, - {file = "coverage-7.4.1-cp38-cp38-win32.whl", hash = "sha256:23f5881362dcb0e1a92b84b3c2809bdc90db892332daab81ad8f642d8ed55042"}, - {file = "coverage-7.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:a07f61fc452c43cd5328b392e52555f7d1952400a1ad09086c4a8addccbd138d"}, - {file = "coverage-7.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8e738a492b6221f8dcf281b67129510835461132b03024830ac0e554311a5c54"}, - {file = "coverage-7.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46342fed0fff72efcda77040b14728049200cbba1279e0bf1188f1f2078c1d70"}, - {file = "coverage-7.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9641e21670c68c7e57d2053ddf6c443e4f0a6e18e547e86af3fad0795414a628"}, - {file = "coverage-7.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aeb2c2688ed93b027eb0d26aa188ada34acb22dceea256d76390eea135083950"}, - {file = "coverage-7.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d12c923757de24e4e2110cf8832d83a886a4cf215c6e61ed506006872b43a6d1"}, - {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0491275c3b9971cdbd28a4595c2cb5838f08036bca31765bad5e17edf900b2c7"}, - {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8dfc5e195bbef80aabd81596ef52a1277ee7143fe419efc3c4d8ba2754671756"}, - {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1a78b656a4d12b0490ca72651fe4d9f5e07e3c6461063a9b6265ee45eb2bdd35"}, - {file = "coverage-7.4.1-cp39-cp39-win32.whl", hash = "sha256:f90515974b39f4dea2f27c0959688621b46d96d5a626cf9c53dbc653a895c05c"}, - {file = "coverage-7.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:64e723ca82a84053dd7bfcc986bdb34af8d9da83c521c19d6b472bc6880e191a"}, - {file = "coverage-7.4.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:32a8d985462e37cfdab611a6f95b09d7c091d07668fdc26e47a725ee575fe166"}, - {file = "coverage-7.4.1.tar.gz", hash = "sha256:1ed4b95480952b1a26d863e546fa5094564aa0065e1e5f0d4d0041f293251d04"}, + {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, + {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"}, + {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"}, + {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"}, + {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"}, + {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"}, + {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"}, + {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"}, + {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"}, + {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"}, + {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"}, + {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"}, + {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"}, + {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, ] [package.extras] @@ -825,43 +825,43 @@ files = [ [[package]] name = "cryptography" -version = "42.0.3" +version = "42.0.5" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-42.0.3-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:de5086cd475d67113ccb6f9fae6d8fe3ac54a4f9238fd08bfdb07b03d791ff0a"}, - {file = "cryptography-42.0.3-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:935cca25d35dda9e7bd46a24831dfd255307c55a07ff38fd1a92119cffc34857"}, - {file = "cryptography-42.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20100c22b298c9eaebe4f0b9032ea97186ac2555f426c3e70670f2517989543b"}, - {file = "cryptography-42.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2eb6368d5327d6455f20327fb6159b97538820355ec00f8cc9464d617caecead"}, - {file = "cryptography-42.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:39d5c93e95bcbc4c06313fc6a500cee414ee39b616b55320c1904760ad686938"}, - {file = "cryptography-42.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3d96ea47ce6d0055d5b97e761d37b4e84195485cb5a38401be341fabf23bc32a"}, - {file = "cryptography-42.0.3-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:d1998e545081da0ab276bcb4b33cce85f775adb86a516e8f55b3dac87f469548"}, - {file = "cryptography-42.0.3-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:93fbee08c48e63d5d1b39ab56fd3fdd02e6c2431c3da0f4edaf54954744c718f"}, - {file = "cryptography-42.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:90147dad8c22d64b2ff7331f8d4cddfdc3ee93e4879796f837bdbb2a0b141e0c"}, - {file = "cryptography-42.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4dcab7c25e48fc09a73c3e463d09ac902a932a0f8d0c568238b3696d06bf377b"}, - {file = "cryptography-42.0.3-cp37-abi3-win32.whl", hash = "sha256:1e935c2900fb53d31f491c0de04f41110351377be19d83d908c1fd502ae8daa5"}, - {file = "cryptography-42.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:762f3771ae40e111d78d77cbe9c1035e886ac04a234d3ee0856bf4ecb3749d54"}, - {file = "cryptography-42.0.3-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:0d3ec384058b642f7fb7e7bff9664030011ed1af8f852540c76a1317a9dd0d20"}, - {file = "cryptography-42.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35772a6cffd1f59b85cb670f12faba05513446f80352fe811689b4e439b5d89e"}, - {file = "cryptography-42.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:04859aa7f12c2b5f7e22d25198ddd537391f1695df7057c8700f71f26f47a129"}, - {file = "cryptography-42.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c3d1f5a1d403a8e640fa0887e9f7087331abb3f33b0f2207d2cc7f213e4a864c"}, - {file = "cryptography-42.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:df34312149b495d9d03492ce97471234fd9037aa5ba217c2a6ea890e9166f151"}, - {file = "cryptography-42.0.3-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:de4ae486041878dc46e571a4c70ba337ed5233a1344c14a0790c4c4be4bbb8b4"}, - {file = "cryptography-42.0.3-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:0fab2a5c479b360e5e0ea9f654bcebb535e3aa1e493a715b13244f4e07ea8eec"}, - {file = "cryptography-42.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25b09b73db78facdfd7dd0fa77a3f19e94896197c86e9f6dc16bce7b37a96504"}, - {file = "cryptography-42.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d5cf11bc7f0b71fb71af26af396c83dfd3f6eed56d4b6ef95d57867bf1e4ba65"}, - {file = "cryptography-42.0.3-cp39-abi3-win32.whl", hash = "sha256:0fea01527d4fb22ffe38cd98951c9044400f6eff4788cf52ae116e27d30a1ba3"}, - {file = "cryptography-42.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:2619487f37da18d6826e27854a7f9d4d013c51eafb066c80d09c63cf24505306"}, - {file = "cryptography-42.0.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ead69ba488f806fe1b1b4050febafdbf206b81fa476126f3e16110c818bac396"}, - {file = "cryptography-42.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:20180da1b508f4aefc101cebc14c57043a02b355d1a652b6e8e537967f1e1b46"}, - {file = "cryptography-42.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:5fbf0f3f0fac7c089308bd771d2c6c7b7d53ae909dce1db52d8e921f6c19bb3a"}, - {file = "cryptography-42.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c23f03cfd7d9826cdcbad7850de67e18b4654179e01fe9bc623d37c2638eb4ef"}, - {file = "cryptography-42.0.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:db0480ffbfb1193ac4e1e88239f31314fe4c6cdcf9c0b8712b55414afbf80db4"}, - {file = "cryptography-42.0.3-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:6c25e1e9c2ce682d01fc5e2dde6598f7313027343bd14f4049b82ad0402e52cd"}, - {file = "cryptography-42.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9541c69c62d7446539f2c1c06d7046aef822940d248fa4b8962ff0302862cc1f"}, - {file = "cryptography-42.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1b797099d221df7cce5ff2a1d272761d1554ddf9a987d3e11f6459b38cd300fd"}, - {file = "cryptography-42.0.3.tar.gz", hash = "sha256:069d2ce9be5526a44093a0991c450fe9906cdf069e0e7cd67d9dee49a62b9ebe"}, + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"}, + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"}, + {file = "cryptography-42.0.5-cp37-abi3-win32.whl", hash = "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"}, + {file = "cryptography-42.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"}, + {file = "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"}, + {file = "cryptography-42.0.5-cp39-abi3-win32.whl", hash = "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"}, + {file = "cryptography-42.0.5-cp39-abi3-win_amd64.whl", hash = "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"}, + {file = "cryptography-42.0.5.tar.gz", hash = "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"}, ] [package.dependencies] @@ -2281,22 +2281,22 @@ tests = ["pytest (>=4.6)"] [[package]] name = "msal" -version = "1.26.0" +version = "1.27.0" description = "The Microsoft Authentication Library (MSAL) for Python library enables your app to access the Microsoft Cloud by supporting authentication of users with Microsoft Azure Active Directory accounts (AAD) and Microsoft Accounts (MSA) using industry standard OAuth2 and OpenID Connect." optional = false python-versions = ">=2.7" files = [ - {file = "msal-1.26.0-py2.py3-none-any.whl", hash = "sha256:be77ba6a8f49c9ff598bbcdc5dfcf1c9842f3044300109af738e8c3e371065b5"}, - {file = "msal-1.26.0.tar.gz", hash = "sha256:224756079fe338be838737682b49f8ebc20a87c1c5eeaf590daae4532b83de15"}, + {file = "msal-1.27.0-py2.py3-none-any.whl", hash = "sha256:572d07149b83e7343a85a3bcef8e581167b4ac76befcbbb6eef0c0e19643cdc0"}, + {file = "msal-1.27.0.tar.gz", hash = "sha256:3109503c038ba6b307152b0e8d34f98113f2e7a78986e28d0baf5b5303afda52"}, ] [package.dependencies] -cryptography = ">=0.6,<44" +cryptography = ">=0.6,<45" PyJWT = {version = ">=1.0.0,<3", extras = ["crypto"]} requests = ">=2.0.0,<3" [package.extras] -broker = ["pymsalruntime (>=0.13.2,<0.14)"] +broker = ["pymsalruntime (>=0.13.2,<0.15)"] [[package]] name = "msal-extensions" @@ -2608,36 +2608,36 @@ reference = ["Pillow", "google-re2"] [[package]] name = "onnxruntime" -version = "1.17.0" +version = "1.17.1" description = "ONNX Runtime is a runtime accelerator for Machine Learning models" optional = false python-versions = "*" files = [ - {file = "onnxruntime-1.17.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:d2b22a25a94109cc983443116da8d9805ced0256eb215c5e6bc6dcbabefeab96"}, - {file = "onnxruntime-1.17.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4c87d83c6f58d1af2675fc99e3dc810f2dbdb844bcefd0c1b7573632661f6fc"}, - {file = "onnxruntime-1.17.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dba55723bf9b835e358f48c98a814b41692c393eb11f51e02ece0625c756b797"}, - {file = "onnxruntime-1.17.0-cp310-cp310-win32.whl", hash = "sha256:ee48422349cc500273beea7607e33c2237909f58468ae1d6cccfc4aecd158565"}, - {file = "onnxruntime-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:f34cc46553359293854e38bdae2ab1be59543aad78a6317e7746d30e311110c3"}, - {file = "onnxruntime-1.17.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:16d26badd092c8c257fa57c458bb600d96dc15282c647ccad0ed7b2732e6c03b"}, - {file = "onnxruntime-1.17.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6f1273bebcdb47ed932d076c85eb9488bc4768fcea16d5f2747ca692fad4f9d3"}, - {file = "onnxruntime-1.17.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cb60fd3c2c1acd684752eb9680e89ae223e9801a9b0e0dc7b28adabe45a2e380"}, - {file = "onnxruntime-1.17.0-cp311-cp311-win32.whl", hash = "sha256:4b038324586bc905299e435f7c00007e6242389c856b82fe9357fdc3b1ef2bdc"}, - {file = "onnxruntime-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:93d39b3fa1ee01f034f098e1c7769a811a21365b4883f05f96c14a2b60c6028b"}, - {file = "onnxruntime-1.17.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:90c0890e36f880281c6c698d9bc3de2afbeee2f76512725ec043665c25c67d21"}, - {file = "onnxruntime-1.17.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7466724e809a40e986b1637cba156ad9fc0d1952468bc00f79ef340bc0199552"}, - {file = "onnxruntime-1.17.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d47bee7557a8b99c8681b6882657a515a4199778d6d5e24e924d2aafcef55b0a"}, - {file = "onnxruntime-1.17.0-cp312-cp312-win32.whl", hash = "sha256:bb1bf1ee575c665b8bbc3813ab906e091a645a24ccc210be7932154b8260eca1"}, - {file = "onnxruntime-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:ac2f286da3494b29b4186ca193c7d4e6a2c1f770c4184c7192c5da142c3dec28"}, - {file = "onnxruntime-1.17.0-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1ec485643b93e0a3896c655eb2426decd63e18a278bb7ccebc133b340723624f"}, - {file = "onnxruntime-1.17.0-cp38-cp38-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83c35809cda898c5a11911c69ceac8a2ac3925911854c526f73bad884582f911"}, - {file = "onnxruntime-1.17.0-cp38-cp38-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fa464aa4d81df818375239e481887b656e261377d5b6b9a4692466f5f3261edc"}, - {file = "onnxruntime-1.17.0-cp38-cp38-win32.whl", hash = "sha256:b7b337cd0586f7836601623cbd30a443df9528ef23965860d11c753ceeb009f2"}, - {file = "onnxruntime-1.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:fbb9faaf51d01aa2c147ef52524d9326744c852116d8005b9041809a71838878"}, - {file = "onnxruntime-1.17.0-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:5a06ab84eaa350bf64b1d747b33ccf10da64221ed1f38f7287f15eccbec81603"}, - {file = "onnxruntime-1.17.0-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d3d11db2c8242766212a68d0b139745157da7ce53bd96ba349a5c65e5a02357"}, - {file = "onnxruntime-1.17.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5632077c3ab8b0cd4f74b0af9c4e924be012b1a7bcd7daa845763c6c6bf14b7d"}, - {file = "onnxruntime-1.17.0-cp39-cp39-win32.whl", hash = "sha256:61a12732cba869b3ad2d4e29ab6cb62c7a96f61b8c213f7fcb961ba412b70b37"}, - {file = "onnxruntime-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:461fa0fc7d9c392c352b6cccdedf44d818430f3d6eacd924bb804fdea2dcfd02"}, + {file = "onnxruntime-1.17.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:d43ac17ac4fa3c9096ad3c0e5255bb41fd134560212dc124e7f52c3159af5d21"}, + {file = "onnxruntime-1.17.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55b5e92a4c76a23981c998078b9bf6145e4fb0b016321a8274b1607bd3c6bd35"}, + {file = "onnxruntime-1.17.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ebbcd2bc3a066cf54e6f18c75708eb4d309ef42be54606d22e5bdd78afc5b0d7"}, + {file = "onnxruntime-1.17.1-cp310-cp310-win32.whl", hash = "sha256:5e3716b5eec9092e29a8d17aab55e737480487deabfca7eac3cd3ed952b6ada9"}, + {file = "onnxruntime-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:fbb98cced6782ae1bb799cc74ddcbbeeae8819f3ad1d942a74d88e72b6511337"}, + {file = "onnxruntime-1.17.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:36fd6f87a1ecad87e9c652e42407a50fb305374f9a31d71293eb231caae18784"}, + {file = "onnxruntime-1.17.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:99a8bddeb538edabc524d468edb60ad4722cff8a49d66f4e280c39eace70500b"}, + {file = "onnxruntime-1.17.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd7fddb4311deb5a7d3390cd8e9b3912d4d963efbe4dfe075edbaf18d01c024e"}, + {file = "onnxruntime-1.17.1-cp311-cp311-win32.whl", hash = "sha256:606a7cbfb6680202b0e4f1890881041ffc3ac6e41760a25763bd9fe146f0b335"}, + {file = "onnxruntime-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:53e4e06c0a541696ebdf96085fd9390304b7b04b748a19e02cf3b35c869a1e76"}, + {file = "onnxruntime-1.17.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:40f08e378e0f85929712a2b2c9b9a9cc400a90c8a8ca741d1d92c00abec60843"}, + {file = "onnxruntime-1.17.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac79da6d3e1bb4590f1dad4bb3c2979d7228555f92bb39820889af8b8e6bd472"}, + {file = "onnxruntime-1.17.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ae9ba47dc099004e3781f2d0814ad710a13c868c739ab086fc697524061695ea"}, + {file = "onnxruntime-1.17.1-cp312-cp312-win32.whl", hash = "sha256:2dff1a24354220ac30e4a4ce2fb1df38cb1ea59f7dac2c116238d63fe7f4c5ff"}, + {file = "onnxruntime-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:6226a5201ab8cafb15e12e72ff2a4fc8f50654e8fa5737c6f0bd57c5ff66827e"}, + {file = "onnxruntime-1.17.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:cd0c07c0d1dfb8629e820b05fda5739e4835b3b82faf43753d2998edf2cf00aa"}, + {file = "onnxruntime-1.17.1-cp38-cp38-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:617ebdf49184efa1ba6e4467e602fbfa029ed52c92f13ce3c9f417d303006381"}, + {file = "onnxruntime-1.17.1-cp38-cp38-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9dae9071e3facdf2920769dceee03b71c684b6439021defa45b830d05e148924"}, + {file = "onnxruntime-1.17.1-cp38-cp38-win32.whl", hash = "sha256:835d38fa1064841679433b1aa8138b5e1218ddf0cfa7a3ae0d056d8fd9cec713"}, + {file = "onnxruntime-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:96621e0c555c2453bf607606d08af3f70fbf6f315230c28ddea91754e17ad4e6"}, + {file = "onnxruntime-1.17.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:7a9539935fb2d78ebf2cf2693cad02d9930b0fb23cdd5cf37a7df813e977674d"}, + {file = "onnxruntime-1.17.1-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45c6a384e9d9a29c78afff62032a46a993c477b280247a7e335df09372aedbe9"}, + {file = "onnxruntime-1.17.1-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4e19f966450f16863a1d6182a685ca33ae04d7772a76132303852d05b95411ea"}, + {file = "onnxruntime-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e2ae712d64a42aac29ed7a40a426cb1e624a08cfe9273dcfe681614aa65b07dc"}, + {file = "onnxruntime-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:f7e9f7fb049825cdddf4a923cfc7c649d84d63c0134315f8e0aa9e0c3004672c"}, ] [package.dependencies] @@ -2650,21 +2650,21 @@ sympy = "*" [[package]] name = "onnxruntime-gpu" -version = "1.17.0" +version = "1.17.1" description = "ONNX Runtime is a runtime accelerator for Machine Learning models" optional = false python-versions = "*" files = [ - {file = "onnxruntime_gpu-1.17.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:1f2a4e0468ac0bd8246996c3d5dbba92cbbaca874bcd7f9cee4e99ce6eb27f5b"}, - {file = "onnxruntime_gpu-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:0721b7930d7abed3730b2335e639e60d94ec411bb4d35a0347cc9c8b52c34540"}, - {file = "onnxruntime_gpu-1.17.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:be0314afe399943904de7c1ca797cbcc63e6fad60eb85d3df6422f81dd94e79e"}, - {file = "onnxruntime_gpu-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:52125c24b21406d1431e43de1c98cea29c21e0cceba80db530b7e4c9216d86ea"}, - {file = "onnxruntime_gpu-1.17.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:bb802d8033885c412269f8bc8877d8779b0dc874df6fb9df8b796cba7276ad66"}, - {file = "onnxruntime_gpu-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:8c43533e3e5335eaa78059fb86b849a4faded513a00c1feaaa205ca5af51c40f"}, - {file = "onnxruntime_gpu-1.17.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:1d461455bba160836d6c11c648c8fd4e4500d5c17096a13e6c2c9d22a4abd436"}, - {file = "onnxruntime_gpu-1.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4398f2175a92f4b35d95279a6294a89c462f24de058a2736ee1d498bab5a16"}, - {file = "onnxruntime_gpu-1.17.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:1d0e3805cd1c024aba7f4ae576fd08545fc27530a2aaad2b3c8ac0ee889fbd05"}, - {file = "onnxruntime_gpu-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc1da5b93363ee600b5b220b04eeec51ad2c2b3e96f0b7615b16b8a173c88001"}, + {file = "onnxruntime_gpu-1.17.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a55fe84ee11a59ea069c6a790ee960f1c7da0d7d6c74822b2a8b357027c93646"}, + {file = "onnxruntime_gpu-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:a9abefceb32879cbee9f57977d6bb8d58cbac501f8a64bf96bca2f4fdff157fe"}, + {file = "onnxruntime_gpu-1.17.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:b2cd54f2b0a05e6bc9ab30182b859364d30115a19c31be24aa2edef40be00277"}, + {file = "onnxruntime_gpu-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:bdffcced8a5f6275c0df202220e9232138b336f868cd671c9d2c571e834d2a80"}, + {file = "onnxruntime_gpu-1.17.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:a1c871e8d0ae4121ea6528fc9410a5a7cbc5e43714b30521d5514fd10b987c83"}, + {file = "onnxruntime_gpu-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:9a0a94eda080e9f4a8e5035fdf0b3c24f5533e7861d88833a94493e63fca0812"}, + {file = "onnxruntime_gpu-1.17.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:624fdb65a632833f13de36854855818680be4f77942d8114524491d58f60d3ab"}, + {file = "onnxruntime_gpu-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:29fa78d232bbb5a5be3a3e0a022148a7b3df2ca66b4c21a11eef56e6f22859e9"}, + {file = "onnxruntime_gpu-1.17.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b0f8c70f2f9aeae825f3a397cc0c5f45124f9ae7c173263cf13c495982b0b99a"}, + {file = "onnxruntime_gpu-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:b1a27a104334461b690e4fc62775e1e71c68936399874932225d7fea21a0c261"}, ] [package.dependencies] @@ -2844,40 +2844,40 @@ test = ["pylint (>=3.0.0,<3.1.0)", "pytest", "pytest-pylint"] [[package]] name = "pandas" -version = "2.2.0" +version = "2.2.1" description = "Powerful data structures for data analysis, time series, and statistics" optional = false python-versions = ">=3.9" files = [ - {file = "pandas-2.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8108ee1712bb4fa2c16981fba7e68b3f6ea330277f5ca34fa8d557e986a11670"}, - {file = "pandas-2.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:736da9ad4033aeab51d067fc3bd69a0ba36f5a60f66a527b3d72e2030e63280a"}, - {file = "pandas-2.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38e0b4fc3ddceb56ec8a287313bc22abe17ab0eb184069f08fc6a9352a769b18"}, - {file = "pandas-2.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20404d2adefe92aed3b38da41d0847a143a09be982a31b85bc7dd565bdba0f4e"}, - {file = "pandas-2.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7ea3ee3f125032bfcade3a4cf85131ed064b4f8dd23e5ce6fa16473e48ebcaf5"}, - {file = "pandas-2.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f9670b3ac00a387620489dfc1bca66db47a787f4e55911f1293063a78b108df1"}, - {file = "pandas-2.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:5a946f210383c7e6d16312d30b238fd508d80d927014f3b33fb5b15c2f895430"}, - {file = "pandas-2.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a1b438fa26b208005c997e78672f1aa8138f67002e833312e6230f3e57fa87d5"}, - {file = "pandas-2.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8ce2fbc8d9bf303ce54a476116165220a1fedf15985b09656b4b4275300e920b"}, - {file = "pandas-2.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2707514a7bec41a4ab81f2ccce8b382961a29fbe9492eab1305bb075b2b1ff4f"}, - {file = "pandas-2.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85793cbdc2d5bc32620dc8ffa715423f0c680dacacf55056ba13454a5be5de88"}, - {file = "pandas-2.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:cfd6c2491dc821b10c716ad6776e7ab311f7df5d16038d0b7458bc0b67dc10f3"}, - {file = "pandas-2.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a146b9dcacc3123aa2b399df1a284de5f46287a4ab4fbfc237eac98a92ebcb71"}, - {file = "pandas-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:fbc1b53c0e1fdf16388c33c3cca160f798d38aea2978004dd3f4d3dec56454c9"}, - {file = "pandas-2.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a41d06f308a024981dcaa6c41f2f2be46a6b186b902c94c2674e8cb5c42985bc"}, - {file = "pandas-2.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:159205c99d7a5ce89ecfc37cb08ed179de7783737cea403b295b5eda8e9c56d1"}, - {file = "pandas-2.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1e1f3861ea9132b32f2133788f3b14911b68102d562715d71bd0013bc45440"}, - {file = "pandas-2.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:761cb99b42a69005dec2b08854fb1d4888fdf7b05db23a8c5a099e4b886a2106"}, - {file = "pandas-2.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a20628faaf444da122b2a64b1e5360cde100ee6283ae8effa0d8745153809a2e"}, - {file = "pandas-2.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f5be5d03ea2073627e7111f61b9f1f0d9625dc3c4d8dda72cc827b0c58a1d042"}, - {file = "pandas-2.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:a626795722d893ed6aacb64d2401d017ddc8a2341b49e0384ab9bf7112bdec30"}, - {file = "pandas-2.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9f66419d4a41132eb7e9a73dcec9486cf5019f52d90dd35547af11bc58f8637d"}, - {file = "pandas-2.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:57abcaeda83fb80d447f28ab0cc7b32b13978f6f733875ebd1ed14f8fbc0f4ab"}, - {file = "pandas-2.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e60f1f7dba3c2d5ca159e18c46a34e7ca7247a73b5dd1a22b6d59707ed6b899a"}, - {file = "pandas-2.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb61dc8567b798b969bcc1fc964788f5a68214d333cade8319c7ab33e2b5d88a"}, - {file = "pandas-2.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:52826b5f4ed658fa2b729264d63f6732b8b29949c7fd234510d57c61dbeadfcd"}, - {file = "pandas-2.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bde2bc699dbd80d7bc7f9cab1e23a95c4375de615860ca089f34e7c64f4a8de7"}, - {file = "pandas-2.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:3de918a754bbf2da2381e8a3dcc45eede8cd7775b047b923f9006d5f876802ae"}, - {file = "pandas-2.2.0.tar.gz", hash = "sha256:30b83f7c3eb217fb4d1b494a57a2fda5444f17834f5df2de6b2ffff68dc3c8e2"}, + {file = "pandas-2.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8df8612be9cd1c7797c93e1c5df861b2ddda0b48b08f2c3eaa0702cf88fb5f88"}, + {file = "pandas-2.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0f573ab277252ed9aaf38240f3b54cfc90fff8e5cab70411ee1d03f5d51f3944"}, + {file = "pandas-2.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f02a3a6c83df4026e55b63c1f06476c9aa3ed6af3d89b4f04ea656ccdaaaa359"}, + {file = "pandas-2.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c38ce92cb22a4bea4e3929429aa1067a454dcc9c335799af93ba9be21b6beb51"}, + {file = "pandas-2.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c2ce852e1cf2509a69e98358e8458775f89599566ac3775e70419b98615f4b06"}, + {file = "pandas-2.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:53680dc9b2519cbf609c62db3ed7c0b499077c7fefda564e330286e619ff0dd9"}, + {file = "pandas-2.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:94e714a1cca63e4f5939cdce5f29ba8d415d85166be3441165edd427dc9f6bc0"}, + {file = "pandas-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f821213d48f4ab353d20ebc24e4faf94ba40d76680642fb7ce2ea31a3ad94f9b"}, + {file = "pandas-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c70e00c2d894cb230e5c15e4b1e1e6b2b478e09cf27cc593a11ef955b9ecc81a"}, + {file = "pandas-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e97fbb5387c69209f134893abc788a6486dbf2f9e511070ca05eed4b930b1b02"}, + {file = "pandas-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101d0eb9c5361aa0146f500773395a03839a5e6ecde4d4b6ced88b7e5a1a6403"}, + {file = "pandas-2.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7d2ed41c319c9fb4fd454fe25372028dfa417aacb9790f68171b2e3f06eae8cd"}, + {file = "pandas-2.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:af5d3c00557d657c8773ef9ee702c61dd13b9d7426794c9dfeb1dc4a0bf0ebc7"}, + {file = "pandas-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:06cf591dbaefb6da9de8472535b185cba556d0ce2e6ed28e21d919704fef1a9e"}, + {file = "pandas-2.2.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:88ecb5c01bb9ca927ebc4098136038519aa5d66b44671861ffab754cae75102c"}, + {file = "pandas-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:04f6ec3baec203c13e3f8b139fb0f9f86cd8c0b94603ae3ae8ce9a422e9f5bee"}, + {file = "pandas-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a935a90a76c44fe170d01e90a3594beef9e9a6220021acfb26053d01426f7dc2"}, + {file = "pandas-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c391f594aae2fd9f679d419e9a4d5ba4bce5bb13f6a989195656e7dc4b95c8f0"}, + {file = "pandas-2.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9d1265545f579edf3f8f0cb6f89f234f5e44ba725a34d86535b1a1d38decbccc"}, + {file = "pandas-2.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:11940e9e3056576ac3244baef2fedade891977bcc1cb7e5cc8f8cc7d603edc89"}, + {file = "pandas-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:4acf681325ee1c7f950d058b05a820441075b0dd9a2adf5c4835b9bc056bf4fb"}, + {file = "pandas-2.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9bd8a40f47080825af4317d0340c656744f2bfdb6819f818e6ba3cd24c0e1397"}, + {file = "pandas-2.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:df0c37ebd19e11d089ceba66eba59a168242fc6b7155cba4ffffa6eccdfb8f16"}, + {file = "pandas-2.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:739cc70eaf17d57608639e74d63387b0d8594ce02f69e7a0b046f117974b3019"}, + {file = "pandas-2.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9d3558d263073ed95e46f4650becff0c5e1ffe0fc3a015de3c79283dfbdb3df"}, + {file = "pandas-2.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4aa1d8707812a658debf03824016bf5ea0d516afdea29b7dc14cf687bc4d4ec6"}, + {file = "pandas-2.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:76f27a809cda87e07f192f001d11adc2b930e93a2b0c4a236fde5429527423be"}, + {file = "pandas-2.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:1ba21b1d5c0e43416218db63037dbe1a01fc101dc6e6024bcad08123e48004ab"}, + {file = "pandas-2.2.1.tar.gz", hash = "sha256:0ab90f87093c13f3e8fa45b48ba9f39181046e8f3317d3aadb2fffbb1b978572"}, ] [package.dependencies] @@ -2905,6 +2905,7 @@ parquet = ["pyarrow (>=10.0.1)"] performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] plot = ["matplotlib (>=3.6.3)"] postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] +pyarrow = ["pyarrow (>=10.0.1)"] spss = ["pyreadstat (>=1.2.0)"] sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] @@ -6468,13 +6469,13 @@ cp2110 = ["hidapi"] [[package]] name = "pytest" -version = "8.0.1" +version = "8.0.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.0.1-py3-none-any.whl", hash = "sha256:3e4f16fe1c0a9dc9d9389161c127c3edc5d810c38d6793042fb81d9f48a59fca"}, - {file = "pytest-8.0.1.tar.gz", hash = "sha256:267f6563751877d772019b13aacbe4e860d73fe8f651f28112e9ac37de7513ae"}, + {file = "pytest-8.0.2-py3-none-any.whl", hash = "sha256:edfaaef32ce5172d5466b5127b42e0d6d35ebbe4453f0e3505d96afd93f6b096"}, + {file = "pytest-8.0.2.tar.gz", hash = "sha256:d4051d623a2e0b7e51960ba963193b09ce6daeb9759a451844a21e4ddedfc1bd"}, ] [package.dependencies] @@ -6639,12 +6640,12 @@ numpy = ["numpy (>=1.6.0)"] [[package]] name = "pytweening" -version = "1.1.0" -description = "A collection of tweening / easing functions." +version = "1.2.0" +description = "A collection of tweening (aka easing) functions." optional = false python-versions = "*" files = [ - {file = "pytweening-1.1.0.tar.gz", hash = "sha256:0d8e14af529dd816ad4aa4a86757dfb5fe2fc2897e06f5db60183706a9370828"}, + {file = "pytweening-1.2.0.tar.gz", hash = "sha256:243318b7736698066c5f362ec5c2b6434ecf4297c3c8e7caa8abfe6af4cac71b"}, ] [[package]] @@ -7172,19 +7173,19 @@ test = ["pytest"] [[package]] name = "setuptools" -version = "69.1.0" +version = "69.1.1" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.1.0-py3-none-any.whl", hash = "sha256:c054629b81b946d63a9c6e732bc8b2513a7c3ea645f11d0139a2191d735c60c6"}, - {file = "setuptools-69.1.0.tar.gz", hash = "sha256:850894c4195f09c4ed30dba56213bf7c3f21d86ed6bdaafb5df5972593bfc401"}, + {file = "setuptools-69.1.1-py3-none-any.whl", hash = "sha256:02fa291a0471b3a18b2b2481ed902af520c69e8ae0919c13da936542754b4c56"}, + {file = "setuptools-69.1.1.tar.gz", hash = "sha256:5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "shapely" @@ -7619,13 +7620,13 @@ files = [ [[package]] name = "typing-extensions" -version = "4.9.0" +version = "4.10.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, - {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, ] [[package]] @@ -7658,13 +7659,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.25.0" +version = "20.25.1" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.25.0-py3-none-any.whl", hash = "sha256:4238949c5ffe6876362d9c0180fc6c3a824a7b12b80604eeb8085f2ed7460de3"}, - {file = "virtualenv-20.25.0.tar.gz", hash = "sha256:bf51c0d9c7dd63ea8e44086fa1e4fb1093a31e963b86959257378aef020e1f1b"}, + {file = "virtualenv-20.25.1-py3-none-any.whl", hash = "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a"}, + {file = "virtualenv-20.25.1.tar.gz", hash = "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197"}, ] [package.dependencies] diff --git a/pyproject.toml b/pyproject.toml index 51396ca39b..99a8602460 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,6 +63,9 @@ warn_unused_ignores=true # restrict dynamic typing warn_return_any=true +# allow implicit optionals for default args +implicit_optional = true + [tool.poetry] name = "openpilot" @@ -187,6 +190,7 @@ lint.flake8-implicit-str-concat.allow-multiline=false "system".msg = "Use openpilot.system" "third_party".msg = "Use openpilot.third_party" "tools".msg = "Use openpilot.tools" +"pytest.main".msg = "pytest.main requires special handling that is easy to mess up!" [tool.coverage.run] concurrency = ["multiprocessing", "thread"] diff --git a/rednose_repo b/rednose_repo index 18b91458fd..c5762e8bc6 160000 --- a/rednose_repo +++ b/rednose_repo @@ -1 +1 @@ -Subproject commit 18b91458fd396530d43e1a2fe9a3ac9055fa9109 +Subproject commit c5762e8bc6f7338c7a30d2cd1cba8cc64e81ba19 diff --git a/release/files_common b/release/files_common index 3407f2f313..1378fa903f 100644 --- a/release/files_common +++ b/release/files_common @@ -27,6 +27,7 @@ common/.gitignore common/__init__.py common/*.py common/*.pyx +common/mock/* common/transformations/__init__.py common/transformations/camera.py @@ -91,6 +92,7 @@ selfdrive/car/docs_definitions.py selfdrive/car/car_helpers.py selfdrive/car/fingerprints.py selfdrive/car/interfaces.py +selfdrive/car/values.py selfdrive/car/vin.py selfdrive/car/disable_ecu.py selfdrive/car/fw_versions.py diff --git a/scripts/pyupgrade.sh b/scripts/pyupgrade.sh new file mode 100755 index 0000000000..19aac4b5e2 --- /dev/null +++ b/scripts/pyupgrade.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -e + +pip install --upgrade pyupgrade + +git ls-files '*.py' | grep -v 'third_party/' | xargs pyupgrade --py311-plus diff --git a/selfdrive/athena/athenad.py b/selfdrive/athena/athenad.py index cc3ab5e95b..9f901498b7 100755 --- a/selfdrive/athena/athenad.py +++ b/selfdrive/athena/athenad.py @@ -19,7 +19,8 @@ from dataclasses import asdict, dataclass, replace from datetime import datetime from functools import partial from queue import Queue -from typing import Callable, Dict, List, Optional, Set, Union, cast +from typing import cast +from collections.abc import Callable import requests from jsonrpc import JSONRPCResponseManager, dispatcher @@ -55,17 +56,17 @@ WS_FRAME_SIZE = 4096 NetworkType = log.DeviceState.NetworkType -UploadFileDict = Dict[str, Union[str, int, float, bool]] -UploadItemDict = Dict[str, Union[str, bool, int, float, Dict[str, str]]] +UploadFileDict = dict[str, str | int | float | bool] +UploadItemDict = dict[str, str | bool | int | float | dict[str, str]] -UploadFilesToUrlResponse = Dict[str, Union[int, List[UploadItemDict], List[str]]] +UploadFilesToUrlResponse = dict[str, int | list[UploadItemDict] | list[str]] @dataclass class UploadFile: fn: str url: str - headers: Dict[str, str] + headers: dict[str, str] allow_cellular: bool @classmethod @@ -77,9 +78,9 @@ class UploadFile: class UploadItem: path: str url: str - headers: Dict[str, str] + headers: dict[str, str] created_at: int - id: Optional[str] + id: str | None retry_count: int = 0 current: bool = False progress: float = 0 @@ -97,9 +98,9 @@ send_queue: Queue[str] = queue.Queue() upload_queue: Queue[UploadItem] = queue.Queue() low_priority_send_queue: Queue[str] = queue.Queue() log_recv_queue: Queue[str] = queue.Queue() -cancelled_uploads: Set[str] = set() +cancelled_uploads: set[str] = set() -cur_upload_items: Dict[int, Optional[UploadItem]] = {} +cur_upload_items: dict[int, UploadItem | None] = {} def strip_bz2_extension(fn: str) -> str: @@ -127,14 +128,14 @@ class UploadQueueCache: @staticmethod def cache(upload_queue: Queue[UploadItem]) -> None: try: - queue: List[Optional[UploadItem]] = list(upload_queue.queue) + queue: list[UploadItem | None] = list(upload_queue.queue) items = [asdict(i) for i in queue if i is not None and (i.id not in cancelled_uploads)] Params().put("AthenadUploadQueue", json.dumps(items)) except Exception: cloudlog.exception("athena.UploadQueueCache.cache.exception") -def handle_long_poll(ws: WebSocket, exit_event: Optional[threading.Event]) -> None: +def handle_long_poll(ws: WebSocket, exit_event: threading.Event | None) -> None: end_event = threading.Event() threads = [ @@ -278,7 +279,7 @@ def upload_handler(end_event: threading.Event) -> None: cloudlog.exception("athena.upload_handler.exception") -def _do_upload(upload_item: UploadItem, callback: Optional[Callable] = None) -> requests.Response: +def _do_upload(upload_item: UploadItem, callback: Callable = None) -> requests.Response: path = upload_item.path compress = False @@ -317,7 +318,7 @@ def getMessage(service: str, timeout: int = 1000) -> dict: @dispatcher.add_method -def getVersion() -> Dict[str, str]: +def getVersion() -> dict[str, str]: return { "version": get_version(), "remote": get_normalized_origin(), @@ -327,7 +328,7 @@ def getVersion() -> Dict[str, str]: @dispatcher.add_method -def setNavDestination(latitude: int = 0, longitude: int = 0, place_name: Optional[str] = None, place_details: Optional[str] = None) -> Dict[str, int]: +def setNavDestination(latitude: int = 0, longitude: int = 0, place_name: str = None, place_details: str = None) -> dict[str, int]: destination = { "latitude": latitude, "longitude": longitude, @@ -339,7 +340,7 @@ def setNavDestination(latitude: int = 0, longitude: int = 0, place_name: Optiona return {"success": 1} -def scan_dir(path: str, prefix: str) -> List[str]: +def scan_dir(path: str, prefix: str) -> list[str]: files = [] # only walk directories that match the prefix # (glob and friends traverse entire dir tree) @@ -359,12 +360,12 @@ def scan_dir(path: str, prefix: str) -> List[str]: return files @dispatcher.add_method -def listDataDirectory(prefix='') -> List[str]: +def listDataDirectory(prefix='') -> list[str]: return scan_dir(Paths.log_root(), prefix) @dispatcher.add_method -def uploadFileToUrl(fn: str, url: str, headers: Dict[str, str]) -> UploadFilesToUrlResponse: +def uploadFileToUrl(fn: str, url: str, headers: dict[str, str]) -> UploadFilesToUrlResponse: # this is because mypy doesn't understand that the decorator doesn't change the return type response: UploadFilesToUrlResponse = uploadFilesToUrls([{ "fn": fn, @@ -375,11 +376,11 @@ def uploadFileToUrl(fn: str, url: str, headers: Dict[str, str]) -> UploadFilesTo @dispatcher.add_method -def uploadFilesToUrls(files_data: List[UploadFileDict]) -> UploadFilesToUrlResponse: +def uploadFilesToUrls(files_data: list[UploadFileDict]) -> UploadFilesToUrlResponse: files = map(UploadFile.from_dict, files_data) - items: List[UploadItemDict] = [] - failed: List[str] = [] + items: list[UploadItemDict] = [] + failed: list[str] = [] for file in files: if len(file.fn) == 0 or file.fn[0] == '/' or '..' in file.fn or len(file.url) == 0: failed.append(file.fn) @@ -418,13 +419,13 @@ def uploadFilesToUrls(files_data: List[UploadFileDict]) -> UploadFilesToUrlRespo @dispatcher.add_method -def listUploadQueue() -> List[UploadItemDict]: +def listUploadQueue() -> list[UploadItemDict]: items = list(upload_queue.queue) + list(cur_upload_items.values()) return [asdict(i) for i in items if (i is not None) and (i.id not in cancelled_uploads)] @dispatcher.add_method -def cancelUpload(upload_id: Union[str, List[str]]) -> Dict[str, Union[int, str]]: +def cancelUpload(upload_id: str | list[str]) -> dict[str, int | str]: if not isinstance(upload_id, list): upload_id = [upload_id] @@ -437,7 +438,7 @@ def cancelUpload(upload_id: Union[str, List[str]]) -> Dict[str, Union[int, str]] return {"success": 1} @dispatcher.add_method -def setRouteViewed(route: str) -> Dict[str, Union[int, str]]: +def setRouteViewed(route: str) -> dict[str, int | str]: # maintain a list of the last 10 routes viewed in connect params = Params() @@ -452,7 +453,7 @@ def setRouteViewed(route: str) -> Dict[str, Union[int, str]]: return {"success": 1} -def startLocalProxy(global_end_event: threading.Event, remote_ws_uri: str, local_port: int) -> Dict[str, int]: +def startLocalProxy(global_end_event: threading.Event, remote_ws_uri: str, local_port: int) -> dict[str, int]: try: if local_port not in LOCAL_PORT_WHITELIST: raise Exception("Requested local port not whitelisted") @@ -486,7 +487,7 @@ def startLocalProxy(global_end_event: threading.Event, remote_ws_uri: str, local @dispatcher.add_method -def getPublicKey() -> Optional[str]: +def getPublicKey() -> str | None: if not os.path.isfile(Paths.persist_root() + '/comma/id_rsa.pub'): return None @@ -526,7 +527,7 @@ def getNetworks(): @dispatcher.add_method -def takeSnapshot() -> Optional[Union[str, Dict[str, str]]]: +def takeSnapshot() -> str | dict[str, str] | None: from openpilot.system.camerad.snapshot.snapshot import jpeg_write, snapshot ret = snapshot() if ret is not None: @@ -543,7 +544,7 @@ def takeSnapshot() -> Optional[Union[str, Dict[str, str]]]: raise Exception("not available while camerad is started") -def get_logs_to_send_sorted() -> List[str]: +def get_logs_to_send_sorted() -> list[str]: # TODO: scan once then use inotify to detect file creation/deletion curr_time = int(time.time()) logs = [] @@ -766,7 +767,7 @@ def backoff(retries: int) -> int: return random.randrange(0, min(128, int(2 ** retries))) -def main(exit_event: Optional[threading.Event] = None): +def main(exit_event: threading.Event = None): try: set_core_affinity([0, 1, 2, 3]) except Exception: diff --git a/selfdrive/athena/registration.py b/selfdrive/athena/registration.py index 7db94c28c8..6574d9ac20 100755 --- a/selfdrive/athena/registration.py +++ b/selfdrive/athena/registration.py @@ -3,7 +3,6 @@ import time import json import jwt from pathlib import Path -from typing import Optional from datetime import datetime, timedelta from openpilot.common.api import api_get @@ -23,12 +22,12 @@ def is_registered_device() -> bool: return dongle not in (None, UNREGISTERED_DONGLE_ID) -def register(show_spinner=False) -> Optional[str]: +def register(show_spinner=False) -> str | None: params = Params() IMEI = params.get("IMEI", encoding='utf8') HardwareSerial = params.get("HardwareSerial", encoding='utf8') - dongle_id: Optional[str] = params.get("DongleId", encoding='utf8') + dongle_id: str | None = params.get("DongleId", encoding='utf8') needs_registration = None in (IMEI, HardwareSerial, dongle_id) pubkey = Path(Paths.persist_root()+"/comma/id_rsa.pub") @@ -48,8 +47,8 @@ def register(show_spinner=False) -> Optional[str]: # Block until we get the imei serial = HARDWARE.get_serial() start_time = time.monotonic() - imei1: Optional[str] = None - imei2: Optional[str] = None + imei1: str | None = None + imei2: str | None = None while imei1 is None and imei2 is None: try: imei1, imei2 = HARDWARE.get_imei(0), HARDWARE.get_imei(1) diff --git a/selfdrive/athena/tests/helpers.py b/selfdrive/athena/tests/helpers.py index 87202665aa..3dd98f02c9 100644 --- a/selfdrive/athena/tests/helpers.py +++ b/selfdrive/athena/tests/helpers.py @@ -1,7 +1,5 @@ import http.server -import threading import socket -from functools import wraps class MockResponse: @@ -65,25 +63,3 @@ class HTTPRequestHandler(http.server.SimpleHTTPRequestHandler): self.rfile.read(length) self.send_response(201, "Created") self.end_headers() - - -def with_http_server(func, handler=http.server.BaseHTTPRequestHandler, setup=None): - @wraps(func) - def inner(*args, **kwargs): - host = '127.0.0.1' - server = http.server.HTTPServer((host, 0), handler) - port = server.server_port - t = threading.Thread(target=server.serve_forever) - t.start() - - if setup is not None: - setup(host, port) - - try: - return func(*args, f'http://{host}:{port}', **kwargs) - finally: - server.shutdown() - server.server_close() - t.join() - - return inner diff --git a/selfdrive/athena/tests/test_athenad.py b/selfdrive/athena/tests/test_athenad.py index d59058d7e2..4850ab9a3f 100755 --- a/selfdrive/athena/tests/test_athenad.py +++ b/selfdrive/athena/tests/test_athenad.py @@ -12,7 +12,6 @@ import unittest from dataclasses import asdict, replace from datetime import datetime, timedelta from parameterized import parameterized -from typing import Optional from unittest import mock from websocket import ABNF @@ -24,9 +23,9 @@ from openpilot.common.params import Params from openpilot.common.timeout import Timeout from openpilot.selfdrive.athena import athenad from openpilot.selfdrive.athena.athenad import MAX_RETRY_COUNT, dispatcher -from openpilot.selfdrive.athena.tests.helpers import MockWebsocket, MockApi, EchoSocket, with_http_server +from openpilot.selfdrive.athena.tests.helpers import HTTPRequestHandler, MockWebsocket, MockApi, EchoSocket +from openpilot.selfdrive.test.helpers import with_http_server from openpilot.system.hardware.hw import Paths -from openpilot.selfdrive.athena.tests.helpers import HTTPRequestHandler def seed_athena_server(host, port): @@ -97,7 +96,7 @@ class TestAthenadMethods(unittest.TestCase): break @staticmethod - def _create_file(file: str, parent: Optional[str] = None, data: bytes = b'') -> str: + def _create_file(file: str, parent: str = None, data: bytes = b'') -> str: fn = os.path.join(Paths.log_root() if parent is None else parent, file) os.makedirs(os.path.dirname(fn), exist_ok=True) with open(fn, 'wb') as f: diff --git a/selfdrive/athena/tests/test_athenad_ping.py b/selfdrive/athena/tests/test_athenad_ping.py index 7f32f60cb4..f56fcac8b5 100755 --- a/selfdrive/athena/tests/test_athenad_ping.py +++ b/selfdrive/athena/tests/test_athenad_ping.py @@ -3,7 +3,7 @@ import subprocess import threading import time import unittest -from typing import cast, Optional +from typing import cast from unittest import mock from openpilot.common.params import Params @@ -29,8 +29,8 @@ class TestAthenadPing(unittest.TestCase): athenad: threading.Thread exit_event: threading.Event - def _get_ping_time(self) -> Optional[str]: - return cast(Optional[str], self.params.get("LastAthenaPingTime", encoding="utf-8")) + def _get_ping_time(self) -> str | None: + return cast(str | None, self.params.get("LastAthenaPingTime", encoding="utf-8")) def _clear_ping_time(self) -> None: self.params.remove("LastAthenaPingTime") diff --git a/selfdrive/boardd/pandad.py b/selfdrive/boardd/pandad.py index a87613c803..988d1a2409 100755 --- a/selfdrive/boardd/pandad.py +++ b/selfdrive/boardd/pandad.py @@ -4,7 +4,7 @@ import os import usb1 import time import subprocess -from typing import List, NoReturn +from typing import NoReturn from functools import cmp_to_key from panda import Panda, PandaDFU, PandaProtocolMismatch, FW_PATH @@ -124,7 +124,7 @@ def main() -> NoReturn: cloudlog.info(f"{len(panda_serials)} panda(s) found, connecting - {panda_serials}") # Flash pandas - pandas: List[Panda] = [] + pandas: list[Panda] = [] for serial in panda_serials: pandas.append(flash_panda(serial)) diff --git a/selfdrive/car/__init__.py b/selfdrive/car/__init__.py index c87564537b..8a7046b1f4 100644 --- a/selfdrive/car/__init__.py +++ b/selfdrive/car/__init__.py @@ -1,8 +1,7 @@ # functions common among cars from collections import namedtuple -from dataclasses import dataclass +from dataclasses import dataclass, field, replace from enum import ReprEnum -from typing import Dict, List, Optional, Union import capnp @@ -27,9 +26,9 @@ def apply_hysteresis(val: float, val_steady: float, hyst_gap: float) -> float: return val_steady -def create_button_events(cur_btn: int, prev_btn: int, buttons_dict: Dict[int, capnp.lib.capnp._EnumModule], - unpressed_btn: int = 0) -> List[capnp.lib.capnp._DynamicStructBuilder]: - events: List[capnp.lib.capnp._DynamicStructBuilder] = [] +def create_button_events(cur_btn: int, prev_btn: int, buttons_dict: dict[int, capnp.lib.capnp._EnumModule], + unpressed_btn: int = 0) -> list[capnp.lib.capnp._DynamicStructBuilder]: + events: list[capnp.lib.capnp._DynamicStructBuilder] = [] if cur_btn == prev_btn: return events @@ -82,7 +81,8 @@ def scale_tire_stiffness(mass, wheelbase, center_to_front, tire_stiffness_factor return tire_stiffness_front, tire_stiffness_rear -DbcDict = Dict[str, str] +DbcDict = dict[str, str] + def dbc_dict(pt_dbc, radar_dbc, chassis_dbc=None, body_dbc=None) -> DbcDict: return {'pt': pt_dbc, 'radar': radar_dbc, 'chassis': chassis_dbc, 'body': body_dbc} @@ -219,7 +219,7 @@ def get_safety_config(safety_model, safety_param = None): class CanBusBase: offset: int - def __init__(self, CP, fingerprint: Optional[Dict[int, Dict[int, int]]]) -> None: + def __init__(self, CP, fingerprint: dict[int, dict[int, int]] | None) -> None: if CP is None: assert fingerprint is not None num = max([k for k, v in fingerprint.items() if len(v)], default=0) // 4 + 1 @@ -249,17 +249,33 @@ class CanSignalRateCalculator: return self.rate -CarInfos = Union[CarInfo, List[CarInfo]] +CarInfos = CarInfo | list[CarInfo] -@dataclass(order=True) + +@dataclass(frozen=True, kw_only=True) +class CarSpecs: + mass: float + wheelbase: float + steerRatio: float + centerToFrontRatio: float = field(default=0.5) + minSteerSpeed: float = field(default=0.) + minEnableSpeed: float = field(default=-1.) + + +@dataclass(frozen=True, order=True) class PlatformConfig: platform_str: str car_info: CarInfos dbc_dict: DbcDict + specs: CarSpecs | None = None + def __hash__(self) -> int: return hash(self.platform_str) + def override(self, **kwargs): + return replace(self, **kwargs) + class Platforms(str, ReprEnum): config: PlatformConfig @@ -271,9 +287,9 @@ class Platforms(str, ReprEnum): return member @classmethod - def create_dbc_map(cls) -> Dict[str, DbcDict]: - return {p.config.platform_str: p.config.dbc_dict for p in cls} + def create_dbc_map(cls) -> dict[str, DbcDict]: + return {p: p.config.dbc_dict for p in cls} @classmethod - def create_carinfo_map(cls) -> Dict[str, CarInfos]: - return {p.config.platform_str: p.config.car_info for p in cls} + def create_carinfo_map(cls) -> dict[str, CarInfos]: + return {p: p.config.car_info for p in cls} diff --git a/selfdrive/car/body/carcontroller.py b/selfdrive/car/body/carcontroller.py index 1dad8e796a..d15f11d3a4 100644 --- a/selfdrive/car/body/carcontroller.py +++ b/selfdrive/car/body/carcontroller.py @@ -1,6 +1,5 @@ import numpy as np -from openpilot.common.params import Params from openpilot.common.realtime import DT_CTRL from opendbc.can.packer import CANPacker from openpilot.selfdrive.car.body import bodycan @@ -20,18 +19,13 @@ class CarController: self.frame = 0 self.packer = CANPacker(dbc_name) - # Speed, balance and turn PIDs - self.speed_pid = PIDController(0.115, k_i=0.23, rate=1/DT_CTRL) - self.balance_pid = PIDController(1300, k_i=0, k_d=280, rate=1/DT_CTRL) + # PIDs self.turn_pid = PIDController(110, k_i=11.5, rate=1/DT_CTRL) self.wheeled_speed_pid = PIDController(110, k_i=11.5, rate=1/DT_CTRL) self.torque_r_filtered = 0. self.torque_l_filtered = 0. - params = Params() - self.wheeled_body = params.get("WheeledBody") - @staticmethod def deadband_filter(torque, deadband): if torque > 0: @@ -55,17 +49,7 @@ class CarController: speed_measured = SPEED_FROM_RPM * (CS.out.wheelSpeeds.fl + CS.out.wheelSpeeds.fr) / 2. speed_error = speed_desired - speed_measured - if self.wheeled_body is None: - freeze_integrator = ((speed_error < 0 and self.speed_pid.error_integral <= -MAX_POS_INTEGRATOR) or - (speed_error > 0 and self.speed_pid.error_integral >= MAX_POS_INTEGRATOR)) - angle_setpoint = self.speed_pid.update(speed_error, freeze_integrator=freeze_integrator) - - # Clip angle error, this is enough to get up from stands - angle_error = np.clip((-CC.orientationNED[1]) - angle_setpoint, -MAX_ANGLE_ERROR, MAX_ANGLE_ERROR) - angle_error_rate = np.clip(-CC.angularVelocity[1], -1., 1.) - torque = self.balance_pid.update(angle_error, error_rate=angle_error_rate) - else: - torque = self.wheeled_speed_pid.update(speed_error, freeze_integrator=False) + torque = self.wheeled_speed_pid.update(speed_error, freeze_integrator=False) speed_diff_measured = SPEED_FROM_RPM * (CS.out.wheelSpeeds.fl - CS.out.wheelSpeeds.fr) turn_error = speed_diff_measured - speed_diff_desired diff --git a/selfdrive/car/car_helpers.py b/selfdrive/car/car_helpers.py index e9fc3d924e..fabdb3f4ed 100644 --- a/selfdrive/car/car_helpers.py +++ b/selfdrive/car/car_helpers.py @@ -3,11 +3,12 @@ import os import requests import threading import time -from typing import Callable, Dict, List, Optional, Tuple +from collections.abc import Callable from cereal import car from openpilot.common.params import Params from openpilot.common.basedir import BASEDIR +from openpilot.selfdrive.car.values import PLATFORMS from openpilot.system.version import is_comma_remote, is_tested_branch from openpilot.selfdrive.car.interfaces import get_interface_attr from openpilot.selfdrive.car.fingerprints import eliminate_incompatible_cars, all_legacy_fingerprint_cars @@ -67,7 +68,7 @@ def load_interfaces(brand_names): return ret -def _get_interface_names() -> Dict[str, List[str]]: +def _get_interface_names() -> dict[str, list[str]]: # returns a dict of brand name and its respective models brand_names = {} for brand_name, brand_models in get_interface_attr("CAR").items(): @@ -81,7 +82,7 @@ interface_names = _get_interface_names() interfaces = load_interfaces(interface_names) -def can_fingerprint(next_can: Callable) -> Tuple[Optional[str], Dict[int, dict]]: +def can_fingerprint(next_can: Callable) -> tuple[str | None, dict[int, dict]]: finger = gen_empty_fingerprint() candidate_cars = {i: all_legacy_fingerprint_cars() for i in [0, 1]} # attempt fingerprint on both bus 0 and 1 frame = 0 @@ -193,7 +194,10 @@ def fingerprint(logcan, sendcan, num_pandas): cloudlog.event("fingerprinted", car_fingerprint=car_fingerprint, source=source, fuzzy=not exact_match, cached=cached, fw_count=len(car_fw), ecu_responses=list(ecu_rx_addrs), vin_rx_addr=vin_rx_addr, vin_rx_bus=vin_rx_bus, fingerprints=repr(finger), fw_query_time=fw_query_time, error=True) - return car_fingerprint, finger, vin, car_fw, source, exact_match + + car_platform = PLATFORMS.get(car_fingerprint, car_fingerprint) + + return car_platform, finger, vin, car_fw, source, exact_match def is_connected_to_internet(timeout=5): diff --git a/selfdrive/car/chrysler/interface.py b/selfdrive/car/chrysler/interface.py index 72e9af6a06..36284bd23c 100755 --- a/selfdrive/car/chrysler/interface.py +++ b/selfdrive/car/chrysler/interface.py @@ -32,7 +32,6 @@ class CarInterface(CarInterfaceBase): elif candidate in RAM_DT: ret.safetyConfigs[0].safetyParam |= Panda.FLAG_CHRYSLER_RAM_DT - ret.minSteerSpeed = 3.8 # m/s CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) if candidate not in RAM_CARS: # Newer FW versions standard on the following platforms, or flashed by a dealer onto older platforms have a higher minimum steering speed. @@ -43,10 +42,6 @@ class CarInterface(CarInterfaceBase): # Chrysler if candidate in (CAR.PACIFICA_2017_HYBRID, CAR.PACIFICA_2018, CAR.PACIFICA_2018_HYBRID, CAR.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020, CAR.DODGE_DURANGO): - ret.mass = 2242. - ret.wheelbase = 3.089 - ret.steerRatio = 16.2 # Pacifica Hybrid 2017 - ret.lateralTuning.init('pid') ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kiBP = [[9., 20.], [9., 20.]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.15, 0.30], [0.03, 0.05]] @@ -54,9 +49,6 @@ class CarInterface(CarInterfaceBase): # Jeep elif candidate in (CAR.JEEP_GRAND_CHEROKEE, CAR.JEEP_GRAND_CHEROKEE_2019): - ret.mass = 1778 - ret.wheelbase = 2.71 - ret.steerRatio = 16.7 ret.steerActuatorDelay = 0.2 ret.lateralTuning.init('pid') @@ -68,19 +60,12 @@ class CarInterface(CarInterfaceBase): elif candidate == CAR.RAM_1500: ret.steerActuatorDelay = 0.2 ret.wheelbase = 3.88 - ret.steerRatio = 16.3 - ret.mass = 2493. - ret.minSteerSpeed = 0.5 - ret.minEnableSpeed = 14.5 - if any(fw.ecu == 'eps' and fw.fwVersion in (b"68273275AF", b"68273275AG", b"68312176AE", b"68312176AG",) for fw in car_fw): - ret.minEnableSpeed = 0. + # Older EPS FW allow steer to zero + if any(fw.ecu == 'eps' and b"68" < fw.fwVersion[:4] <= b"6831" for fw in car_fw): + ret.minSteerSpeed = 0. elif candidate == CAR.RAM_HD: ret.steerActuatorDelay = 0.2 - ret.wheelbase = 3.785 - ret.steerRatio = 15.61 - ret.mass = 3405. - ret.minSteerSpeed = 16 CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, 1.0, False) ret.spFlags |= ChryslerFlagsSP.SP_RAM_HD_PARAMSD_IGNORE.value diff --git a/selfdrive/car/chrysler/values.py b/selfdrive/car/chrysler/values.py index 541af39be9..e6dffaded9 100644 --- a/selfdrive/car/chrysler/values.py +++ b/selfdrive/car/chrysler/values.py @@ -1,10 +1,9 @@ -from enum import IntFlag, StrEnum +from enum import IntFlag from dataclasses import dataclass, field -from typing import Dict, List, Optional, Union from cereal import car from panda.python import uds -from openpilot.selfdrive.car import dbc_dict +from openpilot.selfdrive.car import CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict from openpilot.selfdrive.car.docs_definitions import CarHarness, CarInfo, CarParts from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, p16 @@ -20,24 +19,89 @@ class ChryslerFlagsSP(IntFlag): SP_WP_S20 = 2 -class CAR(StrEnum): +@dataclass +class ChryslerCarInfo(CarInfo): + package: str = "Adaptive Cruise Control (ACC)" + car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.fca])) + + +@dataclass(frozen=True) +class ChryslerPlatformConfig(PlatformConfig): + dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('chrysler_pacifica_2017_hybrid_generated', None)) + + +@dataclass(frozen=True) +class ChryslerCarSpecs(CarSpecs): + minSteerSpeed: float = 3.8 # m/s + + +class CAR(Platforms): # Chrysler - PACIFICA_2017_HYBRID = "CHRYSLER PACIFICA HYBRID 2017" - PACIFICA_2018_HYBRID = "CHRYSLER PACIFICA HYBRID 2018" - PACIFICA_2019_HYBRID = "CHRYSLER PACIFICA HYBRID 2019" - PACIFICA_2018 = "CHRYSLER PACIFICA 2018" - PACIFICA_2020 = "CHRYSLER PACIFICA 2020" + PACIFICA_2017_HYBRID = ChryslerPlatformConfig( + "CHRYSLER PACIFICA HYBRID 2017", + ChryslerCarInfo("Chrysler Pacifica Hybrid 2017"), + specs=ChryslerCarSpecs(mass=2242., wheelbase=3.089, steerRatio=16.2), + ) + PACIFICA_2018_HYBRID = ChryslerPlatformConfig( + "CHRYSLER PACIFICA HYBRID 2018", + ChryslerCarInfo("Chrysler Pacifica Hybrid 2018"), + specs=PACIFICA_2017_HYBRID.specs, + ) + PACIFICA_2019_HYBRID = ChryslerPlatformConfig( + "CHRYSLER PACIFICA HYBRID 2019", + ChryslerCarInfo("Chrysler Pacifica Hybrid 2019-23"), + specs=PACIFICA_2017_HYBRID.specs, + ) + PACIFICA_2018 = ChryslerPlatformConfig( + "CHRYSLER PACIFICA 2018", + ChryslerCarInfo("Chrysler Pacifica 2017-18"), + specs=PACIFICA_2017_HYBRID.specs, + ) + PACIFICA_2020 = ChryslerPlatformConfig( + "CHRYSLER PACIFICA 2020", + [ + ChryslerCarInfo("Chrysler Pacifica 2019-20"), + ChryslerCarInfo("Chrysler Pacifica 2021-23", package="All"), + ], + specs=PACIFICA_2017_HYBRID.specs, + ) # Dodge - DODGE_DURANGO = "DODGE DURANGO 2021" + DODGE_DURANGO = ChryslerPlatformConfig( + "DODGE DURANGO 2021", + ChryslerCarInfo("Dodge Durango 2020-21"), + specs=PACIFICA_2017_HYBRID.specs, + ) # Jeep - JEEP_GRAND_CHEROKEE = "JEEP GRAND CHEROKEE V6 2018" # includes 2017 Trailhawk - JEEP_GRAND_CHEROKEE_2019 = "JEEP GRAND CHEROKEE 2019" # includes 2020 Trailhawk + JEEP_GRAND_CHEROKEE = ChryslerPlatformConfig( # includes 2017 Trailhawk + "JEEP GRAND CHEROKEE V6 2018", + ChryslerCarInfo("Jeep Grand Cherokee 2016-18", video_link="https://www.youtube.com/watch?v=eLR9o2JkuRk"), + specs=ChryslerCarSpecs(mass=1778., wheelbase=2.71, steerRatio=16.7), + ) + + JEEP_GRAND_CHEROKEE_2019 = ChryslerPlatformConfig( # includes 2020 Trailhawk + "JEEP GRAND CHEROKEE 2019", + ChryslerCarInfo("Jeep Grand Cherokee 2019-21", video_link="https://www.youtube.com/watch?v=jBe4lWnRSu4"), + specs=JEEP_GRAND_CHEROKEE.specs, + ) # Ram - RAM_1500 = "RAM 1500 5TH GEN" - RAM_HD = "RAM HD 5TH GEN" + RAM_1500 = ChryslerPlatformConfig( + "RAM 1500 5TH GEN", + ChryslerCarInfo("Ram 1500 2019-24", car_parts=CarParts.common([CarHarness.ram])), + dbc_dict('chrysler_ram_dt_generated', None), + specs=ChryslerCarSpecs(mass=2493., wheelbase=3.88, steerRatio=16.3, minSteerSpeed=14.5), + ) + RAM_HD = ChryslerPlatformConfig( + "RAM HD 5TH GEN", + [ + ChryslerCarInfo("Ram 2500 2020-24", car_parts=CarParts.common([CarHarness.ram])), + ChryslerCarInfo("Ram 3500 2019-22", car_parts=CarParts.common([CarHarness.ram])), + ], + dbc_dict('chrysler_ram_hd_generated', None), + specs=ChryslerCarSpecs(mass=3405., wheelbase=3.785, steerRatio=15.61, minSteerSpeed=16.), + ) class CarControllerParams: @@ -73,32 +137,6 @@ RAM_HD = {CAR.RAM_HD, } RAM_CARS = RAM_DT | RAM_HD -@dataclass -class ChryslerCarInfo(CarInfo): - package: str = "Adaptive Cruise Control (ACC)" - car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.fca])) - - -CAR_INFO: Dict[str, Optional[Union[ChryslerCarInfo, List[ChryslerCarInfo]]]] = { - CAR.PACIFICA_2017_HYBRID: ChryslerCarInfo("Chrysler Pacifica Hybrid 2017"), - CAR.PACIFICA_2018_HYBRID: ChryslerCarInfo("Chrysler Pacifica Hybrid 2018"), - CAR.PACIFICA_2019_HYBRID: ChryslerCarInfo("Chrysler Pacifica Hybrid 2019-23"), - CAR.PACIFICA_2018: ChryslerCarInfo("Chrysler Pacifica 2017-18"), - CAR.PACIFICA_2020: [ - ChryslerCarInfo("Chrysler Pacifica 2019-20"), - ChryslerCarInfo("Chrysler Pacifica 2021-23", package="All"), - ], - CAR.JEEP_GRAND_CHEROKEE: ChryslerCarInfo("Jeep Grand Cherokee 2016-18", video_link="https://www.youtube.com/watch?v=eLR9o2JkuRk"), - CAR.JEEP_GRAND_CHEROKEE_2019: ChryslerCarInfo("Jeep Grand Cherokee 2019-21", video_link="https://www.youtube.com/watch?v=jBe4lWnRSu4"), - CAR.DODGE_DURANGO: ChryslerCarInfo("Dodge Durango 2020-21"), - CAR.RAM_1500: ChryslerCarInfo("Ram 1500 2019-24", car_parts=CarParts.common([CarHarness.ram])), - CAR.RAM_HD: [ - ChryslerCarInfo("Ram 2500 2020-24", car_parts=CarParts.common([CarHarness.ram])), - ChryslerCarInfo("Ram 3500 2019-22", car_parts=CarParts.common([CarHarness.ram])), - ], -} - - CHRYSLER_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \ p16(0xf132) CHRYSLER_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \ @@ -138,16 +176,5 @@ FW_QUERY_CONFIG = FwQueryConfig( ], ) - -DBC = { - CAR.PACIFICA_2017_HYBRID: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'), - CAR.PACIFICA_2018: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'), - CAR.PACIFICA_2020: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'), - CAR.PACIFICA_2018_HYBRID: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'), - CAR.PACIFICA_2019_HYBRID: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'), - CAR.DODGE_DURANGO: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'), - CAR.JEEP_GRAND_CHEROKEE: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'), - CAR.JEEP_GRAND_CHEROKEE_2019: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'), - CAR.RAM_1500: dbc_dict('chrysler_ram_dt_generated', None), - CAR.RAM_HD: dbc_dict('chrysler_ram_hd_generated', None), -} +CAR_INFO = CAR.create_carinfo_map() +DBC = CAR.create_dbc_map() diff --git a/selfdrive/car/docs.py b/selfdrive/car/docs.py index 8475a69d8a..caa79a8f87 100755 --- a/selfdrive/car/docs.py +++ b/selfdrive/car/docs.py @@ -5,7 +5,6 @@ import jinja2 import os from enum import Enum from natsort import natsorted -from typing import Dict, List from cereal import car from openpilot.common.basedir import BASEDIR @@ -14,7 +13,7 @@ from openpilot.selfdrive.car.docs_definitions import CarInfo, Column, CommonFoot from openpilot.selfdrive.car.car_helpers import interfaces, get_interface_attr -def get_all_footnotes() -> Dict[Enum, int]: +def get_all_footnotes() -> dict[Enum, int]: all_footnotes = list(CommonFootnote) for footnotes in get_interface_attr("Footnote", ignore_none=True).values(): all_footnotes.extend(footnotes) @@ -25,8 +24,8 @@ CARS_MD_OUT = os.path.join(BASEDIR, "docs", "CARS.md") CARS_MD_TEMPLATE = os.path.join(BASEDIR, "selfdrive", "car", "CARS_template.md") -def get_all_car_info() -> List[CarInfo]: - all_car_info: List[CarInfo] = [] +def get_all_car_info() -> list[CarInfo]: + all_car_info: list[CarInfo] = [] footnotes = get_all_footnotes() for model, car_info in get_interface_attr("CAR_INFO", combine_brands=True).items(): # If available, uses experimental longitudinal limits for the docs @@ -47,19 +46,19 @@ def get_all_car_info() -> List[CarInfo]: all_car_info.append(_car_info) # Sort cars by make and model + year - sorted_cars: List[CarInfo] = natsorted(all_car_info, key=lambda car: car.name.lower()) + sorted_cars: list[CarInfo] = natsorted(all_car_info, key=lambda car: car.name.lower()) return sorted_cars -def group_by_make(all_car_info: List[CarInfo]) -> Dict[str, List[CarInfo]]: +def group_by_make(all_car_info: list[CarInfo]) -> dict[str, list[CarInfo]]: sorted_car_info = defaultdict(list) for car_info in all_car_info: sorted_car_info[car_info.make].append(car_info) return dict(sorted_car_info) -def generate_cars_md(all_car_info: List[CarInfo], template_fn: str) -> str: - with open(template_fn, "r") as f: +def generate_cars_md(all_car_info: list[CarInfo], template_fn: str) -> str: + with open(template_fn) as f: template = jinja2.Template(f.read(), trim_blocks=True, lstrip_blocks=True) footnotes = [fn.value.text for fn in get_all_footnotes()] diff --git a/selfdrive/car/docs_definitions.py b/selfdrive/car/docs_definitions.py index f2ce743607..841cc3af2f 100644 --- a/selfdrive/car/docs_definitions.py +++ b/selfdrive/car/docs_definitions.py @@ -3,7 +3,6 @@ from collections import namedtuple import copy from dataclasses import dataclass, field from enum import Enum -from typing import Dict, List, Optional, Tuple, Union from cereal import car from openpilot.common.conversions import Conversions as CV @@ -35,7 +34,7 @@ class Star(Enum): @dataclass class BasePart: name: str - parts: List[Enum] = field(default_factory=list) + parts: list[Enum] = field(default_factory=list) def all_parts(self): # Recursively get all parts @@ -76,7 +75,7 @@ class Accessory(EnumBase): @dataclass class BaseCarHarness(BasePart): - parts: List[Enum] = field(default_factory=lambda: [Accessory.harness_box, Accessory.comma_power_v2, Cable.rj45_cable_7ft]) + parts: list[Enum] = field(default_factory=lambda: [Accessory.harness_box, Accessory.comma_power_v2, Cable.rj45_cable_7ft]) has_connector: bool = True # without are hidden on the harness connector page @@ -149,18 +148,18 @@ class PartType(Enum): tool = Tool -DEFAULT_CAR_PARTS: List[EnumBase] = [Device.threex] +DEFAULT_CAR_PARTS: list[EnumBase] = [Device.threex] @dataclass class CarParts: - parts: List[EnumBase] = field(default_factory=list) + parts: list[EnumBase] = field(default_factory=list) def __call__(self): return copy.deepcopy(self) @classmethod - def common(cls, add: Optional[List[EnumBase]] = None, remove: Optional[List[EnumBase]] = None): + def common(cls, add: list[EnumBase] = None, remove: list[EnumBase] = None): p = [part for part in (add or []) + DEFAULT_CAR_PARTS if part not in (remove or [])] return cls(p) @@ -186,7 +185,7 @@ class CommonFootnote(Enum): Column.LONGITUDINAL) -def get_footnotes(footnotes: List[Enum], column: Column) -> List[Enum]: +def get_footnotes(footnotes: list[Enum], column: Column) -> list[Enum]: # Returns applicable footnotes given current column return [fn for fn in footnotes if fn.value.column == column] @@ -209,7 +208,7 @@ def get_year_list(years): return years_list -def split_name(name: str) -> Tuple[str, str, str]: +def split_name(name: str) -> tuple[str, str, str]: make, model = name.split(" ", 1) years = "" match = re.search(MODEL_YEARS_RE, model) @@ -233,13 +232,13 @@ class CarInfo: # the minimum compatibility requirements for this model, regardless # of market. can be a package, trim, or list of features - requirements: Optional[str] = None + requirements: str | None = None - video_link: Optional[str] = None - footnotes: List[Enum] = field(default_factory=list) - min_steer_speed: Optional[float] = None - min_enable_speed: Optional[float] = None - auto_resume: Optional[bool] = None + video_link: str | None = None + footnotes: list[Enum] = field(default_factory=list) + min_steer_speed: float | None = None + min_enable_speed: float | None = None + auto_resume: bool | None = None # all the parts needed for the supported car car_parts: CarParts = field(default_factory=CarParts) @@ -248,7 +247,7 @@ class CarInfo: self.make, self.model, self.years = split_name(self.name) self.year_list = get_year_list(self.years) - def init(self, CP: car.CarParams, all_footnotes: Dict[Enum, int]): + def init(self, CP: car.CarParams, all_footnotes: dict[Enum, int]): self.car_name = CP.carName self.car_fingerprint = CP.carFingerprint @@ -293,7 +292,7 @@ class CarInfo: if len(tools_docs): hardware_col += f'
Tools{display_func(tools_docs)}
' - self.row: Dict[Enum, Union[str, Star]] = { + self.row: dict[Enum, str | Star] = { Column.MAKE: self.make, Column.MODEL: self.model, Column.PACKAGE: self.package, @@ -352,7 +351,7 @@ class CarInfo: raise Exception(f"This notCar does not have a detail sentence: {CP.carFingerprint}") def get_column(self, column: Column, star_icon: str, video_icon: str, footnote_tag: str) -> str: - item: Union[str, Star] = self.row[column] + item: str | Star = self.row[column] if isinstance(item, Star): item = star_icon.format(item.value) elif column == Column.MODEL and len(self.years): diff --git a/selfdrive/car/ecu_addrs.py b/selfdrive/car/ecu_addrs.py index 13f7926def..da5e7b4612 100755 --- a/selfdrive/car/ecu_addrs.py +++ b/selfdrive/car/ecu_addrs.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 import capnp import time -from typing import Optional, Set import cereal.messaging as messaging from panda.python.uds import SERVICE_TYPE @@ -20,7 +19,7 @@ def make_tester_present_msg(addr, bus, subaddr=None): return make_can_msg(addr, bytes(dat), bus) -def is_tester_present_response(msg: capnp.lib.capnp._DynamicStructReader, subaddr: Optional[int] = None) -> bool: +def is_tester_present_response(msg: capnp.lib.capnp._DynamicStructReader, subaddr: int = None) -> bool: # ISO-TP messages are always padded to 8 bytes # tester present response is always a single frame dat_offset = 1 if subaddr is not None else 0 @@ -34,16 +33,16 @@ def is_tester_present_response(msg: capnp.lib.capnp._DynamicStructReader, subadd return False -def get_all_ecu_addrs(logcan: messaging.SubSocket, sendcan: messaging.PubSocket, bus: int, timeout: float = 1, debug: bool = True) -> Set[EcuAddrBusType]: +def get_all_ecu_addrs(logcan: messaging.SubSocket, sendcan: messaging.PubSocket, bus: int, timeout: float = 1, debug: bool = True) -> set[EcuAddrBusType]: addr_list = [0x700 + i for i in range(256)] + [0x18da00f1 + (i << 8) for i in range(256)] - queries: Set[EcuAddrBusType] = {(addr, None, bus) for addr in addr_list} + queries: set[EcuAddrBusType] = {(addr, None, bus) for addr in addr_list} responses = queries return get_ecu_addrs(logcan, sendcan, queries, responses, timeout=timeout, debug=debug) -def get_ecu_addrs(logcan: messaging.SubSocket, sendcan: messaging.PubSocket, queries: Set[EcuAddrBusType], - responses: Set[EcuAddrBusType], timeout: float = 1, debug: bool = False) -> Set[EcuAddrBusType]: - ecu_responses: Set[EcuAddrBusType] = set() # set((addr, subaddr, bus),) +def get_ecu_addrs(logcan: messaging.SubSocket, sendcan: messaging.PubSocket, queries: set[EcuAddrBusType], + responses: set[EcuAddrBusType], timeout: float = 1, debug: bool = False) -> set[EcuAddrBusType]: + ecu_responses: set[EcuAddrBusType] = set() # set((addr, subaddr, bus),) try: msgs = [make_tester_present_msg(addr, bus, subaddr) for addr, subaddr, bus in queries] diff --git a/selfdrive/car/ford/carstate.py b/selfdrive/car/ford/carstate.py index b7fa34a008..887c66fd51 100644 --- a/selfdrive/car/ford/carstate.py +++ b/selfdrive/car/ford/carstate.py @@ -18,7 +18,6 @@ class CarState(CarStateBase): self.shifter_values = can_define.dv["Gear_Shift_by_Wire_FD1"]["TrnRng_D_RqGsm"] self.vehicle_sensors_valid = False - self.unsupported_platform = False self.lkas_enabled = None self.prev_lkas_enabled = None @@ -32,12 +31,6 @@ class CarState(CarStateBase): self.prev_lkas_enabled = self.lkas_enabled self.buttonStatesPrev = self.buttonStates.copy() - # Ford Q3 hybrid variants experience a bug where a message from the PCM sends invalid checksums, - # this must be root-caused before enabling support. Ford Q4 hybrids do not have this problem. - # TrnAin_Tq_Actl and its quality flag are only set on ICE platform variants - self.unsupported_platform = (cp.vl["VehicleOperatingModes"]["TrnAinTq_D_Qf"] == 0 and - self.CP.carFingerprint not in CANFD_CAR) - # Occasionally on startup, the ABS module recalibrates the steering pinion offset, so we need to block engagement # The vehicle usually recovers out of this state within a minute of normal driving self.vehicle_sensors_valid = cp.vl["SteeringPinion_Data"]["StePinCompAnEst_D_Qf"] == 3 diff --git a/selfdrive/car/ford/interface.py b/selfdrive/car/ford/interface.py index fadae1d93c..844d600b73 100644 --- a/selfdrive/car/ford/interface.py +++ b/selfdrive/car/ford/interface.py @@ -3,7 +3,7 @@ from panda import Panda from openpilot.common.conversions import Conversions as CV from openpilot.selfdrive.car import get_safety_config, create_mads_event from openpilot.selfdrive.car.ford.fordcan import CanBus -from openpilot.selfdrive.car.ford.values import CANFD_CAR, CAR, Ecu, BUTTON_STATES +from openpilot.selfdrive.car.ford.values import CANFD_CAR, Ecu, BUTTON_STATES from openpilot.selfdrive.car.interfaces import CarInterfaceBase ButtonType = car.CarState.ButtonEvent.Type @@ -45,51 +45,6 @@ class CarInterface(CarInterfaceBase): if candidate in CANFD_CAR: ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_FORD_CANFD - if candidate == CAR.BRONCO_SPORT_MK1: - ret.wheelbase = 2.67 - ret.steerRatio = 17.7 - ret.mass = 1625 - - elif candidate == CAR.ESCAPE_MK4: - ret.wheelbase = 2.71 - ret.steerRatio = 16.7 - ret.mass = 1750 - - elif candidate == CAR.EXPLORER_MK6: - ret.wheelbase = 3.025 - ret.steerRatio = 16.8 - ret.mass = 2050 - - elif candidate == CAR.F_150_MK14: - # required trim only on SuperCrew - ret.wheelbase = 3.69 - ret.steerRatio = 17.0 - ret.mass = 2000 - - elif candidate == CAR.F_150_LIGHTNING_MK1: - # required trim only on SuperCrew - ret.wheelbase = 3.70 - ret.steerRatio = 16.9 - ret.mass = 2948 - - elif candidate == CAR.MUSTANG_MACH_E_MK1: - ret.wheelbase = 2.984 - ret.steerRatio = 17.0 # guess - ret.mass = 2200 - - elif candidate == CAR.FOCUS_MK4: - ret.wheelbase = 2.7 - ret.steerRatio = 15.0 - ret.mass = 1350 - - elif candidate == CAR.MAVERICK_MK1: - ret.wheelbase = 3.076 - ret.steerRatio = 17.0 - ret.mass = 1650 - - else: - raise ValueError(f"Unsupported car: {candidate}") - # Auto Transmission: 0x732 ECU or Gear_Shift_by_Wire_FD1 found_ecus = [fw.ecu for fw in car_fw] if Ecu.shiftByWire in found_ecus or 0x5A in fingerprint[CAN.main] or docs: @@ -169,8 +124,6 @@ class CarInterface(CarInterfaceBase): if not self.CS.vehicle_sensors_valid: events.add(car.CarEvent.EventName.vehicleSensorsInvalid) - if self.CS.unsupported_platform: - events.add(car.CarEvent.EventName.startupNoControl) ret.events = events.to_msg() diff --git a/selfdrive/car/ford/tests/test_ford.py b/selfdrive/car/ford/tests/test_ford.py index dff29629f6..2ad3f5db1b 100755 --- a/selfdrive/car/ford/tests/test_ford.py +++ b/selfdrive/car/ford/tests/test_ford.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import unittest from parameterized import parameterized -from typing import Dict, Iterable, Optional, Tuple +from collections.abc import Iterable import capnp @@ -19,6 +19,7 @@ ECU_ADDRESSES = { Ecu.fwdCamera: 0x706, # Image Processing Module A (IPMA) Ecu.engine: 0x7E0, # Powertrain Control Module (PCM) Ecu.shiftByWire: 0x732, # Gear Shift Module (GSM) + Ecu.debug: 0x7D0, # Accessory Protocol Interface Module (APIM) } @@ -50,7 +51,7 @@ class TestFordFW(unittest.TestCase): self.assertIsNone(subaddr, "Unexpected ECU subaddress") @parameterized.expand(FW_VERSIONS.items()) - def test_fw_versions(self, car_model: str, fw_versions: Dict[Tuple[capnp.lib.capnp._EnumModule, int, Optional[int]], Iterable[bytes]]): + def test_fw_versions(self, car_model: str, fw_versions: dict[tuple[capnp.lib.capnp._EnumModule, int, int | None], Iterable[bytes]]): for (ecu, addr, subaddr), fws in fw_versions.items(): self.assertIn(ecu, ECU_FW_CORE, "Unexpected ECU") self.assertEqual(addr, ECU_ADDRESSES[ecu], "ECU address mismatch") diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py index 3a7a635533..e54c72bc67 100644 --- a/selfdrive/car/ford/values.py +++ b/selfdrive/car/ford/values.py @@ -1,13 +1,12 @@ -from collections import defaultdict -from dataclasses import dataclass -from enum import Enum, StrEnum -from typing import Dict, List, Union +from dataclasses import dataclass, field +from enum import Enum +import panda.python.uds as uds from cereal import car -from openpilot.selfdrive.car import AngleRateLimit, dbc_dict +from openpilot.selfdrive.car import AngleRateLimit, CarSpecs, dbc_dict, DbcDict, PlatformConfig, Platforms from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarInfo, CarParts, Column, \ Device -from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries +from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16 Ecu = car.CarParams.Ecu @@ -50,33 +49,11 @@ BUTTON_STATES = { } -class CAR(StrEnum): - BRONCO_SPORT_MK1 = "FORD BRONCO SPORT 1ST GEN" - ESCAPE_MK4 = "FORD ESCAPE 4TH GEN" - EXPLORER_MK6 = "FORD EXPLORER 6TH GEN" - F_150_MK14 = "FORD F-150 14TH GEN" - FOCUS_MK4 = "FORD FOCUS 4TH GEN" - MAVERICK_MK1 = "FORD MAVERICK 1ST GEN" - F_150_LIGHTNING_MK1 = "FORD F-150 LIGHTNING 1ST GEN" - MUSTANG_MACH_E_MK1 = "FORD MUSTANG MACH-E 1ST GEN" - - -CANFD_CAR = {CAR.F_150_MK14, CAR.F_150_LIGHTNING_MK1, CAR.MUSTANG_MACH_E_MK1} - - class RADAR: DELPHI_ESR = 'ford_fusion_2018_adas' DELPHI_MRR = 'FORD_CADS' -DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict("ford_lincoln_base_pt", RADAR.DELPHI_MRR)) - -# F-150 radar is not yet supported -DBC[CAR.F_150_MK14] = dbc_dict("ford_lincoln_base_pt", None) -DBC[CAR.F_150_LIGHTNING_MK1] = dbc_dict("ford_lincoln_base_pt", None) -DBC[CAR.MUSTANG_MACH_E_MK1] = dbc_dict("ford_lincoln_base_pt", None) - - class Footnote(Enum): FOCUS = CarFootnote( "Refers only to the Focus Mk4 (C519) available in Europe/China/Taiwan/Australasia, not the Focus Mk3 (C346) in " + @@ -97,25 +74,109 @@ class FordCarInfo(CarInfo): self.car_parts = CarParts([Device.threex, harness]) -CAR_INFO: Dict[str, Union[CarInfo, List[CarInfo]]] = { - CAR.BRONCO_SPORT_MK1: FordCarInfo("Ford Bronco Sport 2021-22"), - CAR.ESCAPE_MK4: [ - FordCarInfo("Ford Escape 2020-22"), - FordCarInfo("Ford Kuga 2020-22", "Adaptive Cruise Control with Lane Centering"), - ], - CAR.EXPLORER_MK6: [ - FordCarInfo("Ford Explorer 2020-23"), - FordCarInfo("Lincoln Aviator 2020-21", "Co-Pilot360 Plus"), - ], - CAR.F_150_MK14: FordCarInfo("Ford F-150 2023", "Co-Pilot360 Active 2.0"), - CAR.F_150_LIGHTNING_MK1: FordCarInfo("Ford F-150 Lightning 2021-23", "Co-Pilot360 Active 2.0"), - CAR.MUSTANG_MACH_E_MK1: FordCarInfo("Ford Mustang Mach-E 2021-23", "Co-Pilot360 Active 2.0"), - CAR.FOCUS_MK4: FordCarInfo("Ford Focus 2018", "Adaptive Cruise Control with Lane Centering", footnotes=[Footnote.FOCUS]), - CAR.MAVERICK_MK1: [ - FordCarInfo("Ford Maverick 2022", "LARIAT Luxury"), - FordCarInfo("Ford Maverick 2023", "Co-Pilot360 Assist"), - ], -} +@dataclass(frozen=True) +class FordPlatformConfig(PlatformConfig): + dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('ford_lincoln_base_pt', RADAR.DELPHI_MRR)) + + +class CAR(Platforms): + BRONCO_SPORT_MK1 = FordPlatformConfig( + "FORD BRONCO SPORT 1ST GEN", + FordCarInfo("Ford Bronco Sport 2021-22"), + specs=CarSpecs(mass=1625, wheelbase=2.67, steerRatio=17.7), + ) + ESCAPE_MK4 = FordPlatformConfig( + "FORD ESCAPE 4TH GEN", + [ + FordCarInfo("Ford Escape 2020-22"), + FordCarInfo("Ford Escape Hybrid 2020-22"), + FordCarInfo("Ford Escape Plug-in Hybrid 2020-22"), + FordCarInfo("Ford Kuga 2020-22", "Adaptive Cruise Control with Lane Centering"), + FordCarInfo("Ford Kuga Hybrid 2020-22", "Adaptive Cruise Control with Lane Centering"), + FordCarInfo("Ford Kuga Plug-in Hybrid 2020-22", "Adaptive Cruise Control with Lane Centering"), + ], + specs=CarSpecs(mass=1750, wheelbase=2.71, steerRatio=16.7), + ) + EXPLORER_MK6 = FordPlatformConfig( + "FORD EXPLORER 6TH GEN", + [ + FordCarInfo("Ford Explorer 2020-23"), + FordCarInfo("Ford Explorer Hybrid 2020-23"), # Limited and Platinum only + FordCarInfo("Lincoln Aviator 2020-23", "Co-Pilot360 Plus"), + FordCarInfo("Lincoln Aviator Plug-in Hybrid 2020-23", "Co-Pilot360 Plus"), # Grand Touring only + ], + specs=CarSpecs(mass=2050, wheelbase=3.025, steerRatio=16.8), + ) + F_150_MK14 = FordPlatformConfig( + "FORD F-150 14TH GEN", + [ + FordCarInfo("Ford F-150 2023", "Co-Pilot360 Active 2.0"), + FordCarInfo("Ford F-150 Hybrid 2023", "Co-Pilot360 Active 2.0"), + ], + dbc_dict=dbc_dict('ford_lincoln_base_pt', None), + specs=CarSpecs(mass=2000, wheelbase=3.69, steerRatio=17.0), + ) + F_150_LIGHTNING_MK1 = FordPlatformConfig( + "FORD F-150 LIGHTNING 1ST GEN", + FordCarInfo("Ford F-150 Lightning 2021-23", "Co-Pilot360 Active 2.0"), + dbc_dict=dbc_dict('ford_lincoln_base_pt', None), + specs=CarSpecs(mass=2948, wheelbase=3.70, steerRatio=16.9), + ) + FOCUS_MK4 = FordPlatformConfig( + "FORD FOCUS 4TH GEN", + [ + FordCarInfo("Ford Focus 2018", "Adaptive Cruise Control with Lane Centering", footnotes=[Footnote.FOCUS]), + FordCarInfo("Ford Focus Hybrid 2018", "Adaptive Cruise Control with Lane Centering", footnotes=[Footnote.FOCUS]), # mHEV only + ], + specs=CarSpecs(mass=1350, wheelbase=2.7, steerRatio=15.0), + ) + MAVERICK_MK1 = FordPlatformConfig( + "FORD MAVERICK 1ST GEN", + [ + FordCarInfo("Ford Maverick 2022", "LARIAT Luxury"), + FordCarInfo("Ford Maverick Hybrid 2022", "LARIAT Luxury"), + FordCarInfo("Ford Maverick 2023", "Co-Pilot360 Assist"), + FordCarInfo("Ford Maverick Hybrid 2023", "Co-Pilot360 Assist"), + ], + specs=CarSpecs(mass=1650, wheelbase=3.076, steerRatio=17.0), + ) + MUSTANG_MACH_E_MK1 = FordPlatformConfig( + "FORD MUSTANG MACH-E 1ST GEN", + FordCarInfo("Ford Mustang Mach-E 2021-23", "Co-Pilot360 Active 2.0"), + dbc_dict=dbc_dict('ford_lincoln_base_pt', None), + specs=CarSpecs(mass=2200, wheelbase=2.984, steerRatio=17.0), # TODO: check steer ratio + ) + + +CANFD_CAR = {CAR.F_150_MK14, CAR.F_150_LIGHTNING_MK1, CAR.MUSTANG_MACH_E_MK1} + + +DATA_IDENTIFIER_FORD_ASBUILT = 0xDE00 + +ASBUILT_BLOCKS: list[tuple[int, list]] = [ + (1, [Ecu.debug, Ecu.fwdCamera, Ecu.eps]), + (2, [Ecu.abs, Ecu.debug, Ecu.eps]), + (3, [Ecu.abs, Ecu.debug, Ecu.eps]), + (4, [Ecu.debug, Ecu.fwdCamera]), + (5, [Ecu.debug]), + (6, [Ecu.debug]), + (7, [Ecu.debug]), + (8, [Ecu.debug]), + (9, [Ecu.debug]), + (16, [Ecu.debug, Ecu.fwdCamera]), + (18, [Ecu.fwdCamera]), + (20, [Ecu.fwdCamera]), + (21, [Ecu.fwdCamera]), +] + + +def ford_asbuilt_block_request(block_id: int): + return bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + p16(DATA_IDENTIFIER_FORD_ASBUILT + block_id - 1) + + +def ford_asbuilt_block_response(block_id: int): + return bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + p16(DATA_IDENTIFIER_FORD_ASBUILT + block_id - 1) + FW_QUERY_CONFIG = FwQueryConfig( requests=[ @@ -124,18 +185,31 @@ FW_QUERY_CONFIG = FwQueryConfig( Request( [StdQueries.TESTER_PRESENT_REQUEST, StdQueries.MANUFACTURER_SOFTWARE_VERSION_REQUEST], [StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.MANUFACTURER_SOFTWARE_VERSION_RESPONSE], + whitelist_ecus=[Ecu.abs, Ecu.debug, Ecu.engine, Ecu.eps, Ecu.fwdCamera, Ecu.fwdRadar, Ecu.shiftByWire], logging=True, ), Request( [StdQueries.TESTER_PRESENT_REQUEST, StdQueries.MANUFACTURER_SOFTWARE_VERSION_REQUEST], [StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.MANUFACTURER_SOFTWARE_VERSION_RESPONSE], + whitelist_ecus=[Ecu.abs, Ecu.debug, Ecu.engine, Ecu.eps, Ecu.fwdCamera, Ecu.fwdRadar, Ecu.shiftByWire], bus=0, auxiliary=True, ), + *[Request( + [StdQueries.TESTER_PRESENT_REQUEST, ford_asbuilt_block_request(block_id)], + [StdQueries.TESTER_PRESENT_RESPONSE, ford_asbuilt_block_response(block_id)], + whitelist_ecus=ecus, + bus=0, + logging=True, + ) for block_id, ecus in ASBUILT_BLOCKS], ], extra_ecus=[ - # We are unlikely to get a response from the PCM from behind the gateway - (Ecu.engine, 0x7e0, None), - (Ecu.shiftByWire, 0x732, None), + (Ecu.engine, 0x7e0, None), # Powertrain Control Module + # Note: We are unlikely to get a response from behind the gateway + (Ecu.shiftByWire, 0x732, None), # Gear Shift Module + (Ecu.debug, 0x7d0, None), # Accessory Protocol Interface Module ], ) + +CAR_INFO = CAR.create_carinfo_map() +DBC = CAR.create_dbc_map() diff --git a/selfdrive/car/fw_query_definitions.py b/selfdrive/car/fw_query_definitions.py index 36e6794d2c..80492b4177 100755 --- a/selfdrive/car/fw_query_definitions.py +++ b/selfdrive/car/fw_query_definitions.py @@ -3,16 +3,16 @@ import capnp import copy from dataclasses import dataclass, field import struct -from typing import Callable, Dict, List, Optional, Set, Tuple +from collections.abc import Callable import panda.python.uds as uds -AddrType = Tuple[int, Optional[int]] -EcuAddrBusType = Tuple[int, Optional[int], int] -EcuAddrSubAddr = Tuple[int, int, Optional[int]] +AddrType = tuple[int, int | None] +EcuAddrBusType = tuple[int, int | None, int] +EcuAddrSubAddr = tuple[int, int, int | None] -LiveFwVersions = Dict[AddrType, Set[bytes]] -OfflineFwVersions = Dict[str, Dict[EcuAddrSubAddr, List[bytes]]] +LiveFwVersions = dict[AddrType, set[bytes]] +OfflineFwVersions = dict[str, dict[EcuAddrSubAddr, list[bytes]]] # A global list of addresses we will only ever consider for VIN responses # engine, hybrid controller, Ford abs, Hyundai CAN FD cluster, 29-bit engine, PGM-FI @@ -71,9 +71,9 @@ class StdQueries: @dataclass class Request: - request: List[bytes] - response: List[bytes] - whitelist_ecus: List[int] = field(default_factory=list) + request: list[bytes] + response: list[bytes] + whitelist_ecus: list[int] = field(default_factory=list) rx_offset: int = 0x8 bus: int = 1 # Whether this query should be run on the first auxiliary panda (CAN FD cars for example) @@ -86,15 +86,15 @@ class Request: @dataclass class FwQueryConfig: - requests: List[Request] + requests: list[Request] # TODO: make this automatic and remove hardcoded lists, or do fingerprinting with ecus # Overrides and removes from essential ecus for specific models and ecus (exact matching) - non_essential_ecus: Dict[capnp.lib.capnp._EnumModule, List[str]] = field(default_factory=dict) + non_essential_ecus: dict[capnp.lib.capnp._EnumModule, list[str]] = field(default_factory=dict) # Ecus added for data collection, not to be fingerprinted on - extra_ecus: List[Tuple[capnp.lib.capnp._EnumModule, int, Optional[int]]] = field(default_factory=list) + extra_ecus: list[tuple[capnp.lib.capnp._EnumModule, int, int | None]] = field(default_factory=list) # Function a brand can implement to provide better fuzzy matching. Takes in FW versions, # returns set of candidates. Only will match if one candidate is returned - match_fw_to_car_fuzzy: Optional[Callable[[LiveFwVersions, OfflineFwVersions], Set[str]]] = None + match_fw_to_car_fuzzy: Callable[[LiveFwVersions, OfflineFwVersions], set[str]] | None = None def __post_init__(self): for i in range(len(self.requests)): diff --git a/selfdrive/car/fw_versions.py b/selfdrive/car/fw_versions.py index 6c02e69503..7673814195 100755 --- a/selfdrive/car/fw_versions.py +++ b/selfdrive/car/fw_versions.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 from collections import defaultdict -from typing import Any, DefaultDict, Dict, Iterator, List, Optional, Set, TypeVar +from typing import Any, TypeVar +from collections.abc import Iterator from tqdm import tqdm import capnp @@ -27,19 +28,19 @@ REQUESTS = [(brand, config, r) for brand, config in FW_QUERY_CONFIGS.items() for T = TypeVar('T') -def chunks(l: List[T], n: int = 128) -> Iterator[List[T]]: +def chunks(l: list[T], n: int = 128) -> Iterator[list[T]]: for i in range(0, len(l), n): yield l[i:i + n] -def is_brand(brand: str, filter_brand: Optional[str]) -> bool: +def is_brand(brand: str, filter_brand: str | None) -> bool: """Returns if brand matches filter_brand or no brand filter is specified""" return filter_brand is None or brand == filter_brand -def build_fw_dict(fw_versions: List[capnp.lib.capnp._DynamicStructBuilder], - filter_brand: Optional[str] = None) -> Dict[AddrType, Set[bytes]]: - fw_versions_dict: DefaultDict[AddrType, Set[bytes]] = defaultdict(set) +def build_fw_dict(fw_versions: list[capnp.lib.capnp._DynamicStructBuilder], + filter_brand: str = None) -> dict[AddrType, set[bytes]]: + fw_versions_dict: defaultdict[AddrType, set[bytes]] = defaultdict(set) for fw in fw_versions: if is_brand(fw.brand, filter_brand) and not fw.logging: sub_addr = fw.subAddress if fw.subAddress != 0 else None @@ -97,7 +98,7 @@ def match_fw_to_car_fuzzy(live_fw_versions, match_brand=None, log=True, exclude= return set() -def match_fw_to_car_exact(live_fw_versions, match_brand=None, log=True, extra_fw_versions=None) -> Set[str]: +def match_fw_to_car_exact(live_fw_versions, match_brand=None, log=True, extra_fw_versions=None) -> set[str]: """Do an exact FW match. Returns all cars that match the given FW versions for a list of "essential" ECUs. If an ECU is not considered essential the FW version can be missing to get a fingerprint, but if it's present it @@ -164,11 +165,11 @@ def match_fw_to_car(fw_versions, allow_exact=True, allow_fuzzy=True, log=True): return True, set() -def get_present_ecus(logcan, sendcan, num_pandas=1) -> Set[EcuAddrBusType]: +def get_present_ecus(logcan, sendcan, num_pandas=1) -> set[EcuAddrBusType]: params = Params() # queries are split by OBD multiplexing mode - queries: Dict[bool, List[List[EcuAddrBusType]]] = {True: [], False: []} - parallel_queries: Dict[bool, List[EcuAddrBusType]] = {True: [], False: []} + queries: dict[bool, list[list[EcuAddrBusType]]] = {True: [], False: []} + parallel_queries: dict[bool, list[EcuAddrBusType]] = {True: [], False: []} responses = set() for brand, config, r in REQUESTS: @@ -203,7 +204,7 @@ def get_present_ecus(logcan, sendcan, num_pandas=1) -> Set[EcuAddrBusType]: return ecu_responses -def get_brand_ecu_matches(ecu_rx_addrs: Set[EcuAddrBusType]) -> dict[str, set[AddrType]]: +def get_brand_ecu_matches(ecu_rx_addrs: set[EcuAddrBusType]) -> dict[str, set[AddrType]]: """Returns dictionary of brands and matches with ECUs in their FW versions""" brand_addrs = {brand: {(addr, subaddr) for _, addr, subaddr in config.get_all_ecus(VERSIONS[brand])} for @@ -231,7 +232,7 @@ def set_obd_multiplexing(params: Params, obd_multiplexing: bool): def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, num_pandas=1, debug=False, progress=False) -> \ - List[capnp.lib.capnp._DynamicStructBuilder]: + list[capnp.lib.capnp._DynamicStructBuilder]: """Queries for FW versions ordering brands by likelihood, breaks when exact match is found""" all_car_fw = [] @@ -254,7 +255,7 @@ def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, num_pand def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1, num_pandas=1, debug=False, progress=False) -> \ - List[capnp.lib.capnp._DynamicStructBuilder]: + list[capnp.lib.capnp._DynamicStructBuilder]: versions = VERSIONS.copy() params = Params() diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py index 24470a1a6b..701a0a8f81 100755 --- a/selfdrive/car/gm/interface.py +++ b/selfdrive/car/gm/interface.py @@ -152,11 +152,7 @@ class CarInterface(CarInterfaceBase): ret.longitudinalActuatorDelayUpperBound = 0.5 # large delay to initially start braking if candidate == CAR.VOLT: - ret.mass = 1607. - ret.wheelbase = 2.69 - ret.steerRatio = 17.7 # Stock 15.7, LiveParameters ret.tireStiffnessFactor = 0.469 # Stock Michelin Energy Saver A/S, LiveParameters - ret.centerToFront = ret.wheelbase * 0.45 # Volt Gen 1, TODO corner weigh ret.lateralTuning.pid.kpBP = [0., 40.] ret.lateralTuning.pid.kpV = [0., 0.17] @@ -165,61 +161,20 @@ class CarInterface(CarInterfaceBase): ret.lateralTuning.pid.kf = 1. # get_steer_feedforward_volt() ret.steerActuatorDelay = 0.2 - elif candidate == CAR.MALIBU: - ret.mass = 1496. - ret.wheelbase = 2.83 - ret.steerRatio = 15.8 - ret.centerToFront = ret.wheelbase * 0.4 # wild guess - - elif candidate == CAR.HOLDEN_ASTRA: - ret.mass = 1363. - ret.wheelbase = 2.662 - # Remaining parameters copied from Volt for now - ret.centerToFront = ret.wheelbase * 0.4 - ret.steerRatio = 15.7 - elif candidate == CAR.ACADIA: ret.minEnableSpeed = -1. # engage speed is decided by pcm - ret.mass = 4353. * CV.LB_TO_KG - ret.wheelbase = 2.86 - ret.steerRatio = 14.4 # end to end is 13.46 - ret.centerToFront = ret.wheelbase * 0.4 ret.steerActuatorDelay = 0.2 CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) elif candidate == CAR.BUICK_LACROSSE: - ret.mass = 1712. - ret.wheelbase = 2.91 - ret.steerRatio = 15.8 - ret.centerToFront = ret.wheelbase * 0.4 # wild guess CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) - elif candidate == CAR.BUICK_REGAL: - ret.mass = 3779. * CV.LB_TO_KG # (3849+3708)/2 - ret.wheelbase = 2.83 # 111.4 inches in meters - ret.steerRatio = 14.4 # guess for tourx - ret.centerToFront = ret.wheelbase * 0.4 # guess for tourx - - elif candidate == CAR.CADILLAC_ATS: - ret.mass = 1601. - ret.wheelbase = 2.78 - ret.steerRatio = 15.3 - ret.centerToFront = ret.wheelbase * 0.5 - elif candidate == CAR.ESCALADE: ret.minEnableSpeed = -1. # engage speed is decided by pcm - ret.mass = 5653. * CV.LB_TO_KG # (5552+5815)/2 - ret.wheelbase = 2.95 # 116 inches in meters - ret.steerRatio = 17.3 - ret.centerToFront = ret.wheelbase * 0.5 CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) elif candidate in (CAR.ESCALADE_ESV, CAR.ESCALADE_ESV_2019): ret.minEnableSpeed = -1. # engage speed is decided by pcm - ret.mass = 2739. - ret.wheelbase = 3.302 - ret.steerRatio = 17.3 - ret.centerToFront = ret.wheelbase * 0.5 ret.tireStiffnessFactor = 1.0 if candidate == CAR.ESCALADE_ESV: @@ -231,19 +186,11 @@ class CarInterface(CarInterfaceBase): CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) elif candidate == CAR.BOLT_EUV: - ret.mass = 1669. - ret.wheelbase = 2.63779 - ret.steerRatio = 16.8 - ret.centerToFront = ret.wheelbase * 0.4 ret.tireStiffnessFactor = 1.0 ret.steerActuatorDelay = 0.2 CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) elif candidate == CAR.SILVERADO: - ret.mass = 2450. - ret.wheelbase = 3.75 - ret.steerRatio = 16.3 - ret.centerToFront = ret.wheelbase * 0.5 ret.tireStiffnessFactor = 1.0 # On the Bolt, the ECM and camera independently check that you are either above 5 kph or at a stop # with foot on brake to allow engagement, but this platform only has that check in the camera. @@ -253,17 +200,9 @@ class CarInterface(CarInterfaceBase): CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) elif candidate == CAR.EQUINOX: - ret.mass = 3500. * CV.LB_TO_KG - ret.wheelbase = 2.72 - ret.steerRatio = 14.4 - ret.centerToFront = ret.wheelbase * 0.4 CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) elif candidate == CAR.TRAILBLAZER: - ret.mass = 1345. - ret.wheelbase = 2.64 - ret.steerRatio = 16.8 - ret.centerToFront = ret.wheelbase * 0.4 ret.tireStiffnessFactor = 1.0 ret.steerActuatorDelay = 0.2 CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) diff --git a/selfdrive/car/gm/values.py b/selfdrive/car/gm/values.py index 8188ad4e6e..35fdf3fb92 100644 --- a/selfdrive/car/gm/values.py +++ b/selfdrive/car/gm/values.py @@ -1,10 +1,8 @@ -from collections import defaultdict -from dataclasses import dataclass -from enum import Enum, StrEnum -from typing import Dict, List, Union +from dataclasses import dataclass, field +from enum import Enum from cereal import car -from openpilot.selfdrive.car import dbc_dict +from openpilot.selfdrive.car import dbc_dict, PlatformConfig, DbcDict, Platforms, CarSpecs from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarInfo, CarParts, Column from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries @@ -62,23 +60,6 @@ class CarControllerParams: self.BRAKE_LOOKUP_V = [self.MAX_BRAKE, 0.] -class CAR(StrEnum): - HOLDEN_ASTRA = "HOLDEN ASTRA RS-V BK 2017" - VOLT = "CHEVROLET VOLT PREMIER 2017" - CADILLAC_ATS = "CADILLAC ATS Premium Performance 2018" - MALIBU = "CHEVROLET MALIBU PREMIER 2017" - ACADIA = "GMC ACADIA DENALI 2018" - BUICK_LACROSSE = "BUICK LACROSSE 2017" - BUICK_REGAL = "BUICK REGAL ESSENCE 2018" - ESCALADE = "CADILLAC ESCALADE 2017" - ESCALADE_ESV = "CADILLAC ESCALADE ESV 2016" - ESCALADE_ESV_2019 = "CADILLAC ESCALADE ESV 2019" - BOLT_EUV = "CHEVROLET BOLT EUV 2022" - SILVERADO = "CHEVROLET SILVERADO 1500 2020" - EQUINOX = "CHEVROLET EQUINOX 2019" - TRAILBLAZER = "CHEVROLET TRAILBLAZER 2021" - - class Footnote(Enum): OBD_II = CarFootnote( 'Requires a community built ASCM harness. ' + @@ -98,28 +79,88 @@ class GMCarInfo(CarInfo): self.footnotes.append(Footnote.OBD_II) -CAR_INFO: Dict[str, Union[GMCarInfo, List[GMCarInfo]]] = { - CAR.HOLDEN_ASTRA: GMCarInfo("Holden Astra 2017"), - CAR.VOLT: GMCarInfo("Chevrolet Volt 2017-18", min_enable_speed=0, video_link="https://youtu.be/QeMCN_4TFfQ"), - CAR.CADILLAC_ATS: GMCarInfo("Cadillac ATS Premium Performance 2018"), - CAR.MALIBU: GMCarInfo("Chevrolet Malibu Premier 2017"), - CAR.ACADIA: GMCarInfo("GMC Acadia 2018", video_link="https://www.youtube.com/watch?v=0ZN6DdsBUZo"), - CAR.BUICK_LACROSSE: GMCarInfo("Buick LaCrosse 2017-19", "Driver Confidence Package 2"), - CAR.BUICK_REGAL: GMCarInfo("Buick Regal Essence 2018"), - CAR.ESCALADE: GMCarInfo("Cadillac Escalade 2017", "Driver Assist Package"), - CAR.ESCALADE_ESV: GMCarInfo("Cadillac Escalade ESV 2016", "Adaptive Cruise Control (ACC) & LKAS"), - CAR.ESCALADE_ESV_2019: GMCarInfo("Cadillac Escalade ESV 2019", "Adaptive Cruise Control (ACC) & LKAS"), - CAR.BOLT_EUV: [ - GMCarInfo("Chevrolet Bolt EUV 2022-23", "Premier or Premier Redline Trim without Super Cruise Package", video_link="https://youtu.be/xvwzGMUA210"), - GMCarInfo("Chevrolet Bolt EV 2022-23", "2LT Trim with Adaptive Cruise Control Package"), - ], - CAR.SILVERADO: [ - GMCarInfo("Chevrolet Silverado 1500 2020-21", "Safety Package II"), - GMCarInfo("GMC Sierra 1500 2020-21", "Driver Alert Package II", video_link="https://youtu.be/5HbNoBLzRwE"), - ], - CAR.EQUINOX: GMCarInfo("Chevrolet Equinox 2019-22"), - CAR.TRAILBLAZER: GMCarInfo("Chevrolet Trailblazer 2021-22"), -} +@dataclass(frozen=True) +class GMPlatformConfig(PlatformConfig): + dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis')) + + +class CAR(Platforms): + HOLDEN_ASTRA = GMPlatformConfig( + "HOLDEN ASTRA RS-V BK 2017", + GMCarInfo("Holden Astra 2017"), + specs=CarSpecs(mass=1363, wheelbase=2.662, steerRatio=15.7, centerToFrontRatio=0.4), + ) + VOLT = GMPlatformConfig( + "CHEVROLET VOLT PREMIER 2017", + GMCarInfo("Chevrolet Volt 2017-18", min_enable_speed=0, video_link="https://youtu.be/QeMCN_4TFfQ"), + specs=CarSpecs(mass=1607, wheelbase=2.69, steerRatio=17.7, centerToFrontRatio=0.45), + ) + CADILLAC_ATS = GMPlatformConfig( + "CADILLAC ATS Premium Performance 2018", + GMCarInfo("Cadillac ATS Premium Performance 2018"), + specs=CarSpecs(mass=1601, wheelbase=2.78, steerRatio=15.3), + ) + MALIBU = GMPlatformConfig( + "CHEVROLET MALIBU PREMIER 2017", + GMCarInfo("Chevrolet Malibu Premier 2017"), + specs=CarSpecs(mass=1496, wheelbase=2.83, steerRatio=15.8, centerToFrontRatio=0.4), + ) + ACADIA = GMPlatformConfig( + "GMC ACADIA DENALI 2018", + GMCarInfo("GMC Acadia 2018", video_link="https://www.youtube.com/watch?v=0ZN6DdsBUZo"), + specs=CarSpecs(mass=1975, wheelbase=2.86, steerRatio=14.4, centerToFrontRatio=0.4), + ) + BUICK_LACROSSE = GMPlatformConfig( + "BUICK LACROSSE 2017", + GMCarInfo("Buick LaCrosse 2017-19", "Driver Confidence Package 2"), + specs=CarSpecs(mass=1712, wheelbase=2.91, steerRatio=15.8, centerToFrontRatio=0.4), + ) + BUICK_REGAL = GMPlatformConfig( + "BUICK REGAL ESSENCE 2018", + GMCarInfo("Buick Regal Essence 2018"), + specs=CarSpecs(mass=1714, wheelbase=2.83, steerRatio=14.4, centerToFrontRatio=0.4), + ) + ESCALADE = GMPlatformConfig( + "CADILLAC ESCALADE 2017", + GMCarInfo("Cadillac Escalade 2017", "Driver Assist Package"), + specs=CarSpecs(mass=2564, wheelbase=2.95, steerRatio=17.3), + ) + ESCALADE_ESV = GMPlatformConfig( + "CADILLAC ESCALADE ESV 2016", + GMCarInfo("Cadillac Escalade ESV 2016", "Adaptive Cruise Control (ACC) & LKAS"), + specs=CarSpecs(mass=2739, wheelbase=3.302, steerRatio=17.3), + ) + ESCALADE_ESV_2019 = GMPlatformConfig( + "CADILLAC ESCALADE ESV 2019", + GMCarInfo("Cadillac Escalade ESV 2019", "Adaptive Cruise Control (ACC) & LKAS"), + specs=ESCALADE_ESV.specs, + ) + BOLT_EUV = GMPlatformConfig( + "CHEVROLET BOLT EUV 2022", + [ + GMCarInfo("Chevrolet Bolt EUV 2022-23", "Premier or Premier Redline Trim without Super Cruise Package", video_link="https://youtu.be/xvwzGMUA210"), + GMCarInfo("Chevrolet Bolt EV 2022-23", "2LT Trim with Adaptive Cruise Control Package"), + ], + specs=CarSpecs(mass=1669, wheelbase=2.63779, steerRatio=16.8, centerToFrontRatio=0.4), + ) + SILVERADO = GMPlatformConfig( + "CHEVROLET SILVERADO 1500 2020", + [ + GMCarInfo("Chevrolet Silverado 1500 2020-21", "Safety Package II"), + GMCarInfo("GMC Sierra 1500 2020-21", "Driver Alert Package II", video_link="https://youtu.be/5HbNoBLzRwE"), + ], + specs=CarSpecs(mass=2450, wheelbase=3.75, steerRatio=16.3), + ) + EQUINOX = GMPlatformConfig( + "CHEVROLET EQUINOX 2019", + GMCarInfo("Chevrolet Equinox 2019-22"), + specs=CarSpecs(mass=1588, wheelbase=2.72, steerRatio=14.4, centerToFrontRatio=0.4), + ) + TRAILBLAZER = GMPlatformConfig( + "CHEVROLET TRAILBLAZER 2021", + GMCarInfo("Chevrolet Trailblazer 2021-22"), + specs=CarSpecs(mass=1345, wheelbase=2.64, steerRatio=16.8, centerToFrontRatio=0.4), + ) class CruiseButtons: @@ -181,11 +222,12 @@ FW_QUERY_CONFIG = FwQueryConfig( extra_ecus=[(Ecu.fwdCamera, 0x24b, None)], ) -DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis')) - EV_CAR = {CAR.VOLT, CAR.BOLT_EUV} # We're integrated at the camera with VOACC on these cars (instead of ASCM w/ OBD-II harness) CAMERA_ACC_CAR = {CAR.BOLT_EUV, CAR.SILVERADO, CAR.EQUINOX, CAR.TRAILBLAZER} STEER_THRESHOLD = 1.0 + +CAR_INFO = CAR.create_carinfo_map() +DBC = CAR.create_dbc_map() diff --git a/selfdrive/car/honda/values.py b/selfdrive/car/honda/values.py index 3a7c706cd0..a787ac4945 100644 --- a/selfdrive/car/honda/values.py +++ b/selfdrive/car/honda/values.py @@ -1,6 +1,5 @@ from dataclasses import dataclass from enum import Enum, IntFlag, StrEnum -from typing import Dict, List, Optional, Union from cereal import car from openpilot.common.conversions import Conversions as CV @@ -126,7 +125,7 @@ class HondaCarInfo(CarInfo): self.car_parts = CarParts.common([CarHarness.nidec]) -CAR_INFO: Dict[str, Optional[Union[HondaCarInfo, List[HondaCarInfo]]]] = { +CAR_INFO: dict[str, HondaCarInfo | list[HondaCarInfo] | None] = { CAR.ACCORD: [ HondaCarInfo("Honda Accord 2018-22", "All", video_link="https://www.youtube.com/watch?v=mrUwlj3Mi58", min_steer_speed=3. * CV.MPH_TO_MS), HondaCarInfo("Honda Inspire 2018", "All", min_steer_speed=3. * CV.MPH_TO_MS), @@ -220,6 +219,8 @@ FW_QUERY_CONFIG = FwQueryConfig( Ecu.combinationMeter: [CAR.CIVIC_BOSCH, CAR.CRV_5G], Ecu.gateway: [CAR.CIVIC_BOSCH, CAR.CRV_5G], Ecu.electricBrakeBooster: [CAR.CIVIC_BOSCH, CAR.CRV_5G], + Ecu.shiftByWire: [CAR.ACCORD], # existence correlates with transmission type for ICE + Ecu.hud: [CAR.ACCORD, CAR.ACCORDH], # existence correlates with trim level }, extra_ecus=[ # The only other ECU on PT bus accessible by camera on radarless Civic diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index ff7bd560cb..54669f3ae9 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -1,7 +1,6 @@ import re from dataclasses import dataclass from enum import Enum, IntFlag, StrEnum -from typing import Dict, List, Optional, Set, Tuple, Union from cereal import car from panda.python import uds @@ -173,7 +172,7 @@ class HyundaiCarInfo(CarInfo): self.footnotes.insert(0, Footnote.CANFD) -CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { +CAR_INFO: dict[str, HyundaiCarInfo | list[HyundaiCarInfo] | None] = { CAR.AZERA_6TH_GEN: HyundaiCarInfo("Hyundai Azera 2022", "All", car_parts=CarParts.common([CarHarness.hyundai_k])), CAR.AZERA_HEV_6TH_GEN: [ HyundaiCarInfo("Hyundai Azera Hybrid 2019", "All", car_parts=CarParts.common([CarHarness.hyundai_c])), @@ -341,7 +340,7 @@ class Buttons: CANCEL = 4 # on newer models, this is a pause/resume button -def get_platform_codes(fw_versions: List[bytes]) -> Set[Tuple[bytes, Optional[bytes]]]: +def get_platform_codes(fw_versions: list[bytes]) -> set[tuple[bytes, bytes | None]]: # Returns unique, platform-specific identification codes for a set of versions codes = set() # (code-Optional[part], date) for fw in fw_versions: @@ -360,12 +359,12 @@ def get_platform_codes(fw_versions: List[bytes]) -> Set[Tuple[bytes, Optional[by return codes -def match_fw_to_car_fuzzy(live_fw_versions, offline_fw_versions) -> Set[str]: +def match_fw_to_car_fuzzy(live_fw_versions, offline_fw_versions) -> set[str]: # Non-electric CAN FD platforms often do not have platform code specifiers needed # to distinguish between hybrid and ICE. All EVs so far are either exclusively # electric or specify electric in the platform code. fuzzy_platform_blacklist = {str(c) for c in (CANFD_CAR - EV_CAR - CANFD_FUZZY_WHITELIST)} - candidates: Set[str] = set() + candidates: set[str] = set() for candidate, fws in offline_fw_versions.items(): # Keep track of ECUs which pass all checks (platform codes, within date range) diff --git a/selfdrive/car/interfaces.py b/selfdrive/car/interfaces.py index 4c006c798c..9433bcf5eb 100644 --- a/selfdrive/car/interfaces.py +++ b/selfdrive/car/interfaces.py @@ -7,7 +7,8 @@ import tomllib from abc import abstractmethod, ABC from difflib import SequenceMatcher from enum import StrEnum -from typing import Any, Dict, Optional, Tuple, List, Callable, NamedTuple +from typing import Any, NamedTuple +from collections.abc import Callable from cereal import car from openpilot.common.basedir import BASEDIR @@ -17,6 +18,7 @@ from openpilot.common.numpy_fast import clip from openpilot.common.params import Params from openpilot.common.realtime import DT_CTRL from openpilot.selfdrive.car import apply_hysteresis, gen_empty_fingerprint, scale_rot_inertia, scale_tire_stiffness, STD_CARGO_KG +from openpilot.selfdrive.car.values import Platform from openpilot.selfdrive.controls.lib.desire_helper import LANE_CHANGE_SPEED_MIN from openpilot.selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX, V_CRUISE_UNSET, get_friction from openpilot.selfdrive.controls.lib.events import Events @@ -258,15 +260,25 @@ class CarInterfaceBase(ABC): return ACCEL_MIN, ACCEL_MAX @classmethod - def get_non_essential_params(cls, candidate: str): + def get_non_essential_params(cls, candidate: Platform): """ Parameters essential to controlling the car may be incomplete or wrong without FW versions or fingerprints. """ return cls.get_params(candidate, gen_empty_fingerprint(), list(), False, False) @classmethod - def get_params(cls, candidate: str, fingerprint: Dict[int, Dict[int, int]], car_fw: List[car.CarParams.CarFw], experimental_long: bool, docs: bool): + def get_params(cls, candidate: Platform, fingerprint: dict[int, dict[int, int]], car_fw: list[car.CarParams.CarFw], experimental_long: bool, docs: bool): ret = CarInterfaceBase.get_std_params(candidate) + + if hasattr(candidate, "config"): + if candidate.config.specs is not None: + ret.mass = candidate.config.specs.mass + ret.wheelbase = candidate.config.specs.wheelbase + ret.steerRatio = candidate.config.specs.steerRatio + ret.centerToFront = ret.wheelbase * candidate.config.specs.centerToFrontRatio + ret.minEnableSpeed = candidate.config.specs.minEnableSpeed + ret.minSteerSpeed = candidate.config.specs.minSteerSpeed + ret = cls._get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs) params = Params() @@ -302,8 +314,8 @@ class CarInterfaceBase(ABC): @staticmethod @abstractmethod - def _get_params(ret: car.CarParams, candidate: str, fingerprint: Dict[int, Dict[int, int]], - car_fw: List[car.CarParams.CarFw], experimental_long: bool, docs: bool): + def _get_params(ret: car.CarParams, candidate: Platform, fingerprint: dict[int, dict[int, int]], + car_fw: list[car.CarParams.CarFw], experimental_long: bool, docs: bool): raise NotImplementedError @staticmethod @@ -395,7 +407,7 @@ class CarInterfaceBase(ABC): def _update(self, c: car.CarControl) -> car.CarState: pass - def update(self, c: car.CarControl, can_strings: List[bytes]) -> car.CarState: + def update(self, c: car.CarControl, can_strings: list[bytes]) -> car.CarState: # parse can for cp in self.can_parsers: if cp is not None: @@ -429,7 +441,7 @@ class CarInterfaceBase(ABC): return reader @abstractmethod - def apply(self, c: car.CarControl, now_nanos: int) -> Tuple[car.CarControl.Actuators, List[bytes]]: + def apply(self, c: car.CarControl, now_nanos: int) -> tuple[car.CarControl.Actuators, list[bytes]]: pass def create_common_events(self, cs_out, c, extra_gears=None, pcm_enable=True, allow_enable=True, @@ -860,11 +872,11 @@ class CarStateBase(ABC): return customStockLong @staticmethod - def parse_gear_shifter(gear: Optional[str]) -> car.CarState.GearShifter: + def parse_gear_shifter(gear: str | None) -> car.CarState.GearShifter: if gear is None: return GearShifter.unknown - d: Dict[str, car.CarState.GearShifter] = { + d: dict[str, car.CarState.GearShifter] = { 'P': GearShifter.park, 'PARK': GearShifter.park, 'R': GearShifter.reverse, 'REVERSE': GearShifter.reverse, 'N': GearShifter.neutral, 'NEUTRAL': GearShifter.neutral, @@ -901,7 +913,7 @@ INTERFACE_ATTR_FILE = { # interface-specific helpers -def get_interface_attr(attr: str, combine_brands: bool = False, ignore_none: bool = False) -> Dict[str | StrEnum, Any]: +def get_interface_attr(attr: str, combine_brands: bool = False, ignore_none: bool = False) -> dict[str | StrEnum, Any]: # read all the folders in selfdrive/car and return a dict where: # - keys are all the car models or brand names # - values are attr values from all car folders @@ -934,7 +946,7 @@ class NanoFFModel: self.load_weights(platform) def load_weights(self, platform: str): - with open(self.weights_loc, 'r') as fob: + with open(self.weights_loc) as fob: self.weights = {k: np.array(v) for k, v in json.load(fob)[platform].items()} def relu(self, x: np.ndarray): @@ -949,7 +961,7 @@ class NanoFFModel: x = np.dot(x, self.weights['w_4']) + self.weights['b_4'] return x - def predict(self, x: List[float], do_sample: bool = False): + def predict(self, x: list[float], do_sample: bool = False): x = self.forward(np.array(x)) if do_sample: pred = np.random.laplace(x[0], np.exp(x[1]) / self.weights['temperature']) diff --git a/selfdrive/car/isotp_parallel_query.py b/selfdrive/car/isotp_parallel_query.py index 678fe9ea46..8fdc747e9e 100644 --- a/selfdrive/car/isotp_parallel_query.py +++ b/selfdrive/car/isotp_parallel_query.py @@ -12,7 +12,7 @@ from panda.python.uds import CanClient, IsoTpMessage, FUNCTIONAL_ADDRS, get_rx_a class IsoTpParallelQuery: def __init__(self, sendcan: messaging.PubSocket, logcan: messaging.SubSocket, bus: int, addrs: list[int] | list[AddrType], request: list[bytes], response: list[bytes], response_offset: int = 0x8, - functional_addrs: list[int] | None = None, debug: bool = False, response_pending_timeout: float = 10) -> None: + functional_addrs: list[int] = None, debug: bool = False, response_pending_timeout: float = 10) -> None: self.sendcan = sendcan self.logcan = logcan self.bus = bus diff --git a/selfdrive/car/mazda/values.py b/selfdrive/car/mazda/values.py index 8330ed805b..332870e960 100644 --- a/selfdrive/car/mazda/values.py +++ b/selfdrive/car/mazda/values.py @@ -1,6 +1,5 @@ from dataclasses import dataclass, field from enum import StrEnum -from typing import Dict, List, Union from cereal import car from openpilot.selfdrive.car import dbc_dict @@ -49,7 +48,7 @@ class MazdaCarInfo(CarInfo): car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.mazda])) -CAR_INFO: Dict[str, Union[MazdaCarInfo, List[MazdaCarInfo]]] = { +CAR_INFO: dict[str, MazdaCarInfo | list[MazdaCarInfo]] = { CAR.CX5: MazdaCarInfo("Mazda CX-5 2017-21"), CAR.CX9: MazdaCarInfo("Mazda CX-9 2016-20"), CAR.MAZDA3: MazdaCarInfo("Mazda 3 2017-18"), diff --git a/selfdrive/car/mock/values.py b/selfdrive/car/mock/values.py index c6c96579b4..e75665c98f 100644 --- a/selfdrive/car/mock/values.py +++ b/selfdrive/car/mock/values.py @@ -1,5 +1,4 @@ from enum import StrEnum -from typing import Dict, List, Optional, Union from openpilot.selfdrive.car.docs_definitions import CarInfo @@ -8,6 +7,6 @@ class CAR(StrEnum): MOCK = 'mock' -CAR_INFO: Dict[str, Optional[Union[CarInfo, List[CarInfo]]]] = { +CAR_INFO: dict[str, CarInfo | list[CarInfo] | None] = { CAR.MOCK: None, } diff --git a/selfdrive/car/nissan/interface.py b/selfdrive/car/nissan/interface.py index fc0c3cbaf2..09d4614248 100644 --- a/selfdrive/car/nissan/interface.py +++ b/selfdrive/car/nissan/interface.py @@ -19,25 +19,13 @@ class CarInterface(CarInterfaceBase): ret.steerLimitTimer = 1.0 ret.steerActuatorDelay = 0.1 - ret.steerRatio = 17 ret.steerControlType = car.CarParams.SteerControlType.angle ret.radarUnavailable = True - if candidate in (CAR.ROGUE, CAR.XTRAIL): - ret.mass = 1610 - ret.wheelbase = 2.705 - ret.centerToFront = ret.wheelbase * 0.44 - elif candidate in (CAR.LEAF, CAR.LEAF_IC): - ret.mass = 1610 - ret.wheelbase = 2.705 - ret.centerToFront = ret.wheelbase * 0.44 - elif candidate == CAR.ALTIMA: + if candidate == CAR.ALTIMA: # Altima has EPS on C-CAN unlike the others that have it on V-CAN ret.safetyConfigs[0].safetyParam |= Panda.FLAG_NISSAN_ALT_EPS_BUS - ret.mass = 1492 - ret.wheelbase = 2.824 - ret.centerToFront = ret.wheelbase * 0.44 return ret diff --git a/selfdrive/car/nissan/values.py b/selfdrive/car/nissan/values.py index b0a3d74e1a..f48b2ecc04 100644 --- a/selfdrive/car/nissan/values.py +++ b/selfdrive/car/nissan/values.py @@ -1,10 +1,8 @@ from dataclasses import dataclass, field -from enum import StrEnum -from typing import Dict, List, Optional, Union from cereal import car from panda.python import uds -from openpilot.selfdrive.car import AngleRateLimit, dbc_dict +from openpilot.selfdrive.car import AngleRateLimit, CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict from openpilot.selfdrive.car.docs_definitions import CarInfo, CarHarness, CarParts from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries @@ -21,29 +19,57 @@ class CarControllerParams: pass -class CAR(StrEnum): - XTRAIL = "NISSAN X-TRAIL 2017" - LEAF = "NISSAN LEAF 2018" - # Leaf with ADAS ECU found behind instrument cluster instead of glovebox - # Currently the only known difference between them is the inverted seatbelt signal. - LEAF_IC = "NISSAN LEAF 2018 Instrument Cluster" - ROGUE = "NISSAN ROGUE 2019" - ALTIMA = "NISSAN ALTIMA 2020" - - @dataclass class NissanCarInfo(CarInfo): package: str = "ProPILOT Assist" car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.nissan_a])) -CAR_INFO: Dict[str, Optional[Union[NissanCarInfo, List[NissanCarInfo]]]] = { - CAR.XTRAIL: NissanCarInfo("Nissan X-Trail 2017"), - CAR.LEAF: NissanCarInfo("Nissan Leaf 2018-23", video_link="https://youtu.be/vaMbtAh_0cY"), - CAR.LEAF_IC: NissanCarInfo("Nissan Leaf Instrument Cluster 2018-23", video_link="https://youtu.be/vaMbtAh_0cY"), # same platforms - CAR.ROGUE: NissanCarInfo("Nissan Rogue 2018-20"), - CAR.ALTIMA: NissanCarInfo("Nissan Altima 2019-20", car_parts=CarParts.common([CarHarness.nissan_b])), -} +@dataclass(frozen=True) +class NissanCarSpecs(CarSpecs): + centerToFrontRatio: float = 0.44 + steerRatio: float = 17. + + +@dataclass(frozen=True) +class NissanPlaformConfig(PlatformConfig): + dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('nissan_x_trail_2017_generated', None)) + + +class CAR(Platforms): + XTRAIL = NissanPlaformConfig( + "NISSAN X-TRAIL 2017", + NissanCarInfo("Nissan X-Trail 2017"), + specs=NissanCarSpecs(mass=1610, wheelbase=2.705) + ) + LEAF = NissanPlaformConfig( + "NISSAN LEAF 2018", + NissanCarInfo("Nissan Leaf 2018-23", video_link="https://youtu.be/vaMbtAh_0cY"), + dbc_dict=dbc_dict('nissan_leaf_2018_generated', None), + specs=NissanCarSpecs(mass=1610, wheelbase=2.705) + ) + # Leaf with ADAS ECU found behind instrument cluster instead of glovebox + # Currently the only known difference between them is the inverted seatbelt signal. + LEAF_IC = NissanPlaformConfig( + "NISSAN LEAF 2018 Instrument Cluster", + NissanCarInfo("Nissan Leaf Instrument Cluster 2018-23", video_link="https://youtu.be/vaMbtAh_0cY"), + dbc_dict=dbc_dict('nissan_leaf_2018_generated', None), + specs=NissanCarSpecs(mass=1610, wheelbase=2.705) + ) + ROGUE = NissanPlaformConfig( + "NISSAN ROGUE 2019", + NissanCarInfo("Nissan Rogue 2018-20"), + specs=NissanCarSpecs(mass=1610, wheelbase=2.705) + ) + ALTIMA = NissanPlaformConfig( + "NISSAN ALTIMA 2020", + NissanCarInfo("Nissan Altima 2019-20", car_parts=CarParts.common([CarHarness.nissan_b])), + specs=NissanCarSpecs(mass=1492, wheelbase=2.824) + ) + + +CAR_INFO = CAR.create_carinfo_map() +DBC = CAR.create_dbc_map() # Default diagnostic session NISSAN_DIAGNOSTIC_REQUEST_KWP = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL, 0x81]) @@ -89,11 +115,3 @@ FW_QUERY_CONFIG = FwQueryConfig( ), ]], ) - -DBC = { - CAR.XTRAIL: dbc_dict('nissan_x_trail_2017_generated', None), - CAR.LEAF: dbc_dict('nissan_leaf_2018_generated', None), - CAR.LEAF_IC: dbc_dict('nissan_leaf_2018_generated', None), - CAR.ROGUE: dbc_dict('nissan_x_trail_2017_generated', None), - CAR.ALTIMA: dbc_dict('nissan_x_trail_2017_generated', None), -} diff --git a/selfdrive/car/subaru/interface.py b/selfdrive/car/subaru/interface.py index 069338bb4b..e6a1094753 100644 --- a/selfdrive/car/subaru/interface.py +++ b/selfdrive/car/subaru/interface.py @@ -1,6 +1,7 @@ from cereal import car from panda import Panda from openpilot.selfdrive.car import get_safety_config, create_mads_event +from openpilot.selfdrive.car.values import Platform from openpilot.selfdrive.car.disable_ecu import disable_ecu from openpilot.selfdrive.car.interfaces import CarInterfaceBase from openpilot.selfdrive.car.subaru.values import CAR, GLOBAL_ES_ADDR, LKAS_ANGLE, GLOBAL_GEN2, PREGLOBAL_CARS, HYBRID_CARS, SubaruFlags, SubaruFlagsSP @@ -13,7 +14,7 @@ GearShifter = car.CarState.GearShifter class CarInterface(CarInterfaceBase): @staticmethod - def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs): + def _get_params(ret, candidate: Platform, fingerprint, car_fw, experimental_long, docs): ret.carName = "subaru" ret.radarUnavailable = True # for HYBRID CARS to be upstreamed, we need: @@ -50,10 +51,6 @@ class CarInterface(CarInterfaceBase): CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) if candidate in (CAR.ASCENT, CAR.ASCENT_2023): - ret.mass = 2031. - ret.wheelbase = 2.89 - ret.centerToFront = ret.wheelbase * 0.5 - ret.steerRatio = 13.5 ret.steerActuatorDelay = 0.3 # end-to-end angle controller ret.lateralTuning.init('pid') ret.lateralTuning.pid.kf = 0.00003 @@ -61,10 +58,6 @@ class CarInterface(CarInterfaceBase): ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.0025, 0.1], [0.00025, 0.01]] elif candidate == CAR.IMPREZA: - ret.mass = 1568. - ret.wheelbase = 2.67 - ret.centerToFront = ret.wheelbase * 0.5 - ret.steerRatio = 15 ret.steerActuatorDelay = 0.4 # end-to-end angle controller ret.lateralTuning.init('pid') ret.lateralTuning.pid.kf = 0.00005 @@ -79,58 +72,31 @@ class CarInterface(CarInterfaceBase): ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.133, 0.2], [0.0133, 0.02]] elif candidate == CAR.IMPREZA_2020: - ret.mass = 1480. - ret.wheelbase = 2.67 - ret.centerToFront = ret.wheelbase * 0.5 - ret.steerRatio = 17 # learned, 14 stock ret.lateralTuning.init('pid') ret.lateralTuning.pid.kf = 0.00005 ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 14., 23.], [0., 14., 23.]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.045, 0.042, 0.20], [0.04, 0.035, 0.045]] elif candidate == CAR.CROSSTREK_HYBRID: - ret.mass = 1668. - ret.wheelbase = 2.67 - ret.centerToFront = ret.wheelbase * 0.5 - ret.steerRatio = 17 ret.steerActuatorDelay = 0.1 elif candidate in (CAR.FORESTER, CAR.FORESTER_2022, CAR.FORESTER_HYBRID): - ret.mass = 1568. - ret.wheelbase = 2.67 - ret.centerToFront = ret.wheelbase * 0.5 - ret.steerRatio = 17 # learned, 14 stock ret.lateralTuning.init('pid') ret.lateralTuning.pid.kf = 0.000038 ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 14., 23.], [0., 14., 23.]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.01, 0.065, 0.2], [0.001, 0.015, 0.025]] elif candidate in (CAR.OUTBACK, CAR.LEGACY, CAR.OUTBACK_2023): - ret.mass = 1568. - ret.wheelbase = 2.67 - ret.centerToFront = ret.wheelbase * 0.5 - ret.steerRatio = 17 ret.steerActuatorDelay = 0.1 elif candidate in (CAR.FORESTER_PREGLOBAL, CAR.OUTBACK_PREGLOBAL_2018): ret.safetyConfigs[0].safetyParam |= Panda.FLAG_SUBARU_PREGLOBAL_REVERSED_DRIVER_TORQUE # Outback 2018-2019 and Forester have reversed driver torque signal - ret.mass = 1568 - ret.wheelbase = 2.67 - ret.centerToFront = ret.wheelbase * 0.5 - ret.steerRatio = 20 # learned, 14 stock elif candidate == CAR.LEGACY_PREGLOBAL: - ret.mass = 1568 - ret.wheelbase = 2.67 - ret.centerToFront = ret.wheelbase * 0.5 - ret.steerRatio = 12.5 # 14.5 stock ret.steerActuatorDelay = 0.15 elif candidate == CAR.OUTBACK_PREGLOBAL: - ret.mass = 1568 - ret.wheelbase = 2.67 - ret.centerToFront = ret.wheelbase * 0.5 - ret.steerRatio = 20 # learned, 14 stock + pass else: raise ValueError(f"unknown car: {candidate}") diff --git a/selfdrive/car/subaru/values.py b/selfdrive/car/subaru/values.py index 44d2543a84..0d761747b9 100644 --- a/selfdrive/car/subaru/values.py +++ b/selfdrive/car/subaru/values.py @@ -1,11 +1,10 @@ from dataclasses import dataclass, field -from enum import Enum, IntFlag, StrEnum +from enum import Enum, IntFlag from panda import Panda -from typing import Dict, List, Union from cereal import car from panda.python import uds -from openpilot.selfdrive.car import dbc_dict +from openpilot.selfdrive.car import CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarInfo, CarParts, Tool, Column from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16 @@ -75,27 +74,6 @@ class CanBus: camera = 2 -class CAR(StrEnum): - # Global platform - ASCENT = "SUBARU ASCENT LIMITED 2019" - ASCENT_2023 = "SUBARU ASCENT 2023" - IMPREZA = "SUBARU IMPREZA LIMITED 2019" - IMPREZA_2020 = "SUBARU IMPREZA SPORT 2020" - FORESTER = "SUBARU FORESTER 2019" - OUTBACK = "SUBARU OUTBACK 6TH GEN" - CROSSTREK_HYBRID = "SUBARU CROSSTREK HYBRID 2020" - FORESTER_HYBRID = "SUBARU FORESTER HYBRID 2020" - LEGACY = "SUBARU LEGACY 7TH GEN" - FORESTER_2022 = "SUBARU FORESTER 2022" - OUTBACK_2023 = "SUBARU OUTBACK 7TH GEN" - - # Pre-global - FORESTER_PREGLOBAL = "SUBARU FORESTER 2017 - 2018" - LEGACY_PREGLOBAL = "SUBARU LEGACY 2015 - 2018" - OUTBACK_PREGLOBAL = "SUBARU OUTBACK 2015 - 2017" - OUTBACK_PREGLOBAL_2018 = "SUBARU OUTBACK 2018 - 2019" - - class Footnote(Enum): GLOBAL = CarFootnote( "In the non-US market, openpilot requires the car to come equipped with EyeSight with Lane Keep Assistance.", @@ -109,7 +87,7 @@ class Footnote(Enum): class SubaruCarInfo(CarInfo): package: str = "EyeSight Driver Assistance" car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.subaru_a])) - footnotes: List[Enum] = field(default_factory=lambda: [Footnote.GLOBAL]) + footnotes: list[Enum] = field(default_factory=lambda: [Footnote.GLOBAL]) def init_make(self, CP: car.CarParams): self.car_parts.parts.extend([Tool.socket_8mm_deep, Tool.pry_tool]) @@ -117,32 +95,107 @@ class SubaruCarInfo(CarInfo): if CP.experimentalLongitudinalAvailable: self.footnotes.append(Footnote.EXP_LONG) -CAR_INFO: Dict[str, Union[SubaruCarInfo, List[SubaruCarInfo]]] = { - CAR.ASCENT: SubaruCarInfo("Subaru Ascent 2019-21", "All"), - CAR.OUTBACK: SubaruCarInfo("Subaru Outback 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b])), - CAR.LEGACY: SubaruCarInfo("Subaru Legacy 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b])), - CAR.IMPREZA: [ - SubaruCarInfo("Subaru Impreza 2017-19"), - SubaruCarInfo("Subaru Crosstrek 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"), - SubaruCarInfo("Subaru XV 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"), - ], - CAR.IMPREZA_2020: [ - SubaruCarInfo("Subaru Impreza 2020-22"), - SubaruCarInfo("Subaru Crosstrek 2020-23"), - SubaruCarInfo("Subaru XV 2020-21"), - ], + +@dataclass(frozen=True) +class SubaruPlatformConfig(PlatformConfig): + dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('subaru_global_2017_generated', None)) + + +class CAR(Platforms): + # Global platform + ASCENT = SubaruPlatformConfig( + "SUBARU ASCENT LIMITED 2019", + SubaruCarInfo("Subaru Ascent 2019-21", "All"), + specs=CarSpecs(mass=2031, wheelbase=2.89, steerRatio=13.5), + ) + OUTBACK = SubaruPlatformConfig( + "SUBARU OUTBACK 6TH GEN", + SubaruCarInfo("Subaru Outback 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b])), + specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=17), + ) + LEGACY = SubaruPlatformConfig( + "SUBARU LEGACY 7TH GEN", + SubaruCarInfo("Subaru Legacy 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b])), + specs=OUTBACK.specs, + ) + IMPREZA = SubaruPlatformConfig( + "SUBARU IMPREZA LIMITED 2019", + [ + SubaruCarInfo("Subaru Impreza 2017-19"), + SubaruCarInfo("Subaru Crosstrek 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"), + SubaruCarInfo("Subaru XV 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"), + ], + specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=15), + ) + IMPREZA_2020 = SubaruPlatformConfig( + "SUBARU IMPREZA SPORT 2020", + [ + SubaruCarInfo("Subaru Impreza 2020-22"), + SubaruCarInfo("Subaru Crosstrek 2020-23"), + SubaruCarInfo("Subaru XV 2020-21"), + ], + specs=CarSpecs(mass=1480, wheelbase=2.67, steerRatio=17), + ) # TODO: is there an XV and Impreza too? - CAR.CROSSTREK_HYBRID: SubaruCarInfo("Subaru Crosstrek Hybrid 2020", car_parts=CarParts.common([CarHarness.subaru_b])), - CAR.FORESTER_HYBRID: SubaruCarInfo("Subaru Forester Hybrid 2020"), - CAR.FORESTER: SubaruCarInfo("Subaru Forester 2019-21", "All"), - CAR.FORESTER_PREGLOBAL: SubaruCarInfo("Subaru Forester 2017-18"), - CAR.LEGACY_PREGLOBAL: SubaruCarInfo("Subaru Legacy 2015-18"), - CAR.OUTBACK_PREGLOBAL: SubaruCarInfo("Subaru Outback 2015-17"), - CAR.OUTBACK_PREGLOBAL_2018: SubaruCarInfo("Subaru Outback 2018-19"), - CAR.FORESTER_2022: SubaruCarInfo("Subaru Forester 2022-24", "All", car_parts=CarParts.common([CarHarness.subaru_c])), - CAR.OUTBACK_2023: SubaruCarInfo("Subaru Outback 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d])), - CAR.ASCENT_2023: SubaruCarInfo("Subaru Ascent 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d])), -} + CROSSTREK_HYBRID = SubaruPlatformConfig( + "SUBARU CROSSTREK HYBRID 2020", + SubaruCarInfo("Subaru Crosstrek Hybrid 2020", car_parts=CarParts.common([CarHarness.subaru_b])), + dbc_dict('subaru_global_2020_hybrid_generated', None), + specs=CarSpecs(mass=1668, wheelbase=2.67, steerRatio=17), + ) + FORESTER = SubaruPlatformConfig( + "SUBARU FORESTER 2019", + SubaruCarInfo("Subaru Forester 2019-21", "All"), + specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=17), + ) + FORESTER_HYBRID = SubaruPlatformConfig( + "SUBARU FORESTER HYBRID 2020", + SubaruCarInfo("Subaru Forester Hybrid 2020"), + dbc_dict('subaru_global_2020_hybrid_generated', None), + specs=FORESTER.specs, + ) + # Pre-global + FORESTER_PREGLOBAL = SubaruPlatformConfig( + "SUBARU FORESTER 2017 - 2018", + SubaruCarInfo("Subaru Forester 2017-18"), + dbc_dict('subaru_forester_2017_generated', None), + specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=20), + ) + LEGACY_PREGLOBAL = SubaruPlatformConfig( + "SUBARU LEGACY 2015 - 2018", + SubaruCarInfo("Subaru Legacy 2015-18"), + dbc_dict('subaru_outback_2015_generated', None), + specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=12.5), + ) + OUTBACK_PREGLOBAL = SubaruPlatformConfig( + "SUBARU OUTBACK 2015 - 2017", + SubaruCarInfo("Subaru Outback 2015-17"), + dbc_dict('subaru_outback_2015_generated', None), + specs=FORESTER_PREGLOBAL.specs, + ) + OUTBACK_PREGLOBAL_2018 = SubaruPlatformConfig( + "SUBARU OUTBACK 2018 - 2019", + SubaruCarInfo("Subaru Outback 2018-19"), + dbc_dict('subaru_outback_2019_generated', None), + specs=FORESTER_PREGLOBAL.specs, + ) + # Angle LKAS + FORESTER_2022 = SubaruPlatformConfig( + "SUBARU FORESTER 2022", + SubaruCarInfo("Subaru Forester 2022-24", "All", car_parts=CarParts.common([CarHarness.subaru_c])), + specs=FORESTER.specs, + ) + OUTBACK_2023 = SubaruPlatformConfig( + "SUBARU OUTBACK 7TH GEN", + SubaruCarInfo("Subaru Outback 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d])), + specs=OUTBACK.specs, + ) + ASCENT_2023 = SubaruPlatformConfig( + "SUBARU ASCENT 2023", + SubaruCarInfo("Subaru Ascent 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d])), + specs=ASCENT.specs, + ) + LKAS_ANGLE = {CAR.FORESTER_2022, CAR.OUTBACK_2023, CAR.ASCENT_2023} GLOBAL_GEN2 = {CAR.OUTBACK, CAR.LEGACY, CAR.OUTBACK_2023, CAR.ASCENT_2023} @@ -193,20 +246,5 @@ FW_QUERY_CONFIG = FwQueryConfig( } ) -DBC = { - CAR.ASCENT: dbc_dict('subaru_global_2017_generated', None), - CAR.ASCENT_2023: dbc_dict('subaru_global_2017_generated', None), - CAR.IMPREZA: dbc_dict('subaru_global_2017_generated', None), - CAR.IMPREZA_2020: dbc_dict('subaru_global_2017_generated', None), - CAR.FORESTER: dbc_dict('subaru_global_2017_generated', None), - CAR.FORESTER_2022: dbc_dict('subaru_global_2017_generated', None), - CAR.OUTBACK: dbc_dict('subaru_global_2017_generated', None), - CAR.FORESTER_HYBRID: dbc_dict('subaru_global_2020_hybrid_generated', None), - CAR.CROSSTREK_HYBRID: dbc_dict('subaru_global_2020_hybrid_generated', None), - CAR.OUTBACK_2023: dbc_dict('subaru_global_2017_generated', None), - CAR.LEGACY: dbc_dict('subaru_global_2017_generated', None), - CAR.FORESTER_PREGLOBAL: dbc_dict('subaru_forester_2017_generated', None), - CAR.LEGACY_PREGLOBAL: dbc_dict('subaru_outback_2015_generated', None), - CAR.OUTBACK_PREGLOBAL: dbc_dict('subaru_outback_2015_generated', None), - CAR.OUTBACK_PREGLOBAL_2018: dbc_dict('subaru_outback_2019_generated', None), -} +CAR_INFO = CAR.create_carinfo_map() +DBC = CAR.create_dbc_map() diff --git a/selfdrive/car/tesla/values.py b/selfdrive/car/tesla/values.py index 12877f1344..2a51d15da8 100644 --- a/selfdrive/car/tesla/values.py +++ b/selfdrive/car/tesla/values.py @@ -1,6 +1,5 @@ from collections import namedtuple from enum import StrEnum -from typing import Dict, List, Union from cereal import car from openpilot.selfdrive.car import AngleRateLimit, dbc_dict @@ -17,7 +16,7 @@ class CAR(StrEnum): AP2_MODELS = 'TESLA AP2 MODEL S' -CAR_INFO: Dict[str, Union[CarInfo, List[CarInfo]]] = { +CAR_INFO: dict[str, CarInfo | list[CarInfo]] = { CAR.AP1_MODELS: CarInfo("Tesla AP1 Model S", "All"), CAR.AP2_MODELS: CarInfo("Tesla AP2 Model S", "All"), } diff --git a/selfdrive/car/tests/routes.py b/selfdrive/car/tests/routes.py index f7ae4e038e..9c14c0d252 100755 --- a/selfdrive/car/tests/routes.py +++ b/selfdrive/car/tests/routes.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -from typing import NamedTuple, Optional +from typing import NamedTuple from openpilot.selfdrive.car.chrysler.values import CAR as CHRYSLER from openpilot.selfdrive.car.gm.values import CAR as GM @@ -29,8 +29,8 @@ non_tested_cars = [ class CarTestRoute(NamedTuple): route: str - car_model: Optional[str] - segment: Optional[int] = None + car_model: str | None + segment: int | None = None routes = [ diff --git a/selfdrive/car/tests/test_docs.py b/selfdrive/car/tests/test_docs.py index d9d67fe87d..0cafb508f7 100755 --- a/selfdrive/car/tests/test_docs.py +++ b/selfdrive/car/tests/test_docs.py @@ -20,7 +20,7 @@ class TestCarDocs(unittest.TestCase): def test_generator(self): generated_cars_md = generate_cars_md(self.all_cars, CARS_MD_TEMPLATE) - with open(CARS_MD_OUT, "r") as f: + with open(CARS_MD_OUT) as f: current_cars_md = f.read() self.assertEqual(generated_cars_md, current_cars_md, @@ -45,7 +45,7 @@ class TestCarDocs(unittest.TestCase): all_car_info_platforms = get_interface_attr("CAR_INFO", combine_brands=True).keys() for platform in sorted(interfaces.keys()): with self.subTest(platform=platform): - self.assertTrue(platform in all_car_info_platforms, "Platform: {} doesn't exist in CarInfo".format(platform)) + self.assertTrue(platform in all_car_info_platforms, f"Platform: {platform} doesn't exist in CarInfo") def test_naming_conventions(self): # Asserts market-standard car naming conventions by brand diff --git a/selfdrive/car/tests/test_fingerprints.py b/selfdrive/car/tests/test_fingerprints.py index 61e9a4d165..34f30bc703 100755 --- a/selfdrive/car/tests/test_fingerprints.py +++ b/selfdrive/car/tests/test_fingerprints.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 import os import sys -from typing import Dict, List from openpilot.common.basedir import BASEDIR @@ -64,7 +63,7 @@ def check_can_ignition_conflicts(fingerprints, brands): if __name__ == "__main__": fingerprints = _get_fingerprints() - fingerprints_flat: List[Dict] = [] + fingerprints_flat: list[dict] = [] car_names = [] brand_names = [] for brand in fingerprints: diff --git a/selfdrive/car/tests/test_fw_fingerprint.py b/selfdrive/car/tests/test_fw_fingerprint.py index e1ebd5cf18..88c7225f22 100755 --- a/selfdrive/car/tests/test_fw_fingerprint.py +++ b/selfdrive/car/tests/test_fw_fingerprint.py @@ -263,13 +263,13 @@ class TestFwFingerprintTiming(unittest.TestCase): print(f'get_vin {name} case, query time={self.total_time / self.N} seconds') def test_fw_query_timing(self): - total_ref_time = {1: 6.05, 2: 6.95} + total_ref_time = {1: 7.8, 2: 8.7} brand_ref_times = { 1: { 'gm': 0.5, 'body': 0.1, 'chrysler': 0.3, - 'ford': 0.2, + 'ford': 1.5, 'honda': 0.55, 'hyundai': 1.05, 'mazda': 0.1, @@ -277,10 +277,10 @@ class TestFwFingerprintTiming(unittest.TestCase): 'subaru': 0.45, 'tesla': 0.2, 'toyota': 1.6, - 'volkswagen': 0.2, + 'volkswagen': 0.65, }, 2: { - 'ford': 0.3, + 'ford': 1.6, 'hyundai': 1.85, } } diff --git a/selfdrive/car/tests/test_lateral_limits.py b/selfdrive/car/tests/test_lateral_limits.py index 10e24ff4c5..083cdd5a5e 100755 --- a/selfdrive/car/tests/test_lateral_limits.py +++ b/selfdrive/car/tests/test_lateral_limits.py @@ -3,7 +3,6 @@ from collections import defaultdict import importlib from parameterized import parameterized_class import sys -from typing import DefaultDict, Dict import unittest from openpilot.common.realtime import DT_CTRL @@ -29,7 +28,7 @@ ABOVE_LIMITS_CARS = [ SUBARU.OUTBACK, ] -car_model_jerks: DefaultDict[str, Dict[str, float]] = defaultdict(dict) +car_model_jerks: defaultdict[str, dict[str, float]] = defaultdict(dict) @parameterized_class('car_model', [(c,) for c in sorted(CAR_MODELS)]) diff --git a/selfdrive/car/tests/test_models.py b/selfdrive/car/tests/test_models.py index 3592fd0baa..2b29c14f72 100755 --- a/selfdrive/car/tests/test_models.py +++ b/selfdrive/car/tests/test_models.py @@ -8,7 +8,6 @@ import unittest from collections import defaultdict, Counter import hypothesis.strategies as st from hypothesis import Phase, given, settings -from typing import List, Optional, Tuple from parameterized import parameterized_class from cereal import messaging, log, car @@ -40,7 +39,7 @@ MAX_EXAMPLES = int(os.environ.get("MAX_EXAMPLES", "300")) CI = os.environ.get("CI", None) is not None -def get_test_cases() -> List[Tuple[str, Optional[CarTestRoute]]]: +def get_test_cases() -> list[tuple[str, CarTestRoute | None]]: # build list of test cases test_cases = [] if not len(INTERNAL_SEG_LIST): @@ -50,7 +49,7 @@ def get_test_cases() -> List[Tuple[str, Optional[CarTestRoute]]]: for i, c in enumerate(sorted(all_known_cars())): if i % NUM_JOBS == JOB_ID: - test_cases.extend(sorted((c.value, r) for r in routes_by_car.get(c, (None,)))) + test_cases.extend(sorted((c, r) for r in routes_by_car.get(c, (None,)))) else: segment_list = read_segment_list(os.path.join(BASEDIR, INTERNAL_SEG_LIST)) @@ -65,14 +64,14 @@ def get_test_cases() -> List[Tuple[str, Optional[CarTestRoute]]]: @pytest.mark.slow @pytest.mark.shared_download_cache class TestCarModelBase(unittest.TestCase): - car_model: Optional[str] = None - test_route: Optional[CarTestRoute] = None + car_model: str | None = None + test_route: CarTestRoute | None = None test_route_on_bucket: bool = True # whether the route is on the preserved CI bucket - can_msgs: List[capnp.lib.capnp._DynamicStructReader] + can_msgs: list[capnp.lib.capnp._DynamicStructReader] fingerprint: dict[int, dict[int, int]] - elm_frame: Optional[int] - car_safety_mode_frame: Optional[int] + elm_frame: int | None + car_safety_mode_frame: int | None @classmethod def get_testing_data_from_logreader(cls, lr): @@ -408,7 +407,7 @@ class TestCarModelBase(unittest.TestCase): controls_allowed_prev = False CS_prev = car.CarState.new_message() - checks = defaultdict(lambda: 0) + checks = defaultdict(int) controlsd = Controls(CI=self.CI) controlsd.initialized = True for idx, can in enumerate(self.can_msgs): diff --git a/selfdrive/car/toyota/fingerprints.py b/selfdrive/car/toyota/fingerprints.py index 12a1d46aaf..6b48408a10 100644 --- a/selfdrive/car/toyota/fingerprints.py +++ b/selfdrive/car/toyota/fingerprints.py @@ -573,6 +573,7 @@ FW_VERSIONS = { b'\x018821F6201400\x00\x00\x00\x00', ], (Ecu.fwdCamera, 0x750, 0x6d): [ + b'\x028646F12010C0\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', b'\x028646F12010D0\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', b'\x028646F1201100\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', b'\x028646F1201200\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', @@ -843,6 +844,7 @@ FW_VERSIONS = { b'8965B47023\x00\x00\x00\x00\x00\x00', b'8965B47050\x00\x00\x00\x00\x00\x00', b'8965B47060\x00\x00\x00\x00\x00\x00', + b'8965B47070\x00\x00\x00\x00\x00\x00', ], (Ecu.abs, 0x7b0, None): [ b'F152647290\x00\x00\x00\x00\x00\x00', @@ -1024,6 +1026,7 @@ FW_VERSIONS = { b'\x02896634A13000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'\x02896634A13001\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', b'\x02896634A13101\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', + b'\x02896634A13201\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', b'\x02896634A14001\x00\x00\x00\x00897CF1203001\x00\x00\x00\x00', b'\x02896634A14001\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', b'\x02896634A14101\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index 6eb1342d0c..6d745761ea 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -2,7 +2,6 @@ import re from collections import defaultdict from dataclasses import dataclass, field from enum import Enum, IntFlag, StrEnum -from typing import Dict, List, Set, Union from cereal import car from openpilot.common.conversions import Conversions as CV @@ -104,7 +103,7 @@ class ToyotaCarInfo(CarInfo): car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.toyota_a])) -CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = { +CAR_INFO: dict[str, ToyotaCarInfo | list[ToyotaCarInfo]] = { # Toyota CAR.ALPHARD_TSS2: [ ToyotaCarInfo("Toyota Alphard 2019-20"), @@ -257,7 +256,7 @@ STATIC_DSU_MSGS = [ ] -def get_platform_codes(fw_versions: List[bytes]) -> Dict[bytes, Set[bytes]]: +def get_platform_codes(fw_versions: list[bytes]) -> dict[bytes, set[bytes]]: # Returns sub versions in a dict so comparisons can be made within part-platform-major_version combos codes = defaultdict(set) # Optional[part]-platform-major_version: set of sub_version for fw in fw_versions: @@ -301,7 +300,7 @@ def get_platform_codes(fw_versions: List[bytes]) -> Dict[bytes, Set[bytes]]: return dict(codes) -def match_fw_to_car_fuzzy(live_fw_versions, offline_fw_versions) -> Set[str]: +def match_fw_to_car_fuzzy(live_fw_versions, offline_fw_versions) -> set[str]: candidates = set() for candidate, fws in offline_fw_versions.items(): diff --git a/selfdrive/car/values.py b/selfdrive/car/values.py new file mode 100644 index 0000000000..3e27870915 --- /dev/null +++ b/selfdrive/car/values.py @@ -0,0 +1,18 @@ +from typing import cast +from openpilot.selfdrive.car.body.values import CAR as BODY +from openpilot.selfdrive.car.chrysler.values import CAR as CHRYSLER +from openpilot.selfdrive.car.ford.values import CAR as FORD +from openpilot.selfdrive.car.gm.values import CAR as GM +from openpilot.selfdrive.car.honda.values import CAR as HONDA +from openpilot.selfdrive.car.hyundai.values import CAR as HYUNDAI +from openpilot.selfdrive.car.mazda.values import CAR as MAZDA +from openpilot.selfdrive.car.nissan.values import CAR as NISSAN +from openpilot.selfdrive.car.subaru.values import CAR as SUBARU +from openpilot.selfdrive.car.tesla.values import CAR as TESLA +from openpilot.selfdrive.car.toyota.values import CAR as TOYOTA +from openpilot.selfdrive.car.volkswagen.values import CAR as VOLKSWAGEN + +Platform = BODY | CHRYSLER | FORD | GM | HONDA | HYUNDAI | MAZDA | NISSAN | SUBARU | TESLA | TOYOTA | VOLKSWAGEN +BRANDS = [BODY, CHRYSLER, FORD, GM, HONDA, HYUNDAI, MAZDA, NISSAN, SUBARU, TESLA, TOYOTA, VOLKSWAGEN] + +PLATFORMS: dict[str, Platform] = {str(platform): platform for brand in BRANDS for platform in cast(list[Platform], brand)} diff --git a/selfdrive/car/volkswagen/interface.py b/selfdrive/car/volkswagen/interface.py index f46c0ce5e2..f43539ab55 100644 --- a/selfdrive/car/volkswagen/interface.py +++ b/selfdrive/car/volkswagen/interface.py @@ -1,10 +1,9 @@ from cereal import car from panda import Panda -from openpilot.common.conversions import Conversions as CV from openpilot.common.params import Params from openpilot.selfdrive.car import get_safety_config, create_mads_event from openpilot.selfdrive.car.interfaces import CarInterfaceBase -from openpilot.selfdrive.car.volkswagen.values import CAR, PQ_CARS, CANBUS, NetworkLocation, TransmissionType, GearShifter, VolkswagenFlags, \ +from openpilot.selfdrive.car.volkswagen.values import PQ_CARS, CANBUS, NetworkLocation, TransmissionType, GearShifter, VolkswagenFlags, \ BUTTON_STATES, VolkswagenFlagsSP ButtonType = car.CarState.ButtonEvent.Type @@ -76,14 +75,17 @@ class CarInterface(CarInterfaceBase): # Global lateral tuning defaults, can be overridden per-vehicle - ret.steerActuatorDelay = 0.1 ret.steerLimitTimer = 0.4 - ret.steerRatio = 15.6 # Let the params learner figure this out - ret.lateralTuning.pid.kpBP = [0.] - ret.lateralTuning.pid.kiBP = [0.] - ret.lateralTuning.pid.kf = 0.00006 - ret.lateralTuning.pid.kpV = [0.6] - ret.lateralTuning.pid.kiV = [0.2] + if candidate in PQ_CARS: + ret.steerActuatorDelay = 0.2 + CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) + else: + ret.steerActuatorDelay = 0.1 + ret.lateralTuning.pid.kpBP = [0.] + ret.lateralTuning.pid.kiBP = [0.] + ret.lateralTuning.pid.kf = 0.00006 + ret.lateralTuning.pid.kpV = [0.6] + ret.lateralTuning.pid.kiV = [0.2] # Global longitudinal tuning defaults, can be overridden per-vehicle @@ -113,125 +115,6 @@ class CarInterface(CarInterfaceBase): # Per-chassis tuning values, override tuning defaults here if desired - if candidate == CAR.ARTEON_MK1: - ret.mass = 1733 - ret.wheelbase = 2.84 - - elif candidate == CAR.ATLAS_MK1: - ret.mass = 2011 - ret.wheelbase = 2.98 - - elif candidate == CAR.CRAFTER_MK2: - ret.mass = 2100 - ret.wheelbase = 3.64 # SWB, LWB is 4.49, TBD how to detect difference - ret.minSteerSpeed = 50 * CV.KPH_TO_MS - - elif candidate == CAR.GOLF_MK7: - ret.mass = 1397 - ret.wheelbase = 2.62 - - elif candidate == CAR.JETTA_MK7: - ret.mass = 1328 - ret.wheelbase = 2.71 - - elif candidate == CAR.PASSAT_MK8: - ret.mass = 1551 - ret.wheelbase = 2.79 - - elif candidate == CAR.PASSAT_NMS: - ret.mass = 1503 - ret.wheelbase = 2.80 - ret.minEnableSpeed = 20 * CV.KPH_TO_MS # ACC "basic", no FtS - ret.minSteerSpeed = 50 * CV.KPH_TO_MS - ret.steerActuatorDelay = 0.2 - CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) - - elif candidate == CAR.POLO_MK6: - ret.mass = 1230 - ret.wheelbase = 2.55 - - elif candidate == CAR.SHARAN_MK2: - ret.mass = 1639 - ret.wheelbase = 2.92 - ret.minSteerSpeed = 50 * CV.KPH_TO_MS - ret.steerActuatorDelay = 0.2 - - elif candidate == CAR.TAOS_MK1: - ret.mass = 1498 - ret.wheelbase = 2.69 - - elif candidate == CAR.TCROSS_MK1: - ret.mass = 1150 - ret.wheelbase = 2.60 - - elif candidate == CAR.TIGUAN_MK2: - ret.mass = 1715 - ret.wheelbase = 2.74 - - elif candidate == CAR.TOURAN_MK2: - ret.mass = 1516 - ret.wheelbase = 2.79 - - elif candidate == CAR.TRANSPORTER_T61: - ret.mass = 1926 - ret.wheelbase = 3.00 # SWB, LWB is 3.40, TBD how to detect difference - ret.minSteerSpeed = 14.0 - - elif candidate == CAR.TROC_MK1: - ret.mass = 1413 - ret.wheelbase = 2.63 - - elif candidate == CAR.AUDI_A3_MK3: - ret.mass = 1335 - ret.wheelbase = 2.61 - - elif candidate == CAR.AUDI_Q2_MK1: - ret.mass = 1205 - ret.wheelbase = 2.61 - - elif candidate == CAR.AUDI_Q3_MK2: - ret.mass = 1623 - ret.wheelbase = 2.68 - - elif candidate == CAR.SEAT_ATECA_MK1: - ret.mass = 1900 - ret.wheelbase = 2.64 - - elif candidate == CAR.SEAT_LEON_MK3: - ret.mass = 1227 - ret.wheelbase = 2.64 - - elif candidate == CAR.SKODA_FABIA_MK4: - ret.mass = 1266 - ret.wheelbase = 2.56 - - elif candidate == CAR.SKODA_KAMIQ_MK1: - ret.mass = 1265 - ret.wheelbase = 2.66 - - elif candidate == CAR.SKODA_KAROQ_MK1: - ret.mass = 1278 - ret.wheelbase = 2.66 - - elif candidate == CAR.SKODA_KODIAQ_MK1: - ret.mass = 1569 - ret.wheelbase = 2.79 - - elif candidate == CAR.SKODA_OCTAVIA_MK3: - ret.mass = 1388 - ret.wheelbase = 2.68 - - elif candidate == CAR.SKODA_SCALA_MK1: - ret.mass = 1192 - ret.wheelbase = 2.65 - - elif candidate == CAR.SKODA_SUPERB_MK3: - ret.mass = 1505 - ret.wheelbase = 2.84 - - else: - raise ValueError(f"unsupported car {candidate}") - ret.autoResumeSng = ret.minEnableSpeed == -1 ret.centerToFront = ret.wheelbase * 0.45 return ret diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py index 73bd7661d9..4d375d687f 100644 --- a/selfdrive/car/volkswagen/values.py +++ b/selfdrive/car/volkswagen/values.py @@ -1,12 +1,12 @@ -from collections import defaultdict, namedtuple +from collections import namedtuple from dataclasses import dataclass, field -from enum import Enum, IntFlag, StrEnum -from typing import Dict, List, Union +from enum import Enum, IntFlag from cereal import car from panda.python import uds from opendbc.can.can_define import CANDefine -from openpilot.selfdrive.car import dbc_dict +from openpilot.common.conversions import Conversions as CV +from openpilot.selfdrive.car import dbc_dict, CarSpecs, DbcDict, PlatformConfig, Platforms from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarInfo, CarParts, Column, \ Device from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, p16 @@ -130,48 +130,17 @@ BUTTON_STATES = { } -# Check the 7th and 8th characters of the VIN before adding a new CAR. If the -# chassis code is already listed below, don't add a new CAR, just add to the -# FW_VERSIONS for that existing CAR. -# Exception: SEAT Leon and SEAT Ateca share a chassis code +@dataclass(frozen=True) +class VolkswagenMQBPlatformConfig(PlatformConfig): + dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('vw_mqb_2010', None)) -class CAR(StrEnum): - ARTEON_MK1 = "VOLKSWAGEN ARTEON 1ST GEN" # Chassis AN, Mk1 VW Arteon and variants - ATLAS_MK1 = "VOLKSWAGEN ATLAS 1ST GEN" # Chassis CA, Mk1 VW Atlas and Atlas Cross Sport - CRAFTER_MK2 = "VOLKSWAGEN CRAFTER 2ND GEN" # Chassis SY/SZ, Mk2 VW Crafter, VW Grand California, MAN TGE - GOLF_MK7 = "VOLKSWAGEN GOLF 7TH GEN" # Chassis 5G/AU/BA/BE, Mk7 VW Golf and variants - JETTA_MK7 = "VOLKSWAGEN JETTA 7TH GEN" # Chassis BU, Mk7 VW Jetta - PASSAT_MK8 = "VOLKSWAGEN PASSAT 8TH GEN" # Chassis 3G, Mk8 VW Passat and variants - PASSAT_NMS = "VOLKSWAGEN PASSAT NMS" # Chassis A3, North America/China/Mideast NMS Passat, incl. facelift - POLO_MK6 = "VOLKSWAGEN POLO 6TH GEN" # Chassis AW, Mk6 VW Polo - SHARAN_MK2 = "VOLKSWAGEN SHARAN 2ND GEN" # Chassis 7N, Mk2 Volkswagen Sharan and SEAT Alhambra - TAOS_MK1 = "VOLKSWAGEN TAOS 1ST GEN" # Chassis B2, Mk1 VW Taos and Tharu - TCROSS_MK1 = "VOLKSWAGEN T-CROSS 1ST GEN" # Chassis C1, Mk1 VW T-Cross SWB and LWB variants - TIGUAN_MK2 = "VOLKSWAGEN TIGUAN 2ND GEN" # Chassis AD/BW, Mk2 VW Tiguan and variants - TOURAN_MK2 = "VOLKSWAGEN TOURAN 2ND GEN" # Chassis 1T, Mk2 VW Touran and variants - TRANSPORTER_T61 = "VOLKSWAGEN TRANSPORTER T6.1" # Chassis 7H/7L, T6-facelift Transporter/Multivan/Caravelle/California - TROC_MK1 = "VOLKSWAGEN T-ROC 1ST GEN" # Chassis A1, Mk1 VW T-Roc and variants - AUDI_A3_MK3 = "AUDI A3 3RD GEN" # Chassis 8V/FF, Mk3 Audi A3 and variants - AUDI_Q2_MK1 = "AUDI Q2 1ST GEN" # Chassis GA, Mk1 Audi Q2 (RoW) and Q2L (China only) - AUDI_Q3_MK2 = "AUDI Q3 2ND GEN" # Chassis 8U/F3/FS, Mk2 Audi Q3 and variants - SEAT_ATECA_MK1 = "SEAT ATECA 1ST GEN" # Chassis 5F, Mk1 SEAT Ateca and CUPRA Ateca - SEAT_LEON_MK3 = "SEAT LEON 3RD GEN" # Chassis 5F, Mk3 SEAT Leon and variants - SKODA_FABIA_MK4 = "SKODA FABIA 4TH GEN" # Chassis PJ, Mk4 Skoda Fabia - SKODA_KAMIQ_MK1 = "SKODA KAMIQ 1ST GEN" # Chassis NW, Mk1 Skoda Kamiq - SKODA_KAROQ_MK1 = "SKODA KAROQ 1ST GEN" # Chassis NU, Mk1 Skoda Karoq - SKODA_KODIAQ_MK1 = "SKODA KODIAQ 1ST GEN" # Chassis NS, Mk1 Skoda Kodiaq - SKODA_SCALA_MK1 = "SKODA SCALA 1ST GEN" # Chassis NW, Mk1 Skoda Scala and Skoda Kamiq - SKODA_SUPERB_MK3 = "SKODA SUPERB 3RD GEN" # Chassis 3V/NP, Mk3 Skoda Superb and variants - SKODA_OCTAVIA_MK3 = "SKODA OCTAVIA 3RD GEN" # Chassis NE, Mk3 Skoda Octavia and variants - - -PQ_CARS = {CAR.PASSAT_NMS, CAR.SHARAN_MK2} - - -DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict("vw_mqb_2010", None)) -for car_type in PQ_CARS: - DBC[car_type] = dbc_dict("vw_golf_mk4", None) +@dataclass(frozen=True) +class VolkswagenPQPlatformConfig(PlatformConfig): + dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('vw_golf_mk4', None)) +@dataclass(frozen=True, kw_only=True) +class VolkswagenCarSpecs(CarSpecs): + steerRatio: float = field(default=15.6) class Footnote(Enum): KAMIQ = CarFootnote( @@ -207,90 +176,202 @@ class VWCarInfo(CarInfo): if CP.carFingerprint in (CAR.CRAFTER_MK2, CAR.TRANSPORTER_T61): self.car_parts = CarParts([Device.threex_angled_mount, CarHarness.j533]) +# Check the 7th and 8th characters of the VIN before adding a new CAR. If the +# chassis code is already listed below, don't add a new CAR, just add to the +# FW_VERSIONS for that existing CAR. +# Exception: SEAT Leon and SEAT Ateca share a chassis code -CAR_INFO: Dict[str, Union[VWCarInfo, List[VWCarInfo]]] = { - CAR.ARTEON_MK1: [ - VWCarInfo("Volkswagen Arteon 2018-23", video_link="https://youtu.be/FAomFKPFlDA"), - VWCarInfo("Volkswagen Arteon R 2020-23", video_link="https://youtu.be/FAomFKPFlDA"), - VWCarInfo("Volkswagen Arteon eHybrid 2020-23", video_link="https://youtu.be/FAomFKPFlDA"), - VWCarInfo("Volkswagen CC 2018-22", video_link="https://youtu.be/FAomFKPFlDA"), - ], - CAR.ATLAS_MK1: [ - VWCarInfo("Volkswagen Atlas 2018-23"), - VWCarInfo("Volkswagen Atlas Cross Sport 2020-22"), - VWCarInfo("Volkswagen Teramont 2018-22"), - VWCarInfo("Volkswagen Teramont Cross Sport 2021-22"), - VWCarInfo("Volkswagen Teramont X 2021-22"), - ], - CAR.CRAFTER_MK2: [ - VWCarInfo("Volkswagen Crafter 2017-23", video_link="https://youtu.be/4100gLeabmo"), - VWCarInfo("Volkswagen e-Crafter 2018-23", video_link="https://youtu.be/4100gLeabmo"), - VWCarInfo("Volkswagen Grand California 2019-23", video_link="https://youtu.be/4100gLeabmo"), - VWCarInfo("MAN TGE 2017-23", video_link="https://youtu.be/4100gLeabmo"), - VWCarInfo("MAN eTGE 2020-23", video_link="https://youtu.be/4100gLeabmo"), - ], - CAR.GOLF_MK7: [ - VWCarInfo("Volkswagen e-Golf 2014-20"), - VWCarInfo("Volkswagen Golf 2015-20", auto_resume=False), - VWCarInfo("Volkswagen Golf Alltrack 2015-19", auto_resume=False), - VWCarInfo("Volkswagen Golf GTD 2015-20"), - VWCarInfo("Volkswagen Golf GTE 2015-20"), - VWCarInfo("Volkswagen Golf GTI 2015-21", auto_resume=False), - VWCarInfo("Volkswagen Golf R 2015-19"), - VWCarInfo("Volkswagen Golf SportsVan 2015-20"), - ], - CAR.JETTA_MK7: [ - VWCarInfo("Volkswagen Jetta 2018-24"), - VWCarInfo("Volkswagen Jetta GLI 2021-24"), - ], - CAR.PASSAT_MK8: [ - VWCarInfo("Volkswagen Passat 2015-22", footnotes=[Footnote.PASSAT]), - VWCarInfo("Volkswagen Passat Alltrack 2015-22"), - VWCarInfo("Volkswagen Passat GTE 2015-22"), - ], - CAR.PASSAT_NMS: VWCarInfo("Volkswagen Passat NMS 2017-22"), - CAR.POLO_MK6: [ - VWCarInfo("Volkswagen Polo 2018-23", footnotes=[Footnote.VW_MQB_A0]), - VWCarInfo("Volkswagen Polo GTI 2018-23", footnotes=[Footnote.VW_MQB_A0]), - ], - CAR.SHARAN_MK2: [ - VWCarInfo("Volkswagen Sharan 2018-22"), - VWCarInfo("SEAT Alhambra 2018-20"), - ], - CAR.TAOS_MK1: VWCarInfo("Volkswagen Taos 2022-23"), - CAR.TCROSS_MK1: VWCarInfo("Volkswagen T-Cross 2021", footnotes=[Footnote.VW_MQB_A0]), - CAR.TIGUAN_MK2: [ - VWCarInfo("Volkswagen Tiguan 2018-24"), - VWCarInfo("Volkswagen Tiguan eHybrid 2021-23"), - ], - CAR.TOURAN_MK2: VWCarInfo("Volkswagen Touran 2016-23"), - CAR.TRANSPORTER_T61: [ - VWCarInfo("Volkswagen Caravelle 2020"), - VWCarInfo("Volkswagen California 2021-23"), - ], - CAR.TROC_MK1: VWCarInfo("Volkswagen T-Roc 2018-22", footnotes=[Footnote.VW_MQB_A0]), - CAR.AUDI_A3_MK3: [ - VWCarInfo("Audi A3 2014-19"), - VWCarInfo("Audi A3 Sportback e-tron 2017-18"), - VWCarInfo("Audi RS3 2018"), - VWCarInfo("Audi S3 2015-17"), - ], - CAR.AUDI_Q2_MK1: VWCarInfo("Audi Q2 2018"), - CAR.AUDI_Q3_MK2: VWCarInfo("Audi Q3 2019-23"), - CAR.SEAT_ATECA_MK1: VWCarInfo("SEAT Ateca 2018"), - CAR.SEAT_LEON_MK3: VWCarInfo("SEAT Leon 2014-20"), - CAR.SKODA_FABIA_MK4: VWCarInfo("Å koda Fabia 2022-23", footnotes=[Footnote.VW_MQB_A0]), - CAR.SKODA_KAMIQ_MK1: VWCarInfo("Å koda Kamiq 2021-23", footnotes=[Footnote.VW_MQB_A0, Footnote.KAMIQ]), - CAR.SKODA_KAROQ_MK1: VWCarInfo("Å koda Karoq 2019-23"), - CAR.SKODA_KODIAQ_MK1: VWCarInfo("Å koda Kodiaq 2017-23"), - CAR.SKODA_SCALA_MK1: VWCarInfo("Å koda Scala 2020-23", footnotes=[Footnote.VW_MQB_A0]), - CAR.SKODA_SUPERB_MK3: VWCarInfo("Å koda Superb 2015-22"), - CAR.SKODA_OCTAVIA_MK3: [ - VWCarInfo("Å koda Octavia 2015-19"), - VWCarInfo("Å koda Octavia RS 2016"), - ], -} +class CAR(Platforms): + ARTEON_MK1 = VolkswagenMQBPlatformConfig( + "VOLKSWAGEN ARTEON 1ST GEN", # Chassis AN + [ + VWCarInfo("Volkswagen Arteon 2018-23", video_link="https://youtu.be/FAomFKPFlDA"), + VWCarInfo("Volkswagen Arteon R 2020-23", video_link="https://youtu.be/FAomFKPFlDA"), + VWCarInfo("Volkswagen Arteon eHybrid 2020-23", video_link="https://youtu.be/FAomFKPFlDA"), + VWCarInfo("Volkswagen CC 2018-22", video_link="https://youtu.be/FAomFKPFlDA"), + ], + specs=VolkswagenCarSpecs(mass=1733, wheelbase=2.84), + ) + ATLAS_MK1 = VolkswagenMQBPlatformConfig( + "VOLKSWAGEN ATLAS 1ST GEN", # Chassis CA + [ + VWCarInfo("Volkswagen Atlas 2018-23"), + VWCarInfo("Volkswagen Atlas Cross Sport 2020-22"), + VWCarInfo("Volkswagen Teramont 2018-22"), + VWCarInfo("Volkswagen Teramont Cross Sport 2021-22"), + VWCarInfo("Volkswagen Teramont X 2021-22"), + ], + specs=VolkswagenCarSpecs(mass=2011, wheelbase=2.98), + ) + CRAFTER_MK2 = VolkswagenMQBPlatformConfig( + "VOLKSWAGEN CRAFTER 2ND GEN", # Chassis SY/SZ + [ + VWCarInfo("Volkswagen Crafter 2017-23", video_link="https://youtu.be/4100gLeabmo"), + VWCarInfo("Volkswagen e-Crafter 2018-23", video_link="https://youtu.be/4100gLeabmo"), + VWCarInfo("Volkswagen Grand California 2019-23", video_link="https://youtu.be/4100gLeabmo"), + VWCarInfo("MAN TGE 2017-23", video_link="https://youtu.be/4100gLeabmo"), + VWCarInfo("MAN eTGE 2020-23", video_link="https://youtu.be/4100gLeabmo"), + ], + specs=VolkswagenCarSpecs(mass=2100, wheelbase=3.64, minSteerSpeed=50 * CV.KPH_TO_MS), + ) + GOLF_MK7 = VolkswagenMQBPlatformConfig( + "VOLKSWAGEN GOLF 7TH GEN", # Chassis 5G/AU/BA/BE + [ + VWCarInfo("Volkswagen e-Golf 2014-20"), + VWCarInfo("Volkswagen Golf 2015-20", auto_resume=False), + VWCarInfo("Volkswagen Golf Alltrack 2015-19", auto_resume=False), + VWCarInfo("Volkswagen Golf GTD 2015-20"), + VWCarInfo("Volkswagen Golf GTE 2015-20"), + VWCarInfo("Volkswagen Golf GTI 2015-21", auto_resume=False), + VWCarInfo("Volkswagen Golf R 2015-19"), + VWCarInfo("Volkswagen Golf SportsVan 2015-20"), + ], + specs=VolkswagenCarSpecs(mass=1397, wheelbase=2.62), + ) + JETTA_MK7 = VolkswagenMQBPlatformConfig( + "VOLKSWAGEN JETTA 7TH GEN", # Chassis BU + [ + VWCarInfo("Volkswagen Jetta 2018-24"), + VWCarInfo("Volkswagen Jetta GLI 2021-24"), + ], + specs=VolkswagenCarSpecs(mass=1328, wheelbase=2.71), + ) + PASSAT_MK8 = VolkswagenMQBPlatformConfig( + "VOLKSWAGEN PASSAT 8TH GEN", # Chassis 3G + [ + VWCarInfo("Volkswagen Passat 2015-22", footnotes=[Footnote.PASSAT]), + VWCarInfo("Volkswagen Passat Alltrack 2015-22"), + VWCarInfo("Volkswagen Passat GTE 2015-22"), + ], + specs=VolkswagenCarSpecs(mass=1551, wheelbase=2.79), + ) + PASSAT_NMS = VolkswagenPQPlatformConfig( + "VOLKSWAGEN PASSAT NMS", # Chassis A3 + VWCarInfo("Volkswagen Passat NMS 2017-22"), + specs=VolkswagenCarSpecs(mass=1503, wheelbase=2.80, minSteerSpeed=50*CV.KPH_TO_MS, minEnableSpeed=20*CV.KPH_TO_MS), + ) + POLO_MK6 = VolkswagenMQBPlatformConfig( + "VOLKSWAGEN POLO 6TH GEN", # Chassis AW + [ + VWCarInfo("Volkswagen Polo 2018-23", footnotes=[Footnote.VW_MQB_A0]), + VWCarInfo("Volkswagen Polo GTI 2018-23", footnotes=[Footnote.VW_MQB_A0]), + ], + specs=VolkswagenCarSpecs(mass=1230, wheelbase=2.55), + ) + SHARAN_MK2 = VolkswagenPQPlatformConfig( + "VOLKSWAGEN SHARAN 2ND GEN", # Chassis 7N + [ + VWCarInfo("Volkswagen Sharan 2018-22"), + VWCarInfo("SEAT Alhambra 2018-20"), + ], + specs=VolkswagenCarSpecs(mass=1639, wheelbase=2.92, minSteerSpeed=50*CV.KPH_TO_MS), + ) + TAOS_MK1 = VolkswagenMQBPlatformConfig( + "VOLKSWAGEN TAOS 1ST GEN", # Chassis B2 + VWCarInfo("Volkswagen Taos 2022-23"), + specs=VolkswagenCarSpecs(mass=1498, wheelbase=2.69), + ) + TCROSS_MK1 = VolkswagenMQBPlatformConfig( + "VOLKSWAGEN T-CROSS 1ST GEN", # Chassis C1 + car_info=VWCarInfo("Volkswagen T-Cross 2021", footnotes=[Footnote.VW_MQB_A0]), + specs=VolkswagenCarSpecs(mass=1150, wheelbase=2.60), + ) + TIGUAN_MK2 = VolkswagenMQBPlatformConfig( + "VOLKSWAGEN TIGUAN 2ND GEN", # Chassis AD/BW + [ + VWCarInfo("Volkswagen Tiguan 2018-24"), + VWCarInfo("Volkswagen Tiguan eHybrid 2021-23"), + ], + specs=VolkswagenCarSpecs(mass=1715, wheelbase=2.74), + ) + TOURAN_MK2 = VolkswagenMQBPlatformConfig( + "VOLKSWAGEN TOURAN 2ND GEN", # Chassis 1T + VWCarInfo("Volkswagen Touran 2016-23"), + specs=VolkswagenCarSpecs(mass=1516, wheelbase=2.79), + ) + TRANSPORTER_T61 = VolkswagenMQBPlatformConfig( + "VOLKSWAGEN TRANSPORTER T6.1", # Chassis 7H/7L + [ + VWCarInfo("Volkswagen Caravelle 2020"), + VWCarInfo("Volkswagen California 2021-23"), + ], + specs=VolkswagenCarSpecs(mass=1926, wheelbase=3.00, minSteerSpeed=14.0), + ) + TROC_MK1 = VolkswagenMQBPlatformConfig( + "VOLKSWAGEN T-ROC 1ST GEN", # Chassis A1 + VWCarInfo("Volkswagen T-Roc 2018-22", footnotes=[Footnote.VW_MQB_A0]), + specs=VolkswagenCarSpecs(mass=1413, wheelbase=2.63), + ) + AUDI_A3_MK3 = VolkswagenMQBPlatformConfig( + "AUDI A3 3RD GEN", # Chassis 8V/FF + [ + VWCarInfo("Audi A3 2014-19"), + VWCarInfo("Audi A3 Sportback e-tron 2017-18"), + VWCarInfo("Audi RS3 2018"), + VWCarInfo("Audi S3 2015-17"), + ], + specs=VolkswagenCarSpecs(mass=1335, wheelbase=2.61), + ) + AUDI_Q2_MK1 = VolkswagenMQBPlatformConfig( + "AUDI Q2 1ST GEN", # Chassis GA + VWCarInfo("Audi Q2 2018"), + specs=VolkswagenCarSpecs(mass=1205, wheelbase=2.61), + ) + AUDI_Q3_MK2 = VolkswagenMQBPlatformConfig( + "AUDI Q3 2ND GEN", # Chassis 8U/F3/FS + VWCarInfo("Audi Q3 2019-23"), + specs=VolkswagenCarSpecs(mass=1623, wheelbase=2.68), + ) + SEAT_ATECA_MK1 = VolkswagenMQBPlatformConfig( + "SEAT ATECA 1ST GEN", # Chassis 5F + VWCarInfo("SEAT Ateca 2018"), + specs=VolkswagenCarSpecs(mass=1900, wheelbase=2.64), + ) + SEAT_LEON_MK3 = VolkswagenMQBPlatformConfig( + "SEAT LEON 3RD GEN", # Chassis 5F + VWCarInfo("SEAT Leon 2014-20"), + specs=VolkswagenCarSpecs(mass=1227, wheelbase=2.64), + ) + SKODA_FABIA_MK4 = VolkswagenMQBPlatformConfig( + "SKODA FABIA 4TH GEN", # Chassis PJ + VWCarInfo("Å koda Fabia 2022-23", footnotes=[Footnote.VW_MQB_A0]), + specs=VolkswagenCarSpecs(mass=1266, wheelbase=2.56), + ) + SKODA_KAMIQ_MK1 = VolkswagenMQBPlatformConfig( + "SKODA KAMIQ 1ST GEN", # Chassis NW + VWCarInfo("Å koda Kamiq 2021-23", footnotes=[Footnote.VW_MQB_A0, Footnote.KAMIQ]), + specs=VolkswagenCarSpecs(mass=1265, wheelbase=2.66), + ) + SKODA_KAROQ_MK1 = VolkswagenMQBPlatformConfig( + "SKODA KAROQ 1ST GEN", # Chassis NU + VWCarInfo("Å koda Karoq 2019-23"), + specs=VolkswagenCarSpecs(mass=1278, wheelbase=2.66), + ) + SKODA_KODIAQ_MK1 = VolkswagenMQBPlatformConfig( + "SKODA KODIAQ 1ST GEN", # Chassis NS + VWCarInfo("Å koda Kodiaq 2017-23"), + specs=VolkswagenCarSpecs(mass=1569, wheelbase=2.79), + ) + SKODA_OCTAVIA_MK3 = VolkswagenMQBPlatformConfig( + "SKODA OCTAVIA 3RD GEN", # Chassis NE + [ + VWCarInfo("Å koda Octavia 2015-19"), + VWCarInfo("Å koda Octavia RS 2016"), + ], + specs=VolkswagenCarSpecs(mass=1388, wheelbase=2.68), + ) + SKODA_SCALA_MK1 = VolkswagenMQBPlatformConfig( + "SKODA SCALA 1ST GEN", # Chassis NW + VWCarInfo("Å koda Scala 2020-23", footnotes=[Footnote.VW_MQB_A0]), + specs=VolkswagenCarSpecs(mass=1192, wheelbase=2.65), + ) + SKODA_SUPERB_MK3 = VolkswagenMQBPlatformConfig( + "SKODA SUPERB 3RD GEN", # Chassis 3V/NP + VWCarInfo("Å koda Superb 2015-22"), + specs=VolkswagenCarSpecs(mass=1505, wheelbase=2.84), + ) +PQ_CARS = {CAR.PASSAT_NMS, CAR.SHARAN_MK2} # All supported cars should return FW from the engine, srs, eps, and fwdRadar. Cars # with a manual trans won't return transmission firmware, but all other cars will. @@ -310,17 +391,27 @@ VOLKSWAGEN_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + VOLKSWAGEN_RX_OFFSET = 0x6a FW_QUERY_CONFIG = FwQueryConfig( - requests=[ + # TODO: add back whitelists after we gather enough data + requests=[request for bus, obd_multiplexing in [(1, True), (1, False), (0, False)] for request in [ Request( [VOLKSWAGEN_VERSION_REQUEST_MULTI], [VOLKSWAGEN_VERSION_RESPONSE], - whitelist_ecus=[Ecu.srs, Ecu.eps, Ecu.fwdRadar], + # whitelist_ecus=[Ecu.srs, Ecu.eps, Ecu.fwdRadar], rx_offset=VOLKSWAGEN_RX_OFFSET, + bus=bus, + logging=(bus != 1 or not obd_multiplexing), + obd_multiplexing=obd_multiplexing, ), Request( [VOLKSWAGEN_VERSION_REQUEST_MULTI], [VOLKSWAGEN_VERSION_RESPONSE], - whitelist_ecus=[Ecu.engine, Ecu.transmission], + # whitelist_ecus=[Ecu.engine, Ecu.transmission], + bus=bus, + logging=(bus != 1 or not obd_multiplexing), + obd_multiplexing=obd_multiplexing, ), - ], + ]], ) + +CAR_INFO = CAR.create_carinfo_map() +DBC = CAR.create_dbc_map() diff --git a/selfdrive/controls/lib/alertmanager.py b/selfdrive/controls/lib/alertmanager.py index 6abcf4cbba..f67e269fa9 100644 --- a/selfdrive/controls/lib/alertmanager.py +++ b/selfdrive/controls/lib/alertmanager.py @@ -3,7 +3,6 @@ import os import json from collections import defaultdict from dataclasses import dataclass -from typing import List, Dict, Optional from openpilot.common.basedir import BASEDIR from openpilot.common.params import Params @@ -14,7 +13,7 @@ with open(os.path.join(BASEDIR, "selfdrive/controls/lib/alerts_offroad.json")) a OFFROAD_ALERTS = json.load(f) -def set_offroad_alert(alert: str, show_alert: bool, extra_text: Optional[str] = None) -> None: +def set_offroad_alert(alert: str, show_alert: bool, extra_text: str = None) -> None: if show_alert: a = copy.copy(OFFROAD_ALERTS[alert]) a['extra'] = extra_text or '' @@ -25,7 +24,7 @@ def set_offroad_alert(alert: str, show_alert: bool, extra_text: Optional[str] = @dataclass class AlertEntry: - alert: Optional[Alert] = None + alert: Alert | None = None start_frame: int = -1 end_frame: int = -1 @@ -34,9 +33,9 @@ class AlertEntry: class AlertManager: def __init__(self): - self.alerts: Dict[str, AlertEntry] = defaultdict(AlertEntry) + self.alerts: dict[str, AlertEntry] = defaultdict(AlertEntry) - def add_many(self, frame: int, alerts: List[Alert]) -> None: + def add_many(self, frame: int, alerts: list[Alert]) -> None: for alert in alerts: entry = self.alerts[alert.alert_type] entry.alert = alert @@ -45,7 +44,7 @@ class AlertManager: min_end_frame = entry.start_frame + alert.duration entry.end_frame = max(frame + 1, min_end_frame) - def process_alerts(self, frame: int, clear_event_types: set) -> Optional[Alert]: + def process_alerts(self, frame: int, clear_event_types: set) -> Alert | None: current_alert = AlertEntry() for v in self.alerts.values(): if not v.alert: diff --git a/selfdrive/controls/lib/events.py b/selfdrive/controls/lib/events.py index c2ffca2450..a2ae918e94 100755 --- a/selfdrive/controls/lib/events.py +++ b/selfdrive/controls/lib/events.py @@ -2,7 +2,7 @@ import math import os from enum import IntEnum -from typing import Dict, Union, Callable, List, Optional +from collections.abc import Callable from cereal import log, car import cereal.messaging as messaging @@ -48,12 +48,12 @@ EVENT_NAME = {v: k for k, v in EventName.schema.enumerants.items()} class Events: def __init__(self): - self.events: List[int] = [] - self.static_events: List[int] = [] + self.events: list[int] = [] + self.static_events: list[int] = [] self.events_prev = dict.fromkeys(EVENTS.keys(), 0) @property - def names(self) -> List[int]: + def names(self) -> list[int]: return self.events def __len__(self) -> int: @@ -71,7 +71,7 @@ class Events: def contains(self, event_type: str) -> bool: return any(event_type in EVENTS.get(e, {}) for e in self.events) - def create_alerts(self, event_types: List[str], callback_args=None): + def create_alerts(self, event_types: list[str], callback_args=None): if callback_args is None: callback_args = [] @@ -132,7 +132,7 @@ class Alert: self.creation_delay = creation_delay self.alert_type = "" - self.event_type: Optional[str] = None + self.event_type: str | None = None def __str__(self) -> str: return f"{self.alert_text_1}/{self.alert_text_2} {self.priority} {self.visual_alert} {self.audible_alert}" @@ -364,7 +364,7 @@ def speed_limit_adjust_alert(CP: car.CarParams, CS: car.CarState, sm: messaging. -EVENTS: Dict[int, Dict[str, Union[Alert, AlertCallbackType]]] = { +EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = { # ********** events with no alerts ********** EventName.stockFcw: {}, @@ -1150,7 +1150,7 @@ if __name__ == '__main__': from collections import defaultdict event_names = {v: k for k, v in EventName.schema.enumerants.items()} - alerts_by_type: Dict[str, Dict[Priority, List[str]]] = defaultdict(lambda: defaultdict(list)) + alerts_by_type: dict[str, dict[Priority, list[str]]] = defaultdict(lambda: defaultdict(list)) CP = car.CarParams.new_message() CS = car.CarState.new_message() @@ -1162,7 +1162,7 @@ if __name__ == '__main__': alert = alert(CP, CS, sm, False, 1) alerts_by_type[et][alert.priority].append(event_names[i]) - all_alerts: Dict[str, List[tuple[Priority, List[str]]]] = {} + all_alerts: dict[str, list[tuple[Priority, list[str]]]] = {} for et, priority_alerts in alerts_by_type.items(): all_alerts[et] = sorted(priority_alerts.items(), key=lambda x: x[0], reverse=True) diff --git a/selfdrive/controls/lib/vehicle_model.py b/selfdrive/controls/lib/vehicle_model.py index 0750384918..b6f50b4ba8 100755 --- a/selfdrive/controls/lib/vehicle_model.py +++ b/selfdrive/controls/lib/vehicle_model.py @@ -12,7 +12,6 @@ x_dot = A*x + B*u A depends on longitudinal speed, u [m/s], and vehicle parameters CP """ -from typing import Tuple import numpy as np from numpy.linalg import solve @@ -169,7 +168,7 @@ def kin_ss_sol(sa: float, u: float, VM: VehicleModel) -> np.ndarray: return K * sa -def create_dyn_state_matrices(u: float, VM: VehicleModel) -> Tuple[np.ndarray, np.ndarray]: +def create_dyn_state_matrices(u: float, VM: VehicleModel) -> tuple[np.ndarray, np.ndarray]: """Returns the A and B matrix for the dynamics system Args: diff --git a/selfdrive/controls/radard.py b/selfdrive/controls/radard.py index bc3426d2e3..08c8b2e50e 100755 --- a/selfdrive/controls/radard.py +++ b/selfdrive/controls/radard.py @@ -2,7 +2,7 @@ import importlib import math from collections import deque -from typing import Optional, Dict, Any +from typing import Any, Optional import capnp from cereal import messaging, log, car @@ -127,7 +127,7 @@ def laplacian_pdf(x: float, mu: float, b: float): return math.exp(-abs(x-mu)/b) -def match_vision_to_track(v_ego: float, lead: capnp._DynamicStructReader, tracks: Dict[int, Track]): +def match_vision_to_track(v_ego: float, lead: capnp._DynamicStructReader, tracks: dict[int, Track]): offset_vision_dist = lead.x[0] - RADAR_TO_CAMERA def prob(c): @@ -168,8 +168,8 @@ def get_RadarState_from_vision(lead_msg: capnp._DynamicStructReader, v_ego: floa } -def get_lead(v_ego: float, ready: bool, tracks: Dict[int, Track], lead_msg: capnp._DynamicStructReader, - model_v_ego: float, CP: car.CarParams, low_speed_override: bool = True) -> Dict[str, Any]: +def get_lead(v_ego: float, ready: bool, tracks: dict[int, Track], lead_msg: capnp._DynamicStructReader, + model_v_ego: float, CP: car.CarParams, low_speed_override: bool = True) -> dict[str, Any]: # Determine leads, this is where the essential logic happens if len(tracks) > 0 and ready and lead_msg.prob > .5: track = match_vision_to_track(v_ego, lead_msg, tracks) @@ -198,14 +198,14 @@ class RadarD: def __init__(self, radar_ts: float, CP: car.CarParams, delay: int = 0): self.current_time = 0.0 - self.tracks: Dict[int, Track] = {} + self.tracks: dict[int, Track] = {} self.kalman_params = KalmanParams(radar_ts) self.v_ego = 0.0 self.v_ego_hist = deque([0.0], maxlen=delay+1) self.last_v_ego_frame = -1 - self.radar_state: Optional[capnp._DynamicStructBuilder] = None + self.radar_state: capnp._DynamicStructBuilder | None = None self.radar_state_valid = False self.ready = False diff --git a/selfdrive/debug/can_print_changes.py b/selfdrive/debug/can_print_changes.py index 810e45cfc6..97d60b2b05 100755 --- a/selfdrive/debug/can_print_changes.py +++ b/selfdrive/debug/can_print_changes.py @@ -3,7 +3,6 @@ import argparse import binascii import time from collections import defaultdict -from typing import Optional import cereal.messaging as messaging from openpilot.selfdrive.debug.can_table import can_table @@ -96,8 +95,8 @@ if __name__ == "__main__": args = parser.parse_args() - init_lr: Optional[LogIterable] = None - new_lr: Optional[LogIterable] = None + init_lr: LogIterable | None = None + new_lr: LogIterable | None = None if args.init: if args.init == '': diff --git a/selfdrive/debug/check_freq.py b/selfdrive/debug/check_freq.py index 7e7b05e950..1765aeb86b 100755 --- a/selfdrive/debug/check_freq.py +++ b/selfdrive/debug/check_freq.py @@ -3,7 +3,7 @@ import argparse import numpy as np import time from collections import defaultdict, deque -from typing import DefaultDict, Deque, MutableSequence +from collections.abc import MutableSequence import cereal.messaging as messaging @@ -19,8 +19,8 @@ if __name__ == "__main__": socket_names = args.socket sockets = {} - rcv_times: DefaultDict[str, MutableSequence[float]] = defaultdict(lambda: deque(maxlen=100)) - valids: DefaultDict[str, Deque[bool]] = defaultdict(lambda: deque(maxlen=100)) + rcv_times: defaultdict[str, MutableSequence[float]] = defaultdict(lambda: deque(maxlen=100)) + valids: defaultdict[str, deque[bool]] = defaultdict(lambda: deque(maxlen=100)) t = time.monotonic() for name in socket_names: diff --git a/selfdrive/debug/check_lag.py b/selfdrive/debug/check_lag.py index 72d11d6eda..341ae79c8b 100755 --- a/selfdrive/debug/check_lag.py +++ b/selfdrive/debug/check_lag.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -from typing import Dict import cereal.messaging as messaging from cereal.services import SERVICE_LIST @@ -10,7 +9,7 @@ TO_CHECK = ['carState'] if __name__ == "__main__": sm = messaging.SubMaster(TO_CHECK) - prev_t: Dict[str, float] = {} + prev_t: dict[str, float] = {} while True: sm.update() diff --git a/selfdrive/debug/check_timings.py b/selfdrive/debug/check_timings.py index fb8467a3c4..3de6b8eb66 100755 --- a/selfdrive/debug/check_timings.py +++ b/selfdrive/debug/check_timings.py @@ -3,13 +3,13 @@ import sys import time import numpy as np -from typing import DefaultDict, MutableSequence +from collections.abc import MutableSequence from collections import defaultdict, deque import cereal.messaging as messaging socks = {s: messaging.sub_sock(s, conflate=False) for s in sys.argv[1:]} -ts: DefaultDict[str, MutableSequence[float]] = defaultdict(lambda: deque(maxlen=100)) +ts: defaultdict[str, MutableSequence[float]] = defaultdict(lambda: deque(maxlen=100)) if __name__ == "__main__": while True: diff --git a/selfdrive/debug/count_events.py b/selfdrive/debug/count_events.py index 30e9bf8ec5..5942054757 100755 --- a/selfdrive/debug/count_events.py +++ b/selfdrive/debug/count_events.py @@ -4,7 +4,7 @@ import math import datetime from collections import Counter from pprint import pprint -from typing import List, Tuple, cast +from typing import cast from cereal.services import SERVICE_LIST from openpilot.tools.lib.logreader import LogReader, ReadMode @@ -15,8 +15,8 @@ if __name__ == "__main__": cams = [s for s in SERVICE_LIST if s.endswith('CameraState')] cnt_cameras = dict.fromkeys(cams, 0) - events: List[Tuple[float, set[str]]] = [] - alerts: List[Tuple[float, str]] = [] + events: list[tuple[float, set[str]]] = [] + alerts: list[tuple[float, str]] = [] start_time = math.inf end_time = -math.inf ignition_off = None diff --git a/selfdrive/debug/cpu_usage_stat.py b/selfdrive/debug/cpu_usage_stat.py index 9050fbb064..ec9d02e8f4 100755 --- a/selfdrive/debug/cpu_usage_stat.py +++ b/selfdrive/debug/cpu_usage_stat.py @@ -66,12 +66,12 @@ if __name__ == "__main__": for p in psutil.process_iter(): if p == psutil.Process(): continue - matched = any(l for l in p.cmdline() if any(pn for pn in monitored_proc_names if re.match(r'.*{}.*'.format(pn), l, re.M | re.I))) + matched = any(l for l in p.cmdline() if any(pn for pn in monitored_proc_names if re.match(fr'.*{pn}.*', l, re.M | re.I))) if matched: k = ' '.join(p.cmdline()) print('Add monitored proc:', k) stats[k] = {'cpu_samples': defaultdict(list), 'min': defaultdict(lambda: None), 'max': defaultdict(lambda: None), - 'avg': defaultdict(lambda: 0.0), 'last_cpu_times': None, 'last_sys_time': None} + 'avg': defaultdict(float), 'last_cpu_times': None, 'last_sys_time': None} stats[k]['last_sys_time'] = timer() stats[k]['last_cpu_times'] = p.cpu_times() monitored_procs.append(p) diff --git a/selfdrive/debug/format_fingerprints.py b/selfdrive/debug/format_fingerprints.py index bd5822729a..2a5e4e6080 100755 --- a/selfdrive/debug/format_fingerprints.py +++ b/selfdrive/debug/format_fingerprints.py @@ -67,7 +67,7 @@ def format_brand_fw_versions(brand, extra_fw_versions: None | dict[str, dict[tup extra_fw_versions = extra_fw_versions or {} fingerprints_file = os.path.join(BASEDIR, f"selfdrive/car/{brand}/fingerprints.py") - with open(fingerprints_file, "r") as f: + with open(fingerprints_file) as f: comments = [line for line in f.readlines() if line.startswith("#") and "noqa" not in line] with open(fingerprints_file, "w") as f: diff --git a/selfdrive/debug/internal/measure_modeld_packet_drop.py b/selfdrive/debug/internal/measure_modeld_packet_drop.py index 6b7f7dbd13..9814942ce2 100755 --- a/selfdrive/debug/internal/measure_modeld_packet_drop.py +++ b/selfdrive/debug/internal/measure_modeld_packet_drop.py @@ -1,12 +1,11 @@ #!/usr/bin/env python3 import cereal.messaging as messaging -from typing import Optional if __name__ == "__main__": modeld_sock = messaging.sub_sock("modelV2") last_frame_id = None - start_t: Optional[int] = None + start_t: int | None = None frame_cnt = 0 dropped = 0 diff --git a/selfdrive/debug/live_cpu_and_temp.py b/selfdrive/debug/live_cpu_and_temp.py index 06f1be0b00..8549b92665 100755 --- a/selfdrive/debug/live_cpu_and_temp.py +++ b/selfdrive/debug/live_cpu_and_temp.py @@ -5,7 +5,6 @@ from collections import defaultdict from cereal.messaging import SubMaster from openpilot.common.numpy_fast import mean -from typing import Optional, Dict def cputime_total(ct): return ct.user + ct.nice + ct.system + ct.idle + ct.iowait + ct.irq + ct.softirq @@ -42,8 +41,8 @@ if __name__ == "__main__": total_times = [0.]*8 busy_times = [0.]*8 - prev_proclog: Optional[capnp._DynamicStructReader] = None - prev_proclog_t: Optional[int] = None + prev_proclog: capnp._DynamicStructReader | None = None + prev_proclog_t: int | None = None while True: sm.update() @@ -76,7 +75,7 @@ if __name__ == "__main__": print(f"CPU {100.0 * mean(cores):.2f}% - RAM: {last_mem:.2f}% - Temp {last_temp:.2f}C") if args.cpu and prev_proclog is not None and prev_proclog_t is not None: - procs: Dict[str, float] = defaultdict(float) + procs: dict[str, float] = defaultdict(float) dt = (sm.logMonoTime['procLog'] - prev_proclog_t) / 1e9 for proc in m.procs: try: diff --git a/selfdrive/locationd/calibrationd.py b/selfdrive/locationd/calibrationd.py index da1c4ec37b..6e154bf07c 100755 --- a/selfdrive/locationd/calibrationd.py +++ b/selfdrive/locationd/calibrationd.py @@ -10,7 +10,7 @@ import gc import os import capnp import numpy as np -from typing import List, NoReturn, Optional +from typing import NoReturn from cereal import log import cereal.messaging as messaging @@ -89,7 +89,7 @@ class Calibrator: valid_blocks: int = 0, wide_from_device_euler_init: np.ndarray = WIDE_FROM_DEVICE_EULER_INIT, height_init: np.ndarray = HEIGHT_INIT, - smooth_from: Optional[np.ndarray] = None) -> None: + smooth_from: np.ndarray = None) -> None: if not np.isfinite(rpy_init).all(): self.rpy = RPY_INIT.copy() else: @@ -125,7 +125,7 @@ class Calibrator: self.old_rpy = smooth_from self.old_rpy_weight = 1.0 - def get_valid_idxs(self) -> List[int]: + def get_valid_idxs(self) -> list[int]: # exclude current block_idx from validity window before_current = list(range(self.block_idx)) after_current = list(range(min(self.valid_blocks, self.block_idx + 1), self.valid_blocks)) @@ -175,12 +175,12 @@ class Calibrator: else: return self.rpy - def handle_cam_odom(self, trans: List[float], - rot: List[float], - wide_from_device_euler: List[float], - trans_std: List[float], - road_transform_trans: List[float], - road_transform_trans_std: List[float]) -> Optional[np.ndarray]: + def handle_cam_odom(self, trans: list[float], + rot: list[float], + wide_from_device_euler: list[float], + trans_std: list[float], + road_transform_trans: list[float], + road_transform_trans_std: list[float]) -> np.ndarray | None: self.old_rpy_weight = max(0.0, self.old_rpy_weight - 1/SMOOTH_CYCLES) straight_and_fast = ((self.v_ego > MIN_SPEED_FILTER) and (trans[0] > MIN_SPEED_FILTER) and (abs(rot[2]) < MAX_YAW_RATE_FILTER)) diff --git a/selfdrive/locationd/helpers.py b/selfdrive/locationd/helpers.py index c292e9886c..786bdbbfec 100644 --- a/selfdrive/locationd/helpers.py +++ b/selfdrive/locationd/helpers.py @@ -1,5 +1,5 @@ import numpy as np -from typing import List, Optional, Tuple, Any +from typing import Any from cereal import log @@ -12,7 +12,7 @@ class NPQueue: def __len__(self) -> int: return len(self.arr) - def append(self, pt: List[float]) -> None: + def append(self, pt: list[float]) -> None: if len(self.arr) < self.maxlen: self.arr = np.append(self.arr, [pt], axis=0) else: @@ -21,7 +21,7 @@ class NPQueue: class PointBuckets: - def __init__(self, x_bounds: List[Tuple[float, float]], min_points: List[float], min_points_total: int, points_per_bucket: int, rowsize: int) -> None: + def __init__(self, x_bounds: list[tuple[float, float]], min_points: list[float], min_points_total: int, points_per_bucket: int, rowsize: int) -> None: self.x_bounds = x_bounds self.buckets = {bounds: NPQueue(maxlen=points_per_bucket, rowsize=rowsize) for bounds in x_bounds} self.buckets_min_points = dict(zip(x_bounds, min_points, strict=True)) @@ -41,13 +41,13 @@ class PointBuckets: def add_point(self, x: float, y: float, bucket_val: float) -> None: raise NotImplementedError - def get_points(self, num_points: Optional[int] = None) -> Any: + def get_points(self, num_points: int = None) -> Any: points = np.vstack([x.arr for x in self.buckets.values()]) if num_points is None: return points return points[np.random.choice(np.arange(len(points)), min(len(points), num_points), replace=False)] - def load_points(self, points: List[List[float]]) -> None: + def load_points(self, points: list[list[float]]) -> None: for point in points: self.add_point(*point) diff --git a/selfdrive/locationd/locationd.cc b/selfdrive/locationd/locationd.cc index e32ed78a3e..1050d68b9b 100644 --- a/selfdrive/locationd/locationd.cc +++ b/selfdrive/locationd/locationd.cc @@ -691,10 +691,9 @@ int Localizer::locationd_thread() { this->configure_gnss_source(source); const std::initializer_list service_list = {gps_location_socket, "cameraOdometry", "liveCalibration", - "carState", "carParams", "accelerometer", "gyroscope"}; + "carState", "accelerometer", "gyroscope"}; - // TODO: remove carParams once we're always sending at 100Hz - SubMaster sm(service_list, {}, nullptr, {gps_location_socket, "carParams"}); + SubMaster sm(service_list, {}, nullptr, {gps_location_socket}); PubMaster pm({"liveLocationKalman"}); uint64_t cnt = 0; @@ -718,8 +717,7 @@ int Localizer::locationd_thread() { filterInitialized = sm.allAliveAndValid(); } - // 100Hz publish for notcars, 20Hz for cars - const char* trigger_msg = sm["carParams"].getCarParams().getNotCar() ? "accelerometer" : "cameraOdometry"; + const char* trigger_msg = "cameraOdometry"; if (sm.updated(trigger_msg)) { bool inputsOK = sm.allValid() && this->are_inputs_ok(); bool gpsOK = this->is_gps_ok(); diff --git a/selfdrive/locationd/models/car_kf.py b/selfdrive/locationd/models/car_kf.py index 9230cb48f0..1f3e447a19 100755 --- a/selfdrive/locationd/models/car_kf.py +++ b/selfdrive/locationd/models/car_kf.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import math import sys -from typing import Any, Dict +from typing import Any import numpy as np @@ -70,7 +70,7 @@ class CarKalman(KalmanFilter): ]) P_initial = Q.copy() - obs_noise: Dict[int, Any] = { + obs_noise: dict[int, Any] = { ObservationKind.STEER_ANGLE: np.atleast_2d(math.radians(0.05)**2), ObservationKind.ANGLE_OFFSET_FAST: np.atleast_2d(math.radians(10.0)**2), ObservationKind.ROAD_ROLL: np.atleast_2d(math.radians(1.0)**2), diff --git a/selfdrive/manager/build.py b/selfdrive/manager/build.py index f2758cf32b..067e1b5a1e 100755 --- a/selfdrive/manager/build.py +++ b/selfdrive/manager/build.py @@ -2,7 +2,6 @@ import os import subprocess from pathlib import Path -from typing import List # NOTE: Do NOT import anything here that needs be built (e.g. params) from openpilot.common.basedir import BASEDIR @@ -29,7 +28,7 @@ def build(spinner: Spinner, dirty: bool = False, minimal: bool = False) -> None: # building with all cores can result in using too # much memory, so retry with less parallelism - compile_output: List[bytes] = [] + compile_output: list[bytes] = [] for n in (nproc, nproc/2, 1): compile_output.clear() scons: subprocess.Popen = subprocess.Popen(["scons", f"-j{int(n)}", "--cache-populate", *extra_args], cwd=BASEDIR, env=env, stderr=subprocess.PIPE) diff --git a/selfdrive/manager/manager.py b/selfdrive/manager/manager.py index 2853e1e480..944fb12789 100755 --- a/selfdrive/manager/manager.py +++ b/selfdrive/manager/manager.py @@ -4,7 +4,6 @@ import os import signal import sys import traceback -from typing import List, Tuple, Union from cereal import custom import cereal.messaging as messaging @@ -33,7 +32,7 @@ def manager_init() -> None: if is_release_branch(): params.clear_all(ParamKeyType.DEVELOPMENT_ONLY) - default_params: List[Tuple[str, Union[str, bytes]]] = [ + default_params: list[tuple[str, str | bytes]] = [ ("CompletedTrainingVersion", "0"), ("DisengageOnAccelerator", "0"), ("GsmMetered", "1"), @@ -200,7 +199,7 @@ def manager_thread() -> None: params = Params() - ignore: List[str] = [] + ignore: list[str] = [] if params.get("DongleId", encoding='utf8') in (None, UNREGISTERED_DONGLE_ID): ignore += ["manage_athenad", "uploader"] if os.getenv("NOBOARD") is not None: @@ -235,7 +234,7 @@ def manager_thread() -> None: ensure_running(managed_processes.values(), started, params=params, CP=sm['carParams'], not_run=ignore) - running = ' '.join("%s%s\u001b[0m" % ("\u001b[32m" if p.proc.is_alive() else "\u001b[31m", p.name) + running = ' '.join("{}{}\u001b[0m".format("\u001b[32m" if p.proc.is_alive() else "\u001b[31m", p.name) for p in managed_processes.values() if p.proc) print(running) cloudlog.debug(running) diff --git a/selfdrive/manager/process.py b/selfdrive/manager/process.py index 844e074fc5..4d38888619 100644 --- a/selfdrive/manager/process.py +++ b/selfdrive/manager/process.py @@ -4,7 +4,7 @@ import signal import struct import time import subprocess -from typing import Optional, Callable, List, ValuesView +from collections.abc import Callable, ValuesView from abc import ABC, abstractmethod from multiprocessing import Process @@ -47,7 +47,7 @@ def launcher(proc: str, name: str) -> None: raise -def nativelauncher(pargs: List[str], cwd: str, name: str) -> None: +def nativelauncher(pargs: list[str], cwd: str, name: str) -> None: os.environ['MANAGER_DAEMON'] = name # exec the process @@ -67,12 +67,12 @@ class ManagerProcess(ABC): daemon = False sigkill = False should_run: Callable[[bool, Params, car.CarParams], bool] - proc: Optional[Process] = None + proc: Process | None = None enabled = True name = "" last_watchdog_time = 0 - watchdog_max_dt: Optional[int] = None + watchdog_max_dt: int | None = None always_watchdog = False watchdog_seen = False shutting_down = False @@ -112,7 +112,7 @@ class ManagerProcess(ABC): else: self.watchdog_seen = True - def stop(self, retry: bool = True, block: bool = True, sig: Optional[signal.Signals] = None) -> Optional[int]: + def stop(self, retry: bool = True, block: bool = True, sig: signal.Signals = None) -> int | None: if self.proc is None: return None @@ -279,7 +279,7 @@ class DaemonProcess(ManagerProcess): def ensure_running(procs: ValuesView[ManagerProcess], started: bool, params=None, CP: car.CarParams=None, - not_run: Optional[List[str]]=None) -> List[ManagerProcess]: + not_run: list[str] | None=None) -> list[ManagerProcess]: if not_run is None: not_run = [] diff --git a/selfdrive/modeld/dmonitoringmodeld.py b/selfdrive/modeld/dmonitoringmodeld.py index 1e25964702..ef403b44fc 100755 --- a/selfdrive/modeld/dmonitoringmodeld.py +++ b/selfdrive/modeld/dmonitoringmodeld.py @@ -6,7 +6,6 @@ import time import ctypes import numpy as np from pathlib import Path -from typing import Tuple, Dict from cereal import messaging from cereal.messaging import PubMaster, SubMaster @@ -53,7 +52,7 @@ class DMonitoringModelResult(ctypes.Structure): ("wheel_on_right_prob", ctypes.c_float)] class ModelState: - inputs: Dict[str, np.ndarray] + inputs: dict[str, np.ndarray] output: np.ndarray model: ModelRunner @@ -68,7 +67,7 @@ class ModelState: self.model.addInput("input_img", None) self.model.addInput("calib", self.inputs['calib']) - def run(self, buf:VisionBuf, calib:np.ndarray) -> Tuple[np.ndarray, float]: + def run(self, buf:VisionBuf, calib:np.ndarray) -> tuple[np.ndarray, float]: self.inputs['calib'][:] = calib v_offset = buf.height - MODEL_HEIGHT diff --git a/selfdrive/modeld/fill_model_msg.py b/selfdrive/modeld/fill_model_msg.py index b51e66fce4..738987f061 100644 --- a/selfdrive/modeld/fill_model_msg.py +++ b/selfdrive/modeld/fill_model_msg.py @@ -1,7 +1,6 @@ import os import capnp import numpy as np -from typing import Dict from cereal import log from openpilot.selfdrive.modeld.constants import ModelConstants, Plan, Meta @@ -43,7 +42,7 @@ def fill_xyvat(builder, t, x, y, v, a, x_std=None, y_std=None, v_std=None, a_std if a_std is not None: builder.aStd = a_std.tolist() -def fill_model_msg(msg: capnp._DynamicStructBuilder, net_output_data: Dict[str, np.ndarray], publish_state: PublishState, +def fill_model_msg(msg: capnp._DynamicStructBuilder, net_output_data: dict[str, np.ndarray], publish_state: PublishState, vipc_frame_id: int, vipc_frame_id_extra: int, frame_id: int, frame_drop: float, timestamp_eof: int, timestamp_llk: int, model_execution_time: float, nav_enabled: bool, valid: bool, model_use_lateral_planner: bool) -> None: @@ -180,7 +179,7 @@ def fill_model_msg(msg: capnp._DynamicStructBuilder, net_output_data: Dict[str, if SEND_RAW_PRED: modelV2.rawPredictions = net_output_data['raw_pred'].tobytes() -def fill_pose_msg(msg: capnp._DynamicStructBuilder, net_output_data: Dict[str, np.ndarray], +def fill_pose_msg(msg: capnp._DynamicStructBuilder, net_output_data: dict[str, np.ndarray], vipc_frame_id: int, vipc_dropped_frames: int, timestamp_eof: int, live_calib_seen: bool) -> None: msg.valid = live_calib_seen & (vipc_dropped_frames < 1) cameraOdometry = msg.cameraOdometry diff --git a/selfdrive/modeld/get_model_metadata.py b/selfdrive/modeld/get_model_metadata.py index 187f83399b..144860204f 100755 --- a/selfdrive/modeld/get_model_metadata.py +++ b/selfdrive/modeld/get_model_metadata.py @@ -4,9 +4,8 @@ import pathlib import onnx import codecs import pickle -from typing import Tuple -def get_name_and_shape(value_info:onnx.ValueInfoProto) -> Tuple[str, Tuple[int,...]]: +def get_name_and_shape(value_info:onnx.ValueInfoProto) -> tuple[str, tuple[int,...]]: shape = tuple([int(dim.dim_value) for dim in value_info.type.tensor_type.shape.dim]) name = value_info.name return name, shape diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 4f895a0ee4..9bc9dbaf21 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -6,7 +6,6 @@ import numpy as np import cereal.messaging as messaging from cereal import car, log from pathlib import Path -from typing import Dict, Optional from setproctitle import setproctitle from cereal.messaging import PubMaster, SubMaster from cereal.visionipc import VisionIpcClient, VisionStreamType, VisionBuf @@ -53,7 +52,7 @@ class FrameMeta: class ModelState: frame: ModelFrame wide_frame: ModelFrame - inputs: Dict[str, np.ndarray] + inputs: dict[str, np.ndarray] output: np.ndarray prev_desire: np.ndarray # for tracking the rising edge of the pulse model: ModelRunner @@ -111,14 +110,14 @@ class ModelState: for k,v in self.inputs.items(): self.model.addInput(k, v) - def slice_outputs(self, model_outputs: np.ndarray) -> Dict[str, np.ndarray]: + def slice_outputs(self, model_outputs: np.ndarray) -> dict[str, np.ndarray]: parsed_model_outputs = {k: model_outputs[np.newaxis, v] for k,v in self.output_slices.items()} if SEND_RAW_PRED: parsed_model_outputs['raw_pred'] = model_outputs.copy() return parsed_model_outputs def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_wide: np.ndarray, - inputs: Dict[str, np.ndarray], prepare_only: bool) -> Optional[Dict[str, np.ndarray]]: + inputs: dict[str, np.ndarray], prepare_only: bool) -> dict[str, np.ndarray] | None: # Model decides when action is completed, so desire input is just a pulse triggered on rising edge inputs['desire'][0] = 0 self.inputs['desire'][:-ModelConstants.DESIRE_LEN] = self.inputs['desire'][ModelConstants.DESIRE_LEN:] @@ -331,7 +330,7 @@ def main(demo=False): 'lateral_control_params': lateral_control_params } - inputs:Dict[str, np.ndarray] = { + inputs:dict[str, np.ndarray] = { 'desire': vec_desire, 'traffic_convention': traffic_convention, **_inputs, diff --git a/selfdrive/modeld/navmodeld.py b/selfdrive/modeld/navmodeld.py index 47439dbab6..57ff50c624 100755 --- a/selfdrive/modeld/navmodeld.py +++ b/selfdrive/modeld/navmodeld.py @@ -5,7 +5,6 @@ import time import ctypes import numpy as np from pathlib import Path -from typing import Tuple, Dict from cereal import messaging from cereal.messaging import PubMaster, SubMaster @@ -44,7 +43,7 @@ class NavModelResult(ctypes.Structure): ("features", ctypes.c_float*NAV_FEATURE_LEN)] class ModelState: - inputs: Dict[str, np.ndarray] + inputs: dict[str, np.ndarray] output: np.ndarray model: ModelRunner @@ -63,7 +62,7 @@ class ModelState: self.model = ModelRunner(_model_paths, self.output, Runtime.DSP, True, None) self.model.addInput("input_img", None) - def run(self, buf:np.ndarray) -> Tuple[np.ndarray, float]: + def run(self, buf:np.ndarray) -> tuple[np.ndarray, float]: self.inputs['input_img'][:] = buf t1 = time.perf_counter() diff --git a/selfdrive/modeld/parse_model_outputs.py b/selfdrive/modeld/parse_model_outputs.py index 01cba29d1c..af57e11d03 100644 --- a/selfdrive/modeld/parse_model_outputs.py +++ b/selfdrive/modeld/parse_model_outputs.py @@ -1,5 +1,4 @@ import numpy as np -from typing import Dict from openpilot.selfdrive.modeld.constants import ModelConstants def sigmoid(x): @@ -82,7 +81,7 @@ class Parser: outs[name] = pred_mu_final.reshape(final_shape) outs[name + '_stds'] = pred_std_final.reshape(final_shape) - def parse_outputs(self, outs: Dict[str, np.ndarray]) -> Dict[str, np.ndarray]: + def parse_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]: 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)) self.parse_mdn('lane_lines', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_LANE_LINES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH)) diff --git a/selfdrive/modeld/runners/onnxmodel.py b/selfdrive/modeld/runners/onnxmodel.py index f86bee3878..69b44a5a97 100644 --- a/selfdrive/modeld/runners/onnxmodel.py +++ b/selfdrive/modeld/runners/onnxmodel.py @@ -3,7 +3,7 @@ import itertools import os import sys import numpy as np -from typing import Tuple, Dict, Union, Any +from typing import Any from openpilot.selfdrive.modeld.runners.runmodel_pyx import RunModel @@ -38,7 +38,7 @@ def create_ort_session(path, fp16_to_fp32): options = ort.SessionOptions() options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_DISABLE_ALL - provider: Union[str, Tuple[str, Dict[Any, Any]]] + provider: str | tuple[str, dict[Any, Any]] if 'OpenVINOExecutionProvider' in ort.get_available_providers() and 'ONNXCPU' not in os.environ: provider = 'OpenVINOExecutionProvider' elif 'CUDAExecutionProvider' in ort.get_available_providers() and 'ONNXCPU' not in os.environ: diff --git a/selfdrive/navd/helpers.py b/selfdrive/navd/helpers.py index 55c3f88a9a..0f0410c2c7 100644 --- a/selfdrive/navd/helpers.py +++ b/selfdrive/navd/helpers.py @@ -2,7 +2,7 @@ from __future__ import annotations import json import math -from typing import Any, Dict, List, Optional, Tuple, Union, cast +from typing import Any, cast from openpilot.common.conversions import Conversions from openpilot.common.numpy_fast import clip @@ -22,13 +22,13 @@ class Coordinate: def __init__(self, latitude: float, longitude: float) -> None: self.latitude = latitude self.longitude = longitude - self.annotations: Dict[str, float] = {} + self.annotations: dict[str, float] = {} @classmethod - def from_mapbox_tuple(cls, t: Tuple[float, float]) -> Coordinate: + def from_mapbox_tuple(cls, t: tuple[float, float]) -> Coordinate: return cls(t[1], t[0]) - def as_dict(self) -> Dict[str, float]: + def as_dict(self) -> dict[str, float]: return {'latitude': self.latitude, 'longitude': self.longitude} def __str__(self) -> str: @@ -83,7 +83,7 @@ def minimum_distance(a: Coordinate, b: Coordinate, p: Coordinate): return projection.distance_to(p) -def distance_along_geometry(geometry: List[Coordinate], pos: Coordinate) -> float: +def distance_along_geometry(geometry: list[Coordinate], pos: Coordinate) -> float: if len(geometry) <= 2: return geometry[0].distance_to(pos) @@ -106,7 +106,7 @@ def distance_along_geometry(geometry: List[Coordinate], pos: Coordinate) -> floa return total_distance_closest -def coordinate_from_param(param: str, params: Optional[Params] = None) -> Optional[Coordinate]: +def coordinate_from_param(param: str, params: Params = None) -> Coordinate | None: if params is None: params = Params() @@ -130,7 +130,7 @@ def string_to_direction(direction: str) -> str: return 'none' -def maxspeed_to_ms(maxspeed: Dict[str, Union[str, float]]) -> float: +def maxspeed_to_ms(maxspeed: dict[str, str | float]) -> float: unit = cast(str, maxspeed['unit']) speed = cast(float, maxspeed['speed']) return SPEED_CONVERSIONS[unit] * speed @@ -140,7 +140,7 @@ def field_valid(dat: dict, field: str) -> bool: return field in dat and dat[field] is not None -def parse_banner_instructions(banners: Any, distance_to_maneuver: float = 0.0) -> Optional[Dict[str, Any]]: +def parse_banner_instructions(banners: Any, distance_to_maneuver: float = 0.0) -> dict[str, Any] | None: if not len(banners): return None diff --git a/selfdrive/statsd.py b/selfdrive/statsd.py index 94572b82c7..299aa295d7 100755 --- a/selfdrive/statsd.py +++ b/selfdrive/statsd.py @@ -5,7 +5,7 @@ import time from pathlib import Path from collections import defaultdict from datetime import datetime, timezone -from typing import NoReturn, Union, List, Dict +from typing import NoReturn from openpilot.common.params import Params from cereal.messaging import SubMaster @@ -61,7 +61,7 @@ class StatLog: def main() -> NoReturn: dongle_id = Params().get("DongleId", encoding='utf-8') - def get_influxdb_line(measurement: str, value: Union[float, Dict[str, float]], timestamp: datetime, tags: dict) -> str: + def get_influxdb_line(measurement: str, value: float | dict[str, float], timestamp: datetime, tags: dict) -> str: res = f"{measurement}" for k, v in tags.items(): res += f",{k}={str(v)}" @@ -102,7 +102,7 @@ def main() -> NoReturn: idx = 0 last_flush_time = time.monotonic() gauges = {} - samples: Dict[str, List[float]] = defaultdict(list) + samples: dict[str, list[float]] = defaultdict(list) try: while True: started_prev = sm['deviceState'].started diff --git a/selfdrive/test/ciui.py b/selfdrive/test/ciui.py index 3f33847b29..f3b0c1a98f 100755 --- a/selfdrive/test/ciui.py +++ b/selfdrive/test/ciui.py @@ -11,7 +11,7 @@ from openpilot.selfdrive.ui.qt.python_helpers import set_main_window class Window(QWidget): def __init__(self, parent=None): - super(Window, self).__init__(parent) + super().__init__(parent) layout = QVBoxLayout() self.setLayout(layout) @@ -47,7 +47,7 @@ class Window(QWidget): def update(self): for cmd, label in self.labels.items(): - out = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + out = subprocess.run(cmd, capture_output=True, shell=True, check=False, encoding='utf8').stdout label.setText(out.strip()) diff --git a/selfdrive/test/fuzzy_generation.py b/selfdrive/test/fuzzy_generation.py index 28c70a0ff4..26c35c0c18 100644 --- a/selfdrive/test/fuzzy_generation.py +++ b/selfdrive/test/fuzzy_generation.py @@ -1,6 +1,7 @@ import capnp import hypothesis.strategies as st -from typing import Any, Callable, Dict, List, Optional, Union +from typing import Any +from collections.abc import Callable from cereal import log @@ -12,7 +13,7 @@ class FuzzyGenerator: self.draw = draw self.real_floats = real_floats - def generate_native_type(self, field: str) -> st.SearchStrategy[Union[bool, int, float, str, bytes]]: + def generate_native_type(self, field: str) -> st.SearchStrategy[bool | int | float | str | bytes]: def floats(**kwargs) -> st.SearchStrategy[float]: allow_nan = not self.real_floats allow_infinity = not self.real_floats @@ -67,18 +68,18 @@ class FuzzyGenerator: else: return self.generate_struct(field.schema) - def generate_struct(self, schema: capnp.lib.capnp._StructSchema, event: Optional[str] = None) -> st.SearchStrategy[Dict[str, Any]]: - full_fill: List[str] = list(schema.non_union_fields) - single_fill: List[str] = [event] if event else [self.draw(st.sampled_from(schema.union_fields))] if schema.union_fields else [] + def generate_struct(self, schema: capnp.lib.capnp._StructSchema, event: str = None) -> st.SearchStrategy[dict[str, Any]]: + full_fill: list[str] = list(schema.non_union_fields) + single_fill: list[str] = [event] if event else [self.draw(st.sampled_from(schema.union_fields))] if schema.union_fields else [] return st.fixed_dictionaries({field: self.generate_field(schema.fields[field]) for field in full_fill + single_fill}) @classmethod - def get_random_msg(cls, draw: DrawType, struct: capnp.lib.capnp._StructModule, real_floats: bool = False) -> Dict[str, Any]: + def get_random_msg(cls, draw: DrawType, struct: capnp.lib.capnp._StructModule, real_floats: bool = False) -> dict[str, Any]: fg = cls(draw, real_floats=real_floats) - data: Dict[str, Any] = draw(fg.generate_struct(struct.schema)) + data: dict[str, Any] = draw(fg.generate_struct(struct.schema)) return data @classmethod - def get_random_event_msg(cls, draw: DrawType, events: List[str], real_floats: bool = False) -> List[Dict[str, Any]]: + def get_random_event_msg(cls, draw: DrawType, events: list[str], real_floats: bool = False) -> list[dict[str, Any]]: fg = cls(draw, real_floats=real_floats) return [draw(fg.generate_struct(log.Event.schema, e)) for e in sorted(events)] diff --git a/selfdrive/test/helpers.py b/selfdrive/test/helpers.py index 0e7912a989..210a283699 100644 --- a/selfdrive/test/helpers.py +++ b/selfdrive/test/helpers.py @@ -1,4 +1,6 @@ +import http.server import os +import threading import time from functools import wraps @@ -72,7 +74,29 @@ def noop(*args, **kwargs): def read_segment_list(segment_list_path): - with open(segment_list_path, "r") as f: + with open(segment_list_path) as f: seg_list = f.read().splitlines() return [(platform[2:], segment) for platform, segment in zip(seg_list[::2], seg_list[1::2], strict=True)] + + +def with_http_server(func, handler=http.server.BaseHTTPRequestHandler, setup=None): + @wraps(func) + def inner(*args, **kwargs): + host = '127.0.0.1' + server = http.server.HTTPServer((host, 0), handler) + port = server.server_port + t = threading.Thread(target=server.serve_forever) + t.start() + + if setup is not None: + setup(host, port) + + try: + return func(*args, f'http://{host}:{port}', **kwargs) + finally: + server.shutdown() + server.server_close() + t.join() + + return inner diff --git a/selfdrive/test/process_replay/capture.py b/selfdrive/test/process_replay/capture.py index 28206c6b91..90c279ef35 100644 --- a/selfdrive/test/process_replay/capture.py +++ b/selfdrive/test/process_replay/capture.py @@ -1,7 +1,7 @@ import os import sys -from typing import Tuple, no_type_check +from typing import no_type_check class FdRedirect: def __init__(self, file_prefix: str, fd: int): @@ -53,7 +53,7 @@ class ProcessOutputCapture: self.stdout_redirect.link() self.stderr_redirect.link() - def read_outerr(self) -> Tuple[str, str]: + def read_outerr(self) -> tuple[str, str]: out_str = self.stdout_redirect.read().decode() err_str = self.stderr_redirect.read().decode() return out_str, err_str diff --git a/selfdrive/test/process_replay/compare_logs.py b/selfdrive/test/process_replay/compare_logs.py index dbb7c223f5..673f3b484c 100755 --- a/selfdrive/test/process_replay/compare_logs.py +++ b/selfdrive/test/process_replay/compare_logs.py @@ -5,7 +5,6 @@ import capnp import numbers import dictdiffer from collections import Counter -from typing import Dict from openpilot.tools.lib.logreader import LogReader @@ -97,7 +96,7 @@ def format_process_diff(diff): diff_short += f" {diff}\n" diff_long += f"\t{diff}\n" else: - cnt: Dict[str, int] = {} + cnt: dict[str, int] = {} for d in diff: diff_long += f"\t{str(d)}\n" diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 77ac3f551e..5119be0a8c 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -8,7 +8,8 @@ import signal import platform from collections import OrderedDict from dataclasses import dataclass, field -from typing import Dict, List, Optional, Callable, Union, Any, Iterable, Tuple +from typing import Any +from collections.abc import Callable, Iterable from tqdm import tqdm import capnp @@ -36,9 +37,9 @@ FAKEDATA = os.path.join(PROC_REPLAY_DIR, "fakedata/") class DummySocket: def __init__(self): - self.data: List[bytes] = [] + self.data: list[bytes] = [] - def receive(self, non_blocking: bool = False) -> Optional[bytes]: + def receive(self, non_blocking: bool = False) -> bytes | None: if non_blocking: return None @@ -128,21 +129,21 @@ class ReplayContext: @dataclass class ProcessConfig: proc_name: str - pubs: List[str] - subs: List[str] - ignore: List[str] - config_callback: Optional[Callable] = None - init_callback: Optional[Callable] = None - should_recv_callback: Optional[Callable] = None - tolerance: Optional[float] = None + pubs: list[str] + subs: list[str] + ignore: list[str] + config_callback: Callable | None = None + init_callback: Callable | None = None + should_recv_callback: Callable | None = None + tolerance: float | None = None processing_time: float = 0.001 timeout: int = 30 simulation: bool = True - main_pub: Optional[str] = None + main_pub: str | None = None main_pub_drained: bool = True - vision_pubs: List[str] = field(default_factory=list) - ignore_alive_pubs: List[str] = field(default_factory=list) - unlocked_pubs: List[str] = field(default_factory=list) + vision_pubs: list[str] = field(default_factory=list) + ignore_alive_pubs: list[str] = field(default_factory=list) + unlocked_pubs: list[str] = field(default_factory=list) class ProcessContainer: @@ -150,25 +151,25 @@ class ProcessContainer: self.prefix = OpenpilotPrefix(clean_dirs_on_exit=False) self.cfg = copy.deepcopy(cfg) self.process = copy.deepcopy(managed_processes[cfg.proc_name]) - self.msg_queue: List[capnp._DynamicStructReader] = [] + self.msg_queue: list[capnp._DynamicStructReader] = [] self.cnt = 0 - self.pm: Optional[messaging.PubMaster] = None - self.sockets: Optional[List[messaging.SubSocket]] = None - self.rc: Optional[ReplayContext] = None - self.vipc_server: Optional[VisionIpcServer] = None - self.environ_config: Optional[Dict[str, Any]] = None - self.capture: Optional[ProcessOutputCapture] = None + self.pm: messaging.PubMaster | None = None + self.sockets: list[messaging.SubSocket] | None = None + self.rc: ReplayContext | None = None + self.vipc_server: VisionIpcServer | None = None + self.environ_config: dict[str, Any] | None = None + self.capture: ProcessOutputCapture | None = None @property def has_empty_queue(self) -> bool: return len(self.msg_queue) == 0 @property - def pubs(self) -> List[str]: + def pubs(self) -> list[str]: return self.cfg.pubs @property - def subs(self) -> List[str]: + def subs(self) -> list[str]: return self.cfg.subs def _clean_env(self): @@ -180,7 +181,7 @@ class ProcessContainer: if k in os.environ: del os.environ[k] - def _setup_env(self, params_config: Dict[str, Any], environ_config: Dict[str, Any]): + def _setup_env(self, params_config: dict[str, Any], environ_config: dict[str, Any]): for k, v in environ_config.items(): if len(v) != 0: os.environ[k] = v @@ -202,7 +203,7 @@ class ProcessContainer: self.environ_config = environ_config - def _setup_vision_ipc(self, all_msgs: LogIterable, frs: Dict[str, Any]): + def _setup_vision_ipc(self, all_msgs: LogIterable, frs: dict[str, Any]): assert len(self.cfg.vision_pubs) != 0 vipc_server = VisionIpcServer("camerad") @@ -223,9 +224,9 @@ class ProcessContainer: self.process.start() def start( - self, params_config: Dict[str, Any], environ_config: Dict[str, Any], - all_msgs: LogIterable, frs: Optional[Dict[str, BaseFrameReader]], - fingerprint: Optional[str], capture_output: bool + self, params_config: dict[str, Any], environ_config: dict[str, Any], + all_msgs: LogIterable, frs: dict[str, BaseFrameReader] | None, + fingerprint: str | None, capture_output: bool ): with self.prefix as p: self._setup_env(params_config, environ_config) @@ -266,7 +267,7 @@ class ProcessContainer: self.prefix.clean_dirs() self._clean_env() - def run_step(self, msg: capnp._DynamicStructReader, frs: Optional[Dict[str, BaseFrameReader]]) -> List[capnp._DynamicStructReader]: + def run_step(self, msg: capnp._DynamicStructReader, frs: dict[str, BaseFrameReader] | None) -> list[capnp._DynamicStructReader]: assert self.rc and self.pm and self.sockets and self.process.proc output_msgs = [] @@ -511,7 +512,7 @@ CONFIGS = [ proc_name="locationd", pubs=[ "cameraOdometry", "accelerometer", "gyroscope", "gpsLocationExternal", - "liveCalibration", "carState", "carParams", "gpsLocation" + "liveCalibration", "carState", "gpsLocation" ], subs=["liveLocationKalman"], ignore=["logMonoTime"], @@ -580,7 +581,7 @@ def get_process_config(name: str) -> ProcessConfig: raise Exception(f"Cannot find process config with name: {name}") from ex -def get_custom_params_from_lr(lr: LogIterable, initial_state: str = "first") -> Dict[str, Any]: +def get_custom_params_from_lr(lr: LogIterable, initial_state: str = "first") -> dict[str, Any]: """ Use this to get custom params dict based on provided logs. Useful when replaying following processes: calibrationd, paramsd, torqued @@ -614,7 +615,7 @@ def get_custom_params_from_lr(lr: LogIterable, initial_state: str = "first") -> return custom_params -def replay_process_with_name(name: Union[str, Iterable[str]], lr: LogIterable, *args, **kwargs) -> List[capnp._DynamicStructReader]: +def replay_process_with_name(name: str | Iterable[str], lr: LogIterable, *args, **kwargs) -> list[capnp._DynamicStructReader]: if isinstance(name, str): cfgs = [get_process_config(name)] elif isinstance(name, Iterable): @@ -626,10 +627,10 @@ def replay_process_with_name(name: Union[str, Iterable[str]], lr: LogIterable, * def replay_process( - cfg: Union[ProcessConfig, Iterable[ProcessConfig]], lr: LogIterable, frs: Optional[Dict[str, BaseFrameReader]] = None, - fingerprint: Optional[str] = None, return_all_logs: bool = False, custom_params: Optional[Dict[str, Any]] = None, - captured_output_store: Optional[Dict[str, Dict[str, str]]] = None, disable_progress: bool = False -) -> List[capnp._DynamicStructReader]: + cfg: ProcessConfig | Iterable[ProcessConfig], lr: LogIterable, frs: dict[str, BaseFrameReader] = None, + fingerprint: str = None, return_all_logs: bool = False, custom_params: dict[str, Any] = None, + captured_output_store: dict[str, dict[str, str]] = None, disable_progress: bool = False +) -> list[capnp._DynamicStructReader]: if isinstance(cfg, Iterable): cfgs = list(cfg) else: @@ -654,9 +655,9 @@ def replay_process( def _replay_multi_process( - cfgs: List[ProcessConfig], lr: LogIterable, frs: Optional[Dict[str, BaseFrameReader]], fingerprint: Optional[str], - custom_params: Optional[Dict[str, Any]], captured_output_store: Optional[Dict[str, Dict[str, str]]], disable_progress: bool -) -> List[capnp._DynamicStructReader]: + cfgs: list[ProcessConfig], lr: LogIterable, frs: dict[str, BaseFrameReader] | None, fingerprint: str | None, + custom_params: dict[str, Any] | None, captured_output_store: dict[str, dict[str, str]] | None, disable_progress: bool +) -> list[capnp._DynamicStructReader]: if fingerprint is not None: params_config = generate_params_config(lr=lr, fingerprint=fingerprint, custom_params=custom_params) env_config = generate_environ_config(fingerprint=fingerprint) @@ -690,10 +691,10 @@ def _replay_multi_process( pub_msgs = [msg for msg in all_msgs if msg.which() in lr_pubs] # external queue for messages taken from logs; internal queue for messages generated by processes, which will be republished - external_pub_queue: List[capnp._DynamicStructReader] = pub_msgs.copy() - internal_pub_queue: List[capnp._DynamicStructReader] = [] + external_pub_queue: list[capnp._DynamicStructReader] = pub_msgs.copy() + internal_pub_queue: list[capnp._DynamicStructReader] = [] # heap for maintaining the order of messages generated by processes, where each element: (logMonoTime, index in internal_pub_queue) - internal_pub_index_heap: List[Tuple[int, int]] = [] + internal_pub_index_heap: list[tuple[int, int]] = [] pbar = tqdm(total=len(external_pub_queue), disable=disable_progress) while len(external_pub_queue) != 0 or (len(internal_pub_index_heap) != 0 and not all(c.has_empty_queue for c in containers)): @@ -723,7 +724,7 @@ def _replay_multi_process( return log_msgs -def generate_params_config(lr=None, CP=None, fingerprint=None, custom_params=None) -> Dict[str, Any]: +def generate_params_config(lr=None, CP=None, fingerprint=None, custom_params=None) -> dict[str, Any]: params_dict = { "OpenpilotEnabledToggle": True, "DisengageOnAccelerator": True, @@ -755,7 +756,7 @@ def generate_params_config(lr=None, CP=None, fingerprint=None, custom_params=Non return params_dict -def generate_environ_config(CP=None, fingerprint=None, log_dir=None) -> Dict[str, Any]: +def generate_environ_config(CP=None, fingerprint=None, log_dir=None) -> dict[str, Any]: environ_dict = {} if platform.system() != "Darwin": environ_dict["PARAMS_ROOT"] = "/dev/shm/params" diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index fc38f87310..9c627fb7c9 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -d0cdea7eb15f3cac8a921f7ace3eaa6baebb4fd5 +47609e372bf616932c4dca74d2616c3d97fa2443 diff --git a/selfdrive/test/process_replay/regen.py b/selfdrive/test/process_replay/regen.py index 245b5b2709..8e882207b5 100755 --- a/selfdrive/test/process_replay/regen.py +++ b/selfdrive/test/process_replay/regen.py @@ -5,7 +5,8 @@ import time import capnp import numpy as np -from typing import Union, Iterable, Optional, List, Any, Dict, Tuple +from typing import Any +from collections.abc import Iterable from openpilot.selfdrive.test.process_replay.process_replay import CONFIGS, FAKEDATA, ProcessConfig, replay_process, get_process_config, \ check_openpilot_enabled, get_custom_params_from_lr @@ -40,9 +41,9 @@ class DummyFrameReader(BaseFrameReader): def regen_segment( - lr: LogIterable, frs: Optional[Dict[str, Any]] = None, + lr: LogIterable, frs: dict[str, Any] = None, processes: Iterable[ProcessConfig] = CONFIGS, disable_tqdm: bool = False -) -> List[capnp._DynamicStructReader]: +) -> list[capnp._DynamicStructReader]: all_msgs = sorted(lr, key=lambda m: m.logMonoTime) custom_params = get_custom_params_from_lr(all_msgs) @@ -57,7 +58,7 @@ def regen_segment( def setup_data_readers( route: str, sidx: int, use_route_meta: bool, needs_driver_cam: bool = True, needs_road_cam: bool = True, dummy_driver_cam: bool = False -) -> Tuple[LogReader, Dict[str, Any]]: +) -> tuple[LogReader, dict[str, Any]]: if use_route_meta: r = Route(route) lr = LogReader(r.log_paths()[sidx]) @@ -92,7 +93,7 @@ def setup_data_readers( def regen_and_save( - route: str, sidx: int, processes: Union[str, Iterable[str]] = "all", outdir: str = FAKEDATA, + route: str, sidx: int, processes: str | Iterable[str] = "all", outdir: str = FAKEDATA, upload: bool = False, use_route_meta: bool = False, disable_tqdm: bool = False, dummy_driver_cam: bool = False ) -> str: if not isinstance(processes, str) and not hasattr(processes, "__iter__"): diff --git a/selfdrive/test/process_replay/test_debayer.py b/selfdrive/test/process_replay/test_debayer.py index bea1b1fb00..edf2cbd469 100755 --- a/selfdrive/test/process_replay/test_debayer.py +++ b/selfdrive/test/process_replay/test_debayer.py @@ -30,7 +30,7 @@ def get_frame_fn(ref_commit, test_route, tici=True): def bzip_frames(frames): - data = bytes() + data = b'' for y, u, v in frames: data += y.tobytes() data += u.tobytes() diff --git a/selfdrive/test/process_replay/test_processes.py b/selfdrive/test/process_replay/test_processes.py index 5323a5280f..2b917b0f61 100755 --- a/selfdrive/test/process_replay/test_processes.py +++ b/selfdrive/test/process_replay/test_processes.py @@ -5,7 +5,7 @@ import os import sys from collections import defaultdict from tqdm import tqdm -from typing import Any, DefaultDict, Dict +from typing import Any from openpilot.selfdrive.car.car_helpers import interface_names from openpilot.tools.lib.openpilotci import get_url, upload_file @@ -172,11 +172,11 @@ if __name__ == "__main__": untested = (set(interface_names) - set(excluded_interfaces)) - {c.lower() for c in tested_cars} assert len(untested) == 0, f"Cars missing routes: {str(untested)}" - log_paths: DefaultDict[str, Dict[str, Dict[str, str]]] = defaultdict(lambda: defaultdict(dict)) + log_paths: defaultdict[str, dict[str, dict[str, str]]] = defaultdict(lambda: defaultdict(dict)) with concurrent.futures.ProcessPoolExecutor(max_workers=args.jobs) as pool: if not args.upload_only: download_segments = [seg for car, seg in segments if car in tested_cars] - log_data: Dict[str, LogReader] = {} + log_data: dict[str, LogReader] = {} p1 = pool.map(get_log_data, download_segments) for segment, lr in tqdm(p1, desc="Getting Logs", total=len(download_segments)): log_data[segment] = lr diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py index de8a4420b3..8f8c93ecff 100755 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -424,4 +424,4 @@ class TestOnroad(unittest.TestCase): if __name__ == "__main__": - pytest.main() + unittest.main() diff --git a/selfdrive/test/update_ci_routes.py b/selfdrive/test/update_ci_routes.py index 5ab5042b2b..a9f4494ffd 100755 --- a/selfdrive/test/update_ci_routes.py +++ b/selfdrive/test/update_ci_routes.py @@ -3,7 +3,7 @@ import os import re import subprocess import sys -from typing import Iterable, List, Optional +from collections.abc import Iterable from tqdm import tqdm @@ -12,14 +12,14 @@ from openpilot.selfdrive.test.process_replay.test_processes import source_segmen from openpilot.tools.lib.azure_container import AzureContainer from openpilot.tools.lib.openpilotcontainers import DataCIContainer, DataProdContainer, OpenpilotCIContainer -SOURCES: List[AzureContainer] = [ +SOURCES: list[AzureContainer] = [ DataProdContainer, DataCIContainer ] DEST = OpenpilotCIContainer -def upload_route(path: str, exclude_patterns: Optional[Iterable[str]] = None) -> None: +def upload_route(path: str, exclude_patterns: Iterable[str] = None) -> None: if exclude_patterns is None: exclude_patterns = [r'dcamera\.hevc'] diff --git a/selfdrive/thermald/power_monitoring.py b/selfdrive/thermald/power_monitoring.py index 962ab7b069..076c8917a7 100644 --- a/selfdrive/thermald/power_monitoring.py +++ b/selfdrive/thermald/power_monitoring.py @@ -1,6 +1,5 @@ import time import threading -from typing import Optional from openpilot.common.numpy_fast import interp from openpilot.common.params import Params @@ -38,7 +37,7 @@ class PowerMonitoring: self.car_battery_capacity_uWh = max((CAR_BATTERY_CAPACITY_uWh / 10), int(car_battery_capacity_uWh)) # Calculation tick - def calculate(self, voltage: Optional[int], ignition: bool): + def calculate(self, voltage: int | None, ignition: bool): try: now = time.monotonic() @@ -108,7 +107,7 @@ class PowerMonitoring: return int(self.car_battery_capacity_uWh) # See if we need to shutdown - def should_shutdown(self, ignition: bool, in_car: bool, offroad_timestamp: Optional[float], started_seen: bool): + def should_shutdown(self, ignition: bool, in_car: bool, offroad_timestamp: float | None, started_seen: bool): if offroad_timestamp is None: return False diff --git a/selfdrive/thermald/thermald.py b/selfdrive/thermald/thermald.py index 5dcc5b8fcd..37206c2f06 100755 --- a/selfdrive/thermald/thermald.py +++ b/selfdrive/thermald/thermald.py @@ -6,7 +6,6 @@ import threading import time from collections import OrderedDict, namedtuple from pathlib import Path -from typing import Dict, Optional, Tuple import psutil @@ -50,9 +49,9 @@ THERMAL_BANDS = OrderedDict({ # Override to highest thermal band when offroad and above this temp OFFROAD_DANGER_TEMP = 75 -prev_offroad_states: Dict[str, Tuple[bool, Optional[str]]] = {} +prev_offroad_states: dict[str, tuple[bool, str | None]] = {} -tz_by_type: Optional[Dict[str, int]] = None +tz_by_type: dict[str, int] | None = None def populate_tz_by_type(): global tz_by_type tz_by_type = {} @@ -87,7 +86,7 @@ def read_thermal(thermal_config): return dat -def set_offroad_alert_if_changed(offroad_alert: str, show_alert: bool, extra_text: Optional[str]=None): +def set_offroad_alert_if_changed(offroad_alert: str, show_alert: bool, extra_text: str | None=None): if prev_offroad_states.get(offroad_alert, None) == (show_alert, extra_text): return prev_offroad_states[offroad_alert] = (show_alert, extra_text) @@ -171,16 +170,16 @@ def thermald_thread(end_event, hw_queue) -> None: count = 0 - onroad_conditions: Dict[str, bool] = { + onroad_conditions: dict[str, bool] = { "ignition": False, } - startup_conditions: Dict[str, bool] = {} - startup_conditions_prev: Dict[str, bool] = {} + startup_conditions: dict[str, bool] = {} + startup_conditions_prev: dict[str, bool] = {} - off_ts: Optional[float] = None - started_ts: Optional[float] = None + off_ts: float | None = None + started_ts: float | None = None started_seen = False - startup_blocked_ts: Optional[float] = None + startup_blocked_ts: float | None = None thermal_status = ThermalStatus.yellow last_hw_state = HardwareState( diff --git a/selfdrive/ui/soundd.py b/selfdrive/ui/soundd.py index 24ce83e522..8455c1bdac 100644 --- a/selfdrive/ui/soundd.py +++ b/selfdrive/ui/soundd.py @@ -3,7 +3,6 @@ import numpy as np import time import wave -from typing import Dict, Optional, Tuple from cereal import car, messaging from openpilot.common.basedir import BASEDIR @@ -28,7 +27,7 @@ DB_SCALE = 30 # AMBIENT_DB + DB_SCALE is where MAX_VOLUME is applied AudibleAlert = car.CarControl.HUDControl.AudibleAlert -sound_list: Dict[int, Tuple[str, Optional[int], float]] = { +sound_list: dict[int, tuple[str, int | None, float]] = { # AudibleAlert, file name, play count (none for infinite) AudibleAlert.engage: ("engage.wav", 1, MAX_VOLUME), AudibleAlert.disengage: ("disengage.wav", 1, MAX_VOLUME), @@ -84,7 +83,7 @@ class Soundd: self.current_alert == AudibleAlert.promptSingleHigh) or (not self.quiet_drive and self.current_alert != AudibleAlert.none) def load_sounds(self): - self.loaded_sounds: Dict[int, np.ndarray] = {} + self.loaded_sounds: dict[int, np.ndarray] = {} # Load all sounds for sound in sound_list: diff --git a/selfdrive/ui/tests/test_translations.py b/selfdrive/ui/tests/test_translations.py index 9ba9054ea1..8e50695e70 100755 --- a/selfdrive/ui/tests/test_translations.py +++ b/selfdrive/ui/tests/test_translations.py @@ -12,7 +12,7 @@ from parameterized import parameterized_class from openpilot.selfdrive.ui.update_translations import TRANSLATIONS_DIR, LANGUAGES_FILE, update_translations -with open(LANGUAGES_FILE, "r") as f: +with open(LANGUAGES_FILE) as f: translation_files = json.load(f) UNFINISHED_TRANSLATION_TAG = " dict[str, pathlib.Path]: +def get_language_files(languages: list[str] = None) -> dict[str, pathlib.Path]: files = {} with open(TRANSLATIONS_LANGUAGES) as fp: diff --git a/selfdrive/ui/translations/create_badges.py b/selfdrive/ui/translations/create_badges.py index f6c8aae448..f56f894111 100755 --- a/selfdrive/ui/translations/create_badges.py +++ b/selfdrive/ui/translations/create_badges.py @@ -13,13 +13,13 @@ BADGE_HEIGHT = 20 + 8 SHIELDS_URL = "https://img.shields.io/badge" if __name__ == "__main__": - with open(LANGUAGES_FILE, "r") as f: + with open(LANGUAGES_FILE) as f: translation_files = json.load(f) badge_svg = [] max_badge_width = 0 # keep track of max width to set parent element for idx, (name, file) in enumerate(translation_files.items()): - with open(os.path.join(TRANSLATIONS_DIR, f"{file}.ts"), "r") as tr_f: + with open(os.path.join(TRANSLATIONS_DIR, f"{file}.ts")) as tr_f: tr_file = tr_f.read() total_translations = 0 diff --git a/selfdrive/ui/update_translations.py b/selfdrive/ui/update_translations.py index e988a13b7f..0fe0f05ac4 100755 --- a/selfdrive/ui/update_translations.py +++ b/selfdrive/ui/update_translations.py @@ -28,7 +28,7 @@ def update_translations(vanish: bool = False, translation_files: None | list[str generate_translations_include() if translation_files is None: - with open(LANGUAGES_FILE, "r") as f: + with open(LANGUAGES_FILE) as f: translation_files = json.load(f).values() for file in translation_files: diff --git a/selfdrive/updated.py b/selfdrive/updated.py index b410555f4a..2947aa96bf 100755 --- a/selfdrive/updated.py +++ b/selfdrive/updated.py @@ -11,7 +11,6 @@ import time import threading from collections import defaultdict from pathlib import Path -from typing import List, Union, Optional from markdown_it import MarkdownIt from openpilot.common.basedir import BASEDIR @@ -64,7 +63,7 @@ def write_time_to_param(params, param) -> None: t = datetime.datetime.utcnow() params.put(param, t.isoformat().encode('utf8')) -def read_time_from_param(params, param) -> Optional[datetime.datetime]: +def read_time_from_param(params, param) -> datetime.datetime | None: t = params.get(param, encoding='utf8') try: return datetime.datetime.fromisoformat(t) @@ -72,7 +71,7 @@ def read_time_from_param(params, param) -> Optional[datetime.datetime]: pass return None -def run(cmd: List[str], cwd: Optional[str] = None) -> str: +def run(cmd: list[str], cwd: str = None) -> str: return subprocess.check_output(cmd, cwd=cwd, stderr=subprocess.STDOUT, encoding='utf8') @@ -234,7 +233,7 @@ def handle_agnos_update() -> None: class Updater: def __init__(self): self.params = Params() - self.branches = defaultdict(lambda: '') + self.branches = defaultdict(str) self._has_internet: bool = False @property @@ -243,7 +242,7 @@ class Updater: @property def target_branch(self) -> str: - b: Union[str, None] = self.params.get("UpdaterTargetBranch", encoding='utf-8') + b: str | None = self.params.get("UpdaterTargetBranch", encoding='utf-8') if b is None: b = self.get_branch(BASEDIR) return b @@ -272,7 +271,7 @@ class Updater: def get_commit_hash(self, path: str = OVERLAY_MERGED) -> str: return run(["git", "rev-parse", "HEAD"], path).rstrip() - def set_params(self, update_success: bool, failed_count: int, exception: Optional[str]) -> None: + def set_params(self, update_success: bool, failed_count: int, exception: str | None) -> None: self.params.put("UpdateFailedCount", str(failed_count)) self.params.put("UpdaterTargetBranch", self.target_branch) diff --git a/system/hardware/base.py b/system/hardware/base.py index 7434bb61e8..6cdb4a4d64 100644 --- a/system/hardware/base.py +++ b/system/hardware/base.py @@ -1,6 +1,5 @@ from abc import abstractmethod, ABC from collections import namedtuple -from typing import Dict from cereal import log @@ -10,7 +9,7 @@ NetworkType = log.DeviceState.NetworkType class HardwareBase(ABC): @staticmethod - def get_cmdline() -> Dict[str, str]: + def get_cmdline() -> dict[str, str]: with open('/proc/cmdline') as f: cmdline = f.read() return {kv[0]: kv[1] for kv in [s.split('=') for s in cmdline.split(' ')] if len(kv) == 2} diff --git a/system/hardware/tici/agnos.json b/system/hardware/tici/agnos.json index b4408d2140..e69842cfec 100644 --- a/system/hardware/tici/agnos.json +++ b/system/hardware/tici/agnos.json @@ -61,17 +61,17 @@ }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-3bfb0f3a6bf677bdc8a6227f49ce3258025e1886dc81533e3fbacb356b5601db.img.xz", - "hash": "10ac02f18c5f1cde5a888a3411d3701b929c3488753467e77aad6085db058eb9", - "hash_raw": "3bfb0f3a6bf677bdc8a6227f49ce3258025e1886dc81533e3fbacb356b5601db", + "url": "https://commadist.azureedge.net/agnosupdate/system-0f69173d5f3058f7197c139442a6556be59e52f15402a263215a329ba5ec41e2.img.xz", + "hash": "4858385ba6284bcaa179ab77ac4263486e4d8670df921e4ac400464dc1dde59c", + "hash_raw": "0f69173d5f3058f7197c139442a6556be59e52f15402a263215a329ba5ec41e2", "size": 10737418240, "sparse": true, "full_check": false, "has_ab": true, "alt": { - "hash": "a7db41b93b587f8f9c3f83a3313f186445c4bdf07283cd6a5421dfbc0286c9db", - "url": "https://commadist.azureedge.net/agnosupdate/system-skip-chunks-3bfb0f3a6bf677bdc8a6227f49ce3258025e1886dc81533e3fbacb356b5601db.img.xz", - "size": 4548131508 + "hash": "42658a6fff660d9b6abb9cb9fbb3481071259c9a9598718af6b1edff2b556009", + "url": "https://commadist.azureedge.net/agnosupdate/system-skip-chunks-0f69173d5f3058f7197c139442a6556be59e52f15402a263215a329ba5ec41e2.img.xz", + "size": 4548292756 } } ] \ No newline at end of file diff --git a/system/hardware/tici/agnos.py b/system/hardware/tici/agnos.py index 342281b0f8..502295be07 100755 --- a/system/hardware/tici/agnos.py +++ b/system/hardware/tici/agnos.py @@ -6,7 +6,7 @@ import os import struct import subprocess import time -from typing import Dict, Generator, List, Tuple, Union +from collections.abc import Generator import requests @@ -117,7 +117,7 @@ def get_raw_hash(path: str, partition_size: int) -> str: return raw_hash.hexdigest().lower() -def verify_partition(target_slot_number: int, partition: Dict[str, Union[str, int]], force_full_check: bool = False) -> bool: +def verify_partition(target_slot_number: int, partition: dict[str, str | int], force_full_check: bool = False) -> bool: full_check = partition['full_check'] or force_full_check path = get_partition_path(target_slot_number, partition) @@ -184,7 +184,7 @@ def extract_casync_image(target_slot_number: int, partition: dict, cloudlog): target = casync.parse_caibx(partition['casync_caibx']) - sources: List[Tuple[str, casync.ChunkReader, casync.ChunkDict]] = [] + sources: list[tuple[str, casync.ChunkReader, casync.ChunkDict]] = [] # First source is the current partition. try: diff --git a/system/hardware/tici/amplifier.py b/system/hardware/tici/amplifier.py index e003f131cc..5725cb8170 100755 --- a/system/hardware/tici/amplifier.py +++ b/system/hardware/tici/amplifier.py @@ -2,7 +2,6 @@ import time from smbus2 import SMBus from collections import namedtuple -from typing import List # https://datasheets.maximintegrated.com/en/ds/MAX98089.pdf @@ -98,6 +97,7 @@ CONFIGS = { AmpConfig("Right DAC input mixer: DAI2 right", 0b1, 0x22, 0, 0b00000001), AmpConfig("Volume adjustment smoothing disabled", 0b1, 0x49, 6, 0b01000000), ], + "mici": [], } class Amplifier: @@ -110,7 +110,7 @@ class Amplifier: def _get_shutdown_config(self, amp_disabled: bool) -> AmpConfig: return AmpConfig("Global shutdown", 0b0 if amp_disabled else 0b1, 0x51, 7, 0b10000000) - def _set_configs(self, configs: List[AmpConfig]) -> None: + def _set_configs(self, configs: list[AmpConfig]) -> None: with SMBus(self.AMP_I2C_BUS) as bus: for config in configs: if self.debug: @@ -123,7 +123,7 @@ class Amplifier: if self.debug: print(f" Changed {hex(config.register)}: {hex(old_value)} -> {hex(new_value)}") - def set_configs(self, configs: List[AmpConfig]) -> bool: + def set_configs(self, configs: list[AmpConfig]) -> bool: # retry in case panda is using the amp tries = 15 for i in range(15): diff --git a/system/hardware/tici/casync.py b/system/hardware/tici/casync.py index 993336616d..986228c1cd 100755 --- a/system/hardware/tici/casync.py +++ b/system/hardware/tici/casync.py @@ -7,7 +7,7 @@ import sys import time from abc import ABC, abstractmethod from collections import defaultdict, namedtuple -from typing import Callable, Dict, List, Optional, Tuple +from collections.abc import Callable import requests from Crypto.Hash import SHA512 @@ -28,7 +28,7 @@ CHUNK_DOWNLOAD_RETRIES = 3 CAIBX_DOWNLOAD_TIMEOUT = 120 Chunk = namedtuple('Chunk', ['sha', 'offset', 'length']) -ChunkDict = Dict[bytes, Chunk] +ChunkDict = dict[bytes, Chunk] class ChunkReader(ABC): @@ -83,7 +83,7 @@ class RemoteChunkReader(ChunkReader): return decompressor.decompress(contents) -def parse_caibx(caibx_path: str) -> List[Chunk]: +def parse_caibx(caibx_path: str) -> list[Chunk]: """Parses the chunks from a caibx file. Can handle both local and remote files. Returns a list of chunks with hash, offset and length""" caibx: io.BufferedIOBase @@ -132,7 +132,7 @@ def parse_caibx(caibx_path: str) -> List[Chunk]: return chunks -def build_chunk_dict(chunks: List[Chunk]) -> ChunkDict: +def build_chunk_dict(chunks: list[Chunk]) -> ChunkDict: """Turn a list of chunks into a dict for faster lookups based on hash. Keep first chunk since it's more likely to be already downloaded.""" r = {} @@ -142,11 +142,11 @@ def build_chunk_dict(chunks: List[Chunk]) -> ChunkDict: return r -def extract(target: List[Chunk], - sources: List[Tuple[str, ChunkReader, ChunkDict]], +def extract(target: list[Chunk], + sources: list[tuple[str, ChunkReader, ChunkDict]], out_path: str, - progress: Optional[Callable[[int], None]] = None): - stats: Dict[str, int] = defaultdict(int) + progress: Callable[[int], None] = None): + stats: dict[str, int] = defaultdict(int) mode = 'rb+' if os.path.exists(out_path) else 'wb' with open(out_path, mode) as out: @@ -181,7 +181,7 @@ def extract(target: List[Chunk], return stats -def print_stats(stats: Dict[str, int]): +def print_stats(stats: dict[str, int]): total_bytes = sum(stats.values()) print(f"Total size: {total_bytes / 1024 / 1024:.2f} MB") for name, total in stats.items(): diff --git a/system/hardware/tici/hardware.h b/system/hardware/tici/hardware.h index e553a665a8..f1d1f1e717 100644 --- a/system/hardware/tici/hardware.h +++ b/system/hardware/tici/hardware.h @@ -20,12 +20,12 @@ public: } static std::string get_name() { - std::string devicetree_model = util::read_file("/sys/firmware/devicetree/base/model"); - return (devicetree_model.find("tizi") != std::string::npos) ? "tizi" : "tici"; + std::string model = util::read_file("/sys/firmware/devicetree/base/model"); + return model.substr(std::string("comma ").size()); } static cereal::InitData::DeviceType get_device_type() { - return (get_name() == "tizi") ? cereal::InitData::DeviceType::TIZI : cereal::InitData::DeviceType::TICI; + return (get_name() == "tizi") ? cereal::InitData::DeviceType::TIZI : (get_name() == "mici" ? cereal::InitData::DeviceType::MICI : cereal::InitData::DeviceType::TICI); } static int get_voltage() { return std::atoi(util::read_file("/sys/class/hwmon/hwmon1/in1_input").c_str()); } diff --git a/system/hardware/tici/power_monitor.py b/system/hardware/tici/power_monitor.py index ef3055ac47..296290dae8 100755 --- a/system/hardware/tici/power_monitor.py +++ b/system/hardware/tici/power_monitor.py @@ -3,7 +3,6 @@ import sys import time import datetime import numpy as np -from typing import List from collections import deque from openpilot.common.realtime import Ratekeeper @@ -14,7 +13,7 @@ def read_power(): with open("/sys/bus/i2c/devices/0-0040/hwmon/hwmon1/power1_input") as f: return int(f.read()) / 1e6 -def sample_power(seconds=5) -> List[float]: +def sample_power(seconds=5) -> list[float]: rate = 123 rk = Ratekeeper(rate, print_delay_threshold=None) diff --git a/system/hardware/tici/precise_power_measure.py b/system/hardware/tici/precise_power_measure.py index e186ba4aea..52fe0850ab 100755 --- a/system/hardware/tici/precise_power_measure.py +++ b/system/hardware/tici/precise_power_measure.py @@ -6,4 +6,4 @@ if __name__ == '__main__': print("measuring for 5 seconds") for _ in range(3): pwrs = sample_power() - print("mean %.2f std %.2f" % (np.mean(pwrs), np.std(pwrs))) + print(f"mean {np.mean(pwrs):.2f} std {np.std(pwrs):.2f}") diff --git a/system/hardware/tici/tests/compare_casync_manifest.py b/system/hardware/tici/tests/compare_casync_manifest.py index 835985b0b5..7de66d91d0 100755 --- a/system/hardware/tici/tests/compare_casync_manifest.py +++ b/system/hardware/tici/tests/compare_casync_manifest.py @@ -3,7 +3,6 @@ import argparse import collections import multiprocessing import os -from typing import Dict, List import requests from tqdm import tqdm @@ -42,7 +41,7 @@ if __name__ == "__main__": szs = list(tqdm(pool.imap(get_chunk_download_size, to), total=len(to))) chunk_sizes = {t.sha: sz for (t, sz) in zip(to, szs, strict=True)} - sources: Dict[str, List[int]] = { + sources: dict[str, list[int]] = { 'seed': [], 'remote_uncompressed': [], 'remote_compressed': [], diff --git a/system/hardware/tici/tests/test_hardware.py b/system/hardware/tici/tests/test_hardware.py index 0c436595ee..6c41c383a0 100755 --- a/system/hardware/tici/tests/test_hardware.py +++ b/system/hardware/tici/tests/test_hardware.py @@ -25,4 +25,4 @@ class TestHardware(unittest.TestCase): if __name__ == "__main__": - pytest.main() + unittest.main() diff --git a/system/hardware/tici/tests/test_power_draw.py b/system/hardware/tici/tests/test_power_draw.py index f6c0cf21a4..180ec155e4 100755 --- a/system/hardware/tici/tests/test_power_draw.py +++ b/system/hardware/tici/tests/test_power_draw.py @@ -1,13 +1,11 @@ #!/usr/bin/env python3 from collections import defaultdict, deque -import sys import pytest import unittest import time import numpy as np from dataclasses import dataclass from tabulate import tabulate -from typing import List import cereal.messaging as messaging from cereal.services import SERVICE_LIST @@ -22,9 +20,9 @@ MAX_WARMUP_TIME = 30 # seconds to wait for SAMPLE_TIME consecutive valid sample @dataclass class Proc: - procs: List[str] + procs: list[str] power: float - msgs: List[str] + msgs: list[str] rtol: float = 0.05 atol: float = 0.12 @@ -66,7 +64,7 @@ class TestPowerDraw(unittest.TestCase): return np.core.numeric.isclose(used, proc.power, rtol=proc.rtol, atol=proc.atol) def tabulate_msg_counts(self, msgs_and_power): - msg_counts = defaultdict(lambda: 0) + msg_counts = defaultdict(int) for _, counts in msgs_and_power: for msg, count in counts.items(): msg_counts[msg] += count @@ -133,4 +131,4 @@ class TestPowerDraw(unittest.TestCase): if __name__ == "__main__": - pytest.main(sys.argv) + unittest.main() diff --git a/system/loggerd/deleter.py b/system/loggerd/deleter.py index 868340150a..2f0b96c90e 100755 --- a/system/loggerd/deleter.py +++ b/system/loggerd/deleter.py @@ -2,7 +2,6 @@ import os import shutil import threading -from typing import List from openpilot.system.hardware.hw import Paths from openpilot.common.swaglog import cloudlog from openpilot.system.loggerd.config import get_available_bytes, get_available_percent @@ -23,7 +22,7 @@ def has_preserve_xattr(d: str) -> bool: return getxattr(os.path.join(Paths.log_root(), d), PRESERVE_ATTR_NAME) == PRESERVE_ATTR_VALUE -def get_preserved_segments(dirs_by_creation: List[str]) -> List[str]: +def get_preserved_segments(dirs_by_creation: list[str]) -> list[str]: preserved = [] for n, d in enumerate(filter(has_preserve_xattr, reversed(dirs_by_creation))): if n == PRESERVE_COUNT: diff --git a/system/loggerd/tests/loggerd_tests_common.py b/system/loggerd/tests/loggerd_tests_common.py index 3aa9e40531..42eec2a0f4 100644 --- a/system/loggerd/tests/loggerd_tests_common.py +++ b/system/loggerd/tests/loggerd_tests_common.py @@ -2,7 +2,6 @@ import os import random import unittest from pathlib import Path -from typing import Optional import openpilot.system.loggerd.deleter as deleter @@ -12,7 +11,7 @@ from openpilot.system.hardware.hw import Paths from openpilot.system.loggerd.xattr_cache import setxattr -def create_random_file(file_path: Path, size_mb: float, lock: bool = False, upload_xattr: Optional[bytes] = None) -> None: +def create_random_file(file_path: Path, size_mb: float, lock: bool = False, upload_xattr: bytes = None) -> None: file_path.parent.mkdir(parents=True, exist_ok=True) if lock: @@ -82,7 +81,7 @@ class UploaderTestCase(unittest.TestCase): self.params.put("DongleId", "0000000000000000") def make_file_with_data(self, f_dir: str, fn: str, size_mb: float = .1, lock: bool = False, - upload_xattr: Optional[bytes] = None, preserve_xattr: Optional[bytes] = None) -> Path: + upload_xattr: bytes = None, preserve_xattr: bytes = None) -> Path: file_path = Path(Paths.log_root()) / f_dir / fn create_random_file(file_path, size_mb, lock, upload_xattr) diff --git a/system/loggerd/tests/test_deleter.py b/system/loggerd/tests/test_deleter.py index e4112b7b4e..37d25507e0 100755 --- a/system/loggerd/tests/test_deleter.py +++ b/system/loggerd/tests/test_deleter.py @@ -4,7 +4,7 @@ import threading import unittest from collections import namedtuple from pathlib import Path -from typing import Sequence +from collections.abc import Sequence import openpilot.system.loggerd.deleter as deleter from openpilot.common.timeout import Timeout, TimeoutException diff --git a/system/loggerd/tests/test_loggerd.py b/system/loggerd/tests/test_loggerd.py index 963978926d..c80dc19fce 100755 --- a/system/loggerd/tests/test_loggerd.py +++ b/system/loggerd/tests/test_loggerd.py @@ -8,7 +8,6 @@ import subprocess import time from collections import defaultdict from pathlib import Path -from typing import Dict, List from flaky import flaky import cereal.messaging as messaging @@ -76,7 +75,7 @@ class TestLoggerd: end_type = SentinelType.endOfRoute if route else SentinelType.endOfSegment assert msgs[-1].sentinel.type == end_type - def _publish_random_messages(self, services: List[str]) -> Dict[str, list]: + def _publish_random_messages(self, services: list[str]) -> dict[str, list]: pm = messaging.PubMaster(services) managed_processes["loggerd"].start() diff --git a/system/loggerd/tests/test_uploader.py b/system/loggerd/tests/test_uploader.py index b674de5438..73917a30cf 100755 --- a/system/loggerd/tests/test_uploader.py +++ b/system/loggerd/tests/test_uploader.py @@ -6,7 +6,6 @@ import unittest import logging import json from pathlib import Path -from typing import List, Optional from openpilot.system.hardware.hw import Paths from openpilot.common.swaglog import cloudlog @@ -53,7 +52,7 @@ class TestUploader(UploaderTestCase): self.end_event.set() self.up_thread.join() - def gen_files(self, lock=False, xattr: Optional[bytes] = None, boot=True) -> List[Path]: + def gen_files(self, lock=False, xattr: bytes = None, boot=True) -> list[Path]: f_paths = [] for t in ["qlog", "rlog", "dcamera.hevc", "fcamera.hevc"]: f_paths.append(self.make_file_with_data(self.seg_dir, t, 1, lock=lock, upload_xattr=xattr)) @@ -62,7 +61,7 @@ class TestUploader(UploaderTestCase): f_paths.append(self.make_file_with_data("boot", f"{self.seg_dir}", 1, lock=lock, upload_xattr=xattr)) return f_paths - def gen_order(self, seg1: List[int], seg2: List[int], boot=True) -> List[str]: + def gen_order(self, seg1: list[int], seg2: list[int], boot=True) -> list[str]: keys = [] if boot: keys += [f"boot/{self.seg_format.format(i)}.bz2" for i in seg1] diff --git a/system/loggerd/uploader.py b/system/loggerd/uploader.py index b395f2afa4..6e414d321e 100755 --- a/system/loggerd/uploader.py +++ b/system/loggerd/uploader.py @@ -9,7 +9,8 @@ import threading import time import traceback import datetime -from typing import BinaryIO, Iterator, List, Optional, Tuple +from typing import BinaryIO +from collections.abc import Iterator from cereal import log import cereal.messaging as messaging @@ -44,10 +45,10 @@ class FakeResponse: self.request = FakeRequest() -def get_directory_sort(d: str) -> List[str]: +def get_directory_sort(d: str) -> list[str]: return [s.rjust(10, '0') for s in d.rsplit('--', 1)] -def listdir_by_creation(d: str) -> List[str]: +def listdir_by_creation(d: str) -> list[str]: if not os.path.isdir(d): return [] @@ -84,7 +85,7 @@ class Uploader: self.immediate_folders = ["crash/", "boot/"] self.immediate_priority = {"qlog": 0, "qlog.bz2": 0, "qcamera.ts": 1} - def list_upload_files(self, metered: bool) -> Iterator[Tuple[str, str, str]]: + def list_upload_files(self, metered: bool) -> Iterator[tuple[str, str, str]]: r = self.params.get("AthenadRecentlyViewedRoutes", encoding="utf8") requested_routes = [] if r is None else r.split(",") @@ -123,7 +124,7 @@ class Uploader: yield name, key, fn - def next_file_to_upload(self, metered: bool) -> Optional[Tuple[str, str, str]]: + def next_file_to_upload(self, metered: bool) -> tuple[str, str, str] | None: upload_files = list(self.list_upload_files(metered)) for name, key, fn in upload_files: @@ -209,7 +210,7 @@ class Uploader: return success - def step(self, network_type: int, metered: bool) -> Optional[bool]: + def step(self, network_type: int, metered: bool) -> bool | None: d = self.next_file_to_upload(metered) if d is None: return None @@ -223,7 +224,7 @@ class Uploader: return self.upload(name, key, fn, network_type, metered) -def main(exit_event: Optional[threading.Event] = None) -> None: +def main(exit_event: threading.Event = None) -> None: if exit_event is None: exit_event = threading.Event() diff --git a/system/loggerd/xattr_cache.py b/system/loggerd/xattr_cache.py index 5feeff34d2..d3220118ac 100644 --- a/system/loggerd/xattr_cache.py +++ b/system/loggerd/xattr_cache.py @@ -1,10 +1,9 @@ import os import errno -from typing import Dict, Optional, Tuple -_cached_attributes: Dict[Tuple, Optional[bytes]] = {} +_cached_attributes: dict[tuple, bytes | None] = {} -def getxattr(path: str, attr_name: str) -> Optional[bytes]: +def getxattr(path: str, attr_name: str) -> bytes | None: key = (path, attr_name) if key not in _cached_attributes: try: diff --git a/system/qcomgpsd/nmeaport.py b/system/qcomgpsd/nmeaport.py index 231096fc5d..caff7af646 100644 --- a/system/qcomgpsd/nmeaport.py +++ b/system/qcomgpsd/nmeaport.py @@ -93,7 +93,7 @@ def nmea_checksum_ok(s): def process_nmea_port_messages(device:str="/dev/ttyUSB1") -> NoReturn: while True: try: - with open(device, "r") as nmeaport: + with open(device) as nmeaport: for line in nmeaport: line = line.strip() if DEBUG: diff --git a/system/qcomgpsd/qcomgpsd.py b/system/qcomgpsd/qcomgpsd.py index e2a180df86..e8c407a627 100755 --- a/system/qcomgpsd/qcomgpsd.py +++ b/system/qcomgpsd/qcomgpsd.py @@ -10,7 +10,7 @@ import shutil import subprocess import datetime from multiprocessing import Process, Event -from typing import NoReturn, Optional +from typing import NoReturn from struct import unpack_from, calcsize, pack from cereal import log @@ -90,7 +90,7 @@ def try_setup_logs(diag, logs): return setup_logs(diag, logs) @retry(attempts=3, delay=1.0) -def at_cmd(cmd: str) -> Optional[str]: +def at_cmd(cmd: str) -> str | None: return subprocess.check_output(f"mmcli -m any --timeout 30 --command='{cmd}'", shell=True, encoding='utf8') def gps_enabled() -> bool: @@ -342,7 +342,7 @@ def main() -> NoReturn: gps.bearingDeg = report["q_FltHeadingRad"] * 180/math.pi # TODO needs update if there is another leap second, after june 2024? - dt_timestamp = (datetime.datetime(1980, 1, 6, 0, 0, 0, 0, datetime.timezone.utc) + + dt_timestamp = (datetime.datetime(1980, 1, 6, 0, 0, 0, 0, datetime.UTC) + datetime.timedelta(weeks=report['w_GpsWeekNumber']) + datetime.timedelta(seconds=(1e-3*report['q_GpsFixTimeMs'] - 18))) gps.unixTimestampMillis = dt_timestamp.timestamp()*1e3 diff --git a/system/sensord/pigeond.py b/system/sensord/pigeond.py index 78b3b07498..21b3a86f97 100755 --- a/system/sensord/pigeond.py +++ b/system/sensord/pigeond.py @@ -7,7 +7,6 @@ import struct import requests import urllib.parse from datetime import datetime -from typing import List, Optional, Tuple from cereal import messaging from openpilot.common.params import Params @@ -41,7 +40,7 @@ def add_ubx_checksum(msg: bytes) -> bytes: B = (B + A) % 256 return msg + bytes([A, B]) -def get_assistnow_messages(token: bytes) -> List[bytes]: +def get_assistnow_messages(token: bytes) -> list[bytes]: # make request # TODO: implement adding the last known location r = requests.get("https://online-live2.services.u-blox.com/GetOnlineData.ashx", params=urllib.parse.urlencode({ @@ -238,7 +237,7 @@ def initialize_pigeon(pigeon: TTYPigeon) -> bool: return False return True -def deinitialize_and_exit(pigeon: Optional[TTYPigeon]): +def deinitialize_and_exit(pigeon: TTYPigeon | None): cloudlog.warning("Storing almanac in ublox flash") if pigeon is not None: @@ -259,7 +258,7 @@ def deinitialize_and_exit(pigeon: Optional[TTYPigeon]): set_power(False) sys.exit(0) -def create_pigeon() -> Tuple[TTYPigeon, messaging.PubMaster]: +def create_pigeon() -> tuple[TTYPigeon, messaging.PubMaster]: pigeon = None # register exit handler diff --git a/system/ubloxd/tests/ubloxd.py b/system/ubloxd/tests/ubloxd.py index 4ee99dc28a..c17387114f 100755 --- a/system/ubloxd/tests/ubloxd.py +++ b/system/ubloxd/tests/ubloxd.py @@ -82,7 +82,7 @@ def configure_ublox(dev): if __name__ == "__main__": class Device: def write(self, s): - d = '"{}"s'.format(''.join('\\x{:02X}'.format(b) for b in s)) + d = '"{}"s'.format(''.join(f'\\x{b:02X}' for b in s)) print(f" if (!send_with_ack({d})) continue;") dev = ublox.UBlox(Device(), baudrate=baudrate) diff --git a/system/version.py b/system/version.py index 4436f24d63..6df4b0fb7e 100755 --- a/system/version.py +++ b/system/version.py @@ -1,7 +1,8 @@ #!/usr/bin/env python3 import os import subprocess -from typing import List, Callable, TypeVar +from typing import TypeVar +from collections.abc import Callable from functools import lru_cache from openpilot.common.basedir import BASEDIR @@ -19,11 +20,11 @@ def cache(user_function: Callable[..., _RT], /) -> Callable[..., _RT]: return lru_cache(maxsize=None)(user_function) -def run_cmd(cmd: List[str]) -> str: +def run_cmd(cmd: list[str]) -> str: return subprocess.check_output(cmd, encoding='utf8').strip() -def run_cmd_default(cmd: List[str], default: str = "") -> str: +def run_cmd_default(cmd: list[str], default: str = "") -> str: try: return run_cmd(cmd) except subprocess.CalledProcessError: diff --git a/system/webrtc/device/audio.py b/system/webrtc/device/audio.py index 3c78be6752..4b22033e03 100644 --- a/system/webrtc/device/audio.py +++ b/system/webrtc/device/audio.py @@ -1,6 +1,5 @@ import asyncio import io -from typing import Optional, List, Tuple import aiortc import av @@ -17,7 +16,7 @@ class AudioInputStreamTrack(aiortc.mediastreams.AudioStreamTrack): pyaudio.paFloat32: 'flt', } - def __init__(self, audio_format: int = pyaudio.paInt16, rate: int = 16000, channels: int = 1, packet_time: float = 0.020, device_index: Optional[int] = None): + def __init__(self, audio_format: int = pyaudio.paInt16, rate: int = 16000, channels: int = 1, packet_time: float = 0.020, device_index: int = None): super().__init__() self.p = pyaudio.PyAudio() @@ -49,7 +48,7 @@ class AudioInputStreamTrack(aiortc.mediastreams.AudioStreamTrack): class AudioOutputSpeaker: - def __init__(self, audio_format: int = pyaudio.paInt16, rate: int = 48000, channels: int = 2, packet_time: float = 0.2, device_index: Optional[int] = None): + def __init__(self, audio_format: int = pyaudio.paInt16, rate: int = 48000, channels: int = 2, packet_time: float = 0.2, device_index: int = None): chunk_size = int(packet_time * rate) self.p = pyaudio.PyAudio() @@ -62,7 +61,7 @@ class AudioOutputSpeaker: output=True, output_device_index=device_index, stream_callback=self.__pyaudio_callback) - self.tracks_and_tasks: List[Tuple[aiortc.MediaStreamTrack, Optional[asyncio.Task]]] = [] + self.tracks_and_tasks: list[tuple[aiortc.MediaStreamTrack, asyncio.Task | None]] = [] def __pyaudio_callback(self, in_data, frame_count, time_info, status): if self.buffer.getbuffer().nbytes < frame_count * self.channels * 2: diff --git a/system/webrtc/device/video.py b/system/webrtc/device/video.py index 314f812834..1bca909294 100644 --- a/system/webrtc/device/video.py +++ b/system/webrtc/device/video.py @@ -1,5 +1,4 @@ import asyncio -from typing import Optional import av from teleoprtc.tracks import TiciVideoStreamTrack @@ -40,5 +39,5 @@ class LiveStreamVideoStreamTrack(TiciVideoStreamTrack): return packet - def codec_preference(self) -> Optional[str]: + def codec_preference(self) -> str | None: return "H264" diff --git a/system/webrtc/schema.py b/system/webrtc/schema.py index f659b34293..d80986ebf2 100644 --- a/system/webrtc/schema.py +++ b/system/webrtc/schema.py @@ -1,8 +1,8 @@ import capnp -from typing import Union, List, Dict, Any +from typing import Any -def generate_type(type_walker, schema_walker) -> Union[str, List[Any], Dict[str, Any]]: +def generate_type(type_walker, schema_walker) -> str | list[Any] | dict[str, Any]: data_type = next(type_walker) if data_type.which() == 'struct': return generate_struct(next(schema_walker)) @@ -15,11 +15,11 @@ def generate_type(type_walker, schema_walker) -> Union[str, List[Any], Dict[str, return str(data_type.which()) -def generate_struct(schema: capnp.lib.capnp._StructSchema) -> Dict[str, Any]: +def generate_struct(schema: capnp.lib.capnp._StructSchema) -> dict[str, Any]: return {field: generate_field(schema.fields[field]) for field in schema.fields if not field.endswith("DEPRECATED")} -def generate_field(field: capnp.lib.capnp._StructSchemaField) -> Union[str, List[Any], Dict[str, Any]]: +def generate_field(field: capnp.lib.capnp._StructSchemaField) -> str | list[Any] | dict[str, Any]: def schema_walker(field): yield field.schema diff --git a/system/webrtc/tests/test_webrtcd.py b/system/webrtc/tests/test_webrtcd.py index e92958cc92..c31b63d02b 100755 --- a/system/webrtc/tests/test_webrtcd.py +++ b/system/webrtc/tests/test_webrtcd.py @@ -18,7 +18,7 @@ class TestWebrtcdProc(unittest.IsolatedAsyncioTestCase): try: async with asyncio.timeout(timeout): await awaitable - except asyncio.TimeoutError: + except TimeoutError: self.fail("Timeout while waiting for awaitable to complete") async def test_webrtcd(self): diff --git a/system/webrtc/webrtcd.py b/system/webrtc/webrtcd.py index cc26d50daf..6c1370eae9 100755 --- a/system/webrtc/webrtcd.py +++ b/system/webrtc/webrtcd.py @@ -6,7 +6,7 @@ import json import uuid import logging from dataclasses import dataclass, field -from typing import Any, List, Optional, Union, TYPE_CHECKING +from typing import Any, TYPE_CHECKING # aiortc and its dependencies have lots of internal warnings :( import warnings @@ -24,7 +24,7 @@ from cereal import messaging, log class CerealOutgoingMessageProxy: def __init__(self, sm: messaging.SubMaster): self.sm = sm - self.channels: List['RTCDataChannel'] = [] + self.channels: list['RTCDataChannel'] = [] def add_channel(self, channel: 'RTCDataChannel'): self.channels.append(channel) @@ -103,7 +103,7 @@ class CerealProxyRunner: class StreamSession: - def __init__(self, sdp: str, cameras: List[str], incoming_services: List[str], outgoing_services: List[str], debug_mode: bool = False): + def __init__(self, sdp: str, cameras: list[str], incoming_services: list[str], outgoing_services: list[str], debug_mode: bool = False): from aiortc.mediastreams import VideoStreamTrack, AudioStreamTrack from aiortc.contrib.media import MediaBlackhole from openpilot.system.webrtc.device.video import LiveStreamVideoStreamTrack @@ -132,8 +132,8 @@ class StreamSession: self.incoming_bridge = CerealIncomingMessageProxy(messaging.PubMaster(incoming_services)) self.outgoing_bridge_runner = CerealProxyRunner(self.outgoing_bridge) - self.audio_output: Optional[Union[AudioOutputSpeaker, MediaBlackhole]] = None - self.run_task: Optional[asyncio.Task] = None + self.audio_output: AudioOutputSpeaker | MediaBlackhole | None = None + self.run_task: asyncio.Task | None = None self.logger = logging.getLogger("webrtcd") self.logger.info("New stream session (%s), cameras %s, audio in %s out %s, incoming services %s, outgoing services %s", self.identifier, cameras, config.incoming_audio_track, config.expected_audio_track, incoming_services, outgoing_services) @@ -189,9 +189,9 @@ class StreamSession: @dataclass class StreamRequestBody: sdp: str - cameras: List[str] - bridge_services_in: List[str] = field(default_factory=list) - bridge_services_out: List[str] = field(default_factory=list) + cameras: list[str] + bridge_services_in: list[str] = field(default_factory=list) + bridge_services_out: list[str] = field(default_factory=list) async def get_stream(request: 'web.Request'): diff --git a/tools/bodyteleop/web.py b/tools/bodyteleop/web.py index b1fb9525db..fd8f691d19 100644 --- a/tools/bodyteleop/web.py +++ b/tools/bodyteleop/web.py @@ -56,7 +56,7 @@ def create_ssl_cert(cert_path: str, key_path: str): try: proc = subprocess.run(f'openssl req -x509 -newkey rsa:4096 -nodes -out {cert_path} -keyout {key_path} \ -days 365 -subj "/C=US/ST=California/O=commaai/OU=comma body"', - stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + capture_output=True, shell=True) proc.check_returncode() except subprocess.CalledProcessError as ex: raise ValueError(f"Error creating SSL certificate:\n[stdout]\n{proc.stdout.decode()}\n[stderr]\n{proc.stderr.decode()}") from ex @@ -77,7 +77,7 @@ def create_ssl_context(): ## ENDPOINTS async def index(request: 'web.Request'): - with open(os.path.join(TELEOPDIR, "static", "index.html"), "r") as f: + with open(os.path.join(TELEOPDIR, "static", "index.html")) as f: content = f.read() return web.Response(content_type="text/html", text=content) diff --git a/tools/car_porting/auto_fingerprint.py b/tools/car_porting/auto_fingerprint.py index 7dae9ce048..f122c2774e 100755 --- a/tools/car_porting/auto_fingerprint.py +++ b/tools/car_porting/auto_fingerprint.py @@ -2,7 +2,6 @@ import argparse from collections import defaultdict -from typing import Optional from openpilot.selfdrive.debug.format_fingerprints import format_brand_fw_versions from openpilot.selfdrive.car.fw_versions import match_fw_to_car @@ -30,7 +29,7 @@ if __name__ == "__main__": carVin = None carPlatform = None - platform: Optional[str] = None + platform: str | None = None CP = lr.first("carParams") diff --git a/tools/car_porting/test_car_model.py b/tools/car_porting/test_car_model.py index 66fe2ea65f..86980b054b 100755 --- a/tools/car_porting/test_car_model.py +++ b/tools/car_porting/test_car_model.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 import argparse import sys -from typing import List import unittest from openpilot.selfdrive.car.tests.routes import CarTestRoute @@ -9,7 +8,7 @@ from openpilot.selfdrive.car.tests.test_models import TestCarModel from openpilot.tools.lib.route import SegmentName -def create_test_models_suite(routes: List[CarTestRoute], ci=False) -> unittest.TestSuite: +def create_test_models_suite(routes: list[CarTestRoute], ci=False) -> unittest.TestSuite: test_suite = unittest.TestSuite() for test_route in routes: # create new test case and discover tests diff --git a/tools/latencylogger/latency_logger.py b/tools/latencylogger/latency_logger.py index 19c0a86bf4..8c6af56b6e 100755 --- a/tools/latencylogger/latency_logger.py +++ b/tools/latencylogger/latency_logger.py @@ -90,7 +90,7 @@ def read_logs(lr): return (data, frame_mismatches) # This is not needed in 3.10 as a "key" parameter is added to bisect -class KeyifyList(object): +class KeyifyList: def __init__(self, inner, key): self.inner = inner self.key = key diff --git a/tools/lib/README.md b/tools/lib/README.md index a0c4a0863b..af1ad0de22 100644 --- a/tools/lib/README.md +++ b/tools/lib/README.md @@ -34,10 +34,16 @@ for msg in lr: ### Segment Ranges -We also support a new format called a "segment range", where you can specify which segments from a route to load. +We also support a new format called a "segment range": + +``` +344c5c15b34f2d8a / 2024-01-03--09-37-12 / 2:6 / q +[ dongle id ] [ timestamp ] [ selector ] [ query type] +``` + +you can specify which segments from a route to load ```python - lr = LogReader("a2a0ccea32023010|2023-07-27--13-01-19/4") # 4th segment lr = LogReader("a2a0ccea32023010|2023-07-27--13-01-19/4:6") # 4th and 5th segment lr = LogReader("a2a0ccea32023010|2023-07-27--13-01-19/-1") # last segment diff --git a/tools/lib/auth.py b/tools/lib/auth.py index 997d1f860d..5988397d0a 100755 --- a/tools/lib/auth.py +++ b/tools/lib/auth.py @@ -26,7 +26,7 @@ import sys import pprint import webbrowser from http.server import BaseHTTPRequestHandler, HTTPServer -from typing import Any, Dict +from typing import Any from urllib.parse import parse_qs, urlencode from openpilot.tools.lib.api import APIError, CommaApi, UnauthorizedError @@ -36,7 +36,7 @@ PORT = 3000 class ClientRedirectServer(HTTPServer): - query_params: Dict[str, Any] = {} + query_params: dict[str, Any] = {} class ClientRedirectHandler(BaseHTTPRequestHandler): diff --git a/tools/lib/azure_container.py b/tools/lib/azure_container.py index 7d9550266d..52b2f37dbf 100644 --- a/tools/lib/azure_container.py +++ b/tools/lib/azure_container.py @@ -2,7 +2,7 @@ import os from datetime import datetime, timedelta from functools import lru_cache from pathlib import Path -from typing import IO, Union +from typing import IO TOKEN_PATH = Path("/data/azure_token") @@ -57,7 +57,7 @@ class AzureContainer: ext = "hevc" if log_type.endswith('camera') else "bz2" return self.BASE_URL + f"{route_name.replace('|', '/')}/{segment_num}/{log_type}.{ext}" - def upload_bytes(self, data: Union[bytes, IO], blob_name: str) -> str: + def upload_bytes(self, data: bytes | IO, blob_name: str) -> str: from azure.storage.blob import BlobClient blob = BlobClient( account_url=self.ACCOUNT_URL, @@ -69,6 +69,6 @@ class AzureContainer: blob.upload_blob(data) return self.BASE_URL + blob_name - def upload_file(self, path: Union[str, os.PathLike], blob_name: str) -> str: + def upload_file(self, path: str | os.PathLike, blob_name: str) -> str: with open(path, "rb") as f: return self.upload_bytes(f, blob_name) diff --git a/tools/lib/bootlog.py b/tools/lib/bootlog.py index 827ef1eefc..208ddc19c2 100644 --- a/tools/lib/bootlog.py +++ b/tools/lib/bootlog.py @@ -1,6 +1,5 @@ import functools import re -from typing import List, Optional from openpilot.tools.lib.auth_config import get_token from openpilot.tools.lib.api import CommaApi @@ -44,7 +43,7 @@ class Bootlog: return False return self.id < b.id -def get_bootlog_from_id(bootlog_id: str) -> Optional[Bootlog]: +def get_bootlog_from_id(bootlog_id: str) -> Bootlog | None: # TODO: implement an API endpoint for this bl = Bootlog(bootlog_id) for b in get_bootlogs(bl.dongle_id): @@ -52,7 +51,7 @@ def get_bootlog_from_id(bootlog_id: str) -> Optional[Bootlog]: return b return None -def get_bootlogs(dongle_id: str) -> List[Bootlog]: +def get_bootlogs(dongle_id: str) -> list[Bootlog]: api = CommaApi(get_token()) r = api.get(f'v1/devices/{dongle_id}/bootlogs') return [Bootlog(b) for b in r] diff --git a/tools/lib/helpers.py b/tools/lib/helpers.py index f0af3d03c5..653eb3d7e0 100644 --- a/tools/lib/helpers.py +++ b/tools/lib/helpers.py @@ -9,18 +9,18 @@ class RE: DONGLE_ID = r'(?P[a-f0-9]{16})' TIMESTAMP = r'(?P[0-9]{4}-[0-9]{2}-[0-9]{2}--[0-9]{2}-[0-9]{2}-[0-9]{2})' LOG_ID_V2 = r'(?P[a-f0-9]{8})--(?P[a-z0-9]{10})' - LOG_ID = r'(?P(?:{}|{}))'.format(TIMESTAMP, LOG_ID_V2) - ROUTE_NAME = r'(?P{}[|_/]{})'.format(DONGLE_ID, LOG_ID) - SEGMENT_NAME = r'{}(?:--|/)(?P[0-9]+)'.format(ROUTE_NAME) + LOG_ID = fr'(?P(?:{TIMESTAMP}|{LOG_ID_V2}))' + ROUTE_NAME = fr'(?P{DONGLE_ID}[|_/]{LOG_ID})' + SEGMENT_NAME = fr'{ROUTE_NAME}(?:--|/)(?P[0-9]+)' INDEX = r'-?[0-9]+' - SLICE = r'(?P{})?:?(?P{})?:?(?P{})?'.format(INDEX, INDEX, INDEX) - SEGMENT_RANGE = r'{}(?:(--|/)(?P({})))?(?:/(?P([qras])))?'.format(ROUTE_NAME, SLICE) + SLICE = fr'(?P{INDEX})?:?(?P{INDEX})?:?(?P{INDEX})?' + SEGMENT_RANGE = fr'{ROUTE_NAME}(?:(--|/)(?P({SLICE})))?(?:/(?P([qras])))?' BOOTLOG_NAME = ROUTE_NAME - EXPLORER_FILE = r'^(?P{})--(?P[a-z]+\.[a-z0-9]+)$'.format(SEGMENT_NAME) - OP_SEGMENT_DIR = r'^(?P{})$'.format(SEGMENT_NAME) + EXPLORER_FILE = fr'^(?P{SEGMENT_NAME})--(?P[a-z]+\.[a-z0-9]+)$' + OP_SEGMENT_DIR = fr'^(?P{SEGMENT_NAME})$' def timestamp_to_datetime(t: str) -> datetime.datetime: diff --git a/tools/lib/live_logreader.py b/tools/lib/live_logreader.py index 0678fd1d00..6a7ecee6fd 100644 --- a/tools/lib/live_logreader.py +++ b/tools/lib/live_logreader.py @@ -1,5 +1,4 @@ import os -from typing import List from cereal import log as capnp_log, messaging from cereal.services import SERVICE_LIST @@ -8,7 +7,7 @@ from openpilot.tools.lib.logreader import LogIterable, RawLogIterable ALL_SERVICES = list(SERVICE_LIST.keys()) -def raw_live_logreader(services: List[str] = ALL_SERVICES, addr: str = '127.0.0.1') -> RawLogIterable: +def raw_live_logreader(services: list[str] = ALL_SERVICES, addr: str = '127.0.0.1') -> RawLogIterable: if addr != "127.0.0.1": os.environ["ZMQ"] = "1" messaging.context = messaging.Context() @@ -25,7 +24,7 @@ def raw_live_logreader(services: List[str] = ALL_SERVICES, addr: str = '127.0.0. yield msg -def live_logreader(services: List[str] = ALL_SERVICES, addr: str = '127.0.0.1') -> LogIterable: +def live_logreader(services: list[str] = ALL_SERVICES, addr: str = '127.0.0.1') -> LogIterable: for m in raw_live_logreader(services, addr): with capnp_log.Event.from_bytes(m) as evt: yield evt diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index 7957712aff..6247bbc9db 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -11,7 +11,7 @@ import tqdm import urllib.parse import warnings -from typing import Callable, Dict, Iterable, Iterator, List, Optional, Type +from collections.abc import Callable, Iterable, Iterator from urllib.parse import parse_qs, urlparse from cereal import log as capnp_log @@ -21,7 +21,7 @@ from openpilot.tools.lib.openpilotci import get_url from openpilot.tools.lib.filereader import FileReader, file_exists, internal_source_available from openpilot.tools.lib.route import Route, SegmentRange -LogMessage = Type[capnp._DynamicStructReader] +LogMessage = type[capnp._DynamicStructReader] LogIterable = Iterable[LogMessage] RawLogIterable = Iterable[bytes] @@ -76,8 +76,8 @@ class ReadMode(enum.StrEnum): AUTO_INTERACTIVE = "i" # default to rlogs, fallback to qlogs with a prompt from the user -LogPath = Optional[str] -LogPaths = List[LogPath] +LogPath = str | None +LogPaths = list[LogPath] ValidFileCallable = Callable[[LogPath], bool] Source = Callable[[SegmentRange, ReadMode], LogPaths] @@ -170,7 +170,7 @@ def auto_source(sr: SegmentRange, mode=ReadMode.RLOG) -> LogPaths: if mode == ReadMode.SANITIZED: return comma_car_segments_source(sr, mode) - SOURCES: List[Source] = [internal_source, openpilotci_source, comma_api_source, comma_car_segments_source,] + SOURCES: list[Source] = [internal_source, openpilotci_source, comma_api_source, comma_car_segments_source,] exceptions = [] # Automatically determine viable source for source in SOURCES: @@ -212,7 +212,7 @@ def parse_indirect(identifier: str): class LogReader: - def _parse_identifiers(self, identifier: str | List[str]): + def _parse_identifiers(self, identifier: str | list[str]): if isinstance(identifier, list): return [i for j in identifier for i in self._parse_identifiers(j)] @@ -234,7 +234,7 @@ class LogReader: are uploaded or auto fallback to qlogs with '/a' selector at the end of the route name." return identifiers - def __init__(self, identifier: str | List[str], default_mode: ReadMode = ReadMode.RLOG, + def __init__(self, identifier: str | list[str], default_mode: ReadMode = ReadMode.RLOG, default_source=auto_source, sort_by_time=False, only_union_types=False): self.default_mode = default_mode self.default_source = default_source @@ -243,7 +243,7 @@ are uploaded or auto fallback to qlogs with '/a' selector at the end of the rout self.sort_by_time = sort_by_time self.only_union_types = only_union_types - self.__lrs: Dict[int, _LogFileReader] = {} + self.__lrs: dict[int, _LogFileReader] = {} self.reset() def _get_lr(self, i): diff --git a/tools/lib/route.py b/tools/lib/route.py index 47ebdc7a51..bd0ccc1fc3 100644 --- a/tools/lib/route.py +++ b/tools/lib/route.py @@ -4,7 +4,7 @@ from functools import cache from urllib.parse import urlparse from collections import defaultdict from itertools import chain -from typing import Optional, cast +from typing import cast from openpilot.tools.lib.auth_config import get_token from openpilot.tools.lib.api import CommaApi @@ -231,7 +231,7 @@ class SegmentName: def route_name(self) -> RouteName: return self._route_name @property - def data_dir(self) -> Optional[str]: return self._data_dir + def data_dir(self) -> str | None: return self._data_dir def __str__(self) -> str: return self._canonical_name diff --git a/tools/lib/tests/test_caching.py b/tools/lib/tests/test_caching.py index bc92b01357..5d3dfeba42 100755 --- a/tools/lib/tests/test_caching.py +++ b/tools/lib/tests/test_caching.py @@ -7,7 +7,7 @@ import socket import unittest from parameterized import parameterized -from openpilot.selfdrive.athena.tests.helpers import with_http_server +from openpilot.selfdrive.test.helpers import with_http_server from openpilot.system.hardware.hw import Paths from openpilot.tools.lib.url_file import URLFile diff --git a/tools/lib/tests/test_comma_car_segments.py b/tools/lib/tests/test_comma_car_segments.py index b355b0fe60..50d4200b4f 100644 --- a/tools/lib/tests/test_comma_car_segments.py +++ b/tools/lib/tests/test_comma_car_segments.py @@ -1,5 +1,3 @@ - - import unittest import requests diff --git a/tools/lib/vidindex.py b/tools/lib/vidindex.py index 8156faba6b..f2e4e9ca45 100755 --- a/tools/lib/vidindex.py +++ b/tools/lib/vidindex.py @@ -3,7 +3,6 @@ import argparse import os import struct from enum import IntEnum -from typing import Tuple from openpilot.tools.lib.filereader import FileReader @@ -120,7 +119,7 @@ HEVC_CODED_SLICE_SEGMENT_NAL_UNITS = ( class VideoFileInvalid(Exception): pass -def get_ue(dat: bytes, start_idx: int, skip_bits: int) -> Tuple[int, int]: +def get_ue(dat: bytes, start_idx: int, skip_bits: int) -> tuple[int, int]: prefix_val = 0 prefix_len = 0 suffix_val = 0 @@ -184,7 +183,7 @@ def get_hevc_nal_unit_type(dat: bytes, nal_unit_start: int) -> HevcNalUnitType: print(" nal_unit_type:", nal_unit_type.name, f"({nal_unit_type.value})") return nal_unit_type -def get_hevc_slice_type(dat: bytes, nal_unit_start: int, nal_unit_type: HevcNalUnitType) -> Tuple[int, bool]: +def get_hevc_slice_type(dat: bytes, nal_unit_start: int, nal_unit_type: HevcNalUnitType) -> tuple[int, bool]: # 7.3.2.9 Slice segment layer RBSP syntax # slice_segment_layer_rbsp( ) { # slice_segment_header( ) @@ -259,7 +258,7 @@ def get_hevc_slice_type(dat: bytes, nal_unit_start: int, nal_unit_type: HevcNalU raise VideoFileInvalid("slice_type must be 0, 1, or 2") return slice_type, is_first_slice -def hevc_index(hevc_file_name: str, allow_corrupt: bool=False) -> Tuple[list, int, bytes]: +def hevc_index(hevc_file_name: str, allow_corrupt: bool=False) -> tuple[list, int, bytes]: with FileReader(hevc_file_name) as f: dat = f.read() diff --git a/tools/plotjuggler/juggle.py b/tools/plotjuggler/juggle.py index dc94062801..0caf5a18ff 100755 --- a/tools/plotjuggler/juggle.py +++ b/tools/plotjuggler/juggle.py @@ -26,7 +26,7 @@ MAX_STREAMING_BUFFER_SIZE = 1000 def install(): m = f"{platform.system()}-{platform.machine()}" - supported = ("Linux-x86_64", "Darwin-arm64", "Darwin-x86_64") + supported = ("Linux-x86_64", "Linux-aarch64", "Darwin-arm64", "Darwin-x86_64") if m not in supported: raise Exception(f"Unsupported platform: '{m}'. Supported platforms: {supported}") diff --git a/tools/replay/can_replay.py b/tools/replay/can_replay.py index c597df1f66..bf6b1e9ae2 100755 --- a/tools/replay/can_replay.py +++ b/tools/replay/can_replay.py @@ -21,41 +21,41 @@ ENABLE_IGN = IGN_ON > 0 and IGN_OFF > 0 ENABLE_PWR = PWR_ON > 0 and PWR_OFF > 0 -def send_thread(s, flock): +def send_thread(j: PandaJungle, flock): if "FLASH" in os.environ: with flock: - s.flash() + j.flash() for i in [0, 1, 2, 3, 0xFFFF]: - s.can_clear(i) - s.set_can_speed_kbps(i, 500) - s.set_can_data_speed_kbps(i, 500) - s.set_ignition(False) + j.can_clear(i) + j.set_can_speed_kbps(i, 500) + j.set_can_data_speed_kbps(i, 500) + j.set_ignition(False) time.sleep(5) - s.set_ignition(True) - s.set_panda_power(True) - s.set_can_loopback(False) + j.set_ignition(True) + j.set_panda_power(True) + j.set_can_loopback(False) rk = Ratekeeper(1 / DT_CTRL, print_delay_threshold=None) while True: # handle cycling if ENABLE_PWR: i = (rk.frame*DT_CTRL) % (PWR_ON + PWR_OFF) < PWR_ON - s.set_panda_power(i) + j.set_panda_power(i) if ENABLE_IGN: i = (rk.frame*DT_CTRL) % (IGN_ON + IGN_OFF) < IGN_ON - s.set_ignition(i) + j.set_ignition(i) snd = CAN_MSGS[rk.frame % len(CAN_MSGS)] snd = list(filter(lambda x: x[-1] <= 2, snd)) try: - s.can_send_many(snd) + j.can_send_many(snd) except usb1.USBErrorTimeout: # timeout is fine, just means the CAN TX buffer is full pass # Drain panda message buffer - s.can_recv() + j.can_recv() rk.keep_time() diff --git a/tools/replay/lib/ui_helpers.py b/tools/replay/lib/ui_helpers.py index e350b89bac..23f3563084 100644 --- a/tools/replay/lib/ui_helpers.py +++ b/tools/replay/lib/ui_helpers.py @@ -1,5 +1,5 @@ import itertools -from typing import Any, Dict, Tuple +from typing import Any import matplotlib.pyplot as plt import numpy as np @@ -84,7 +84,7 @@ class Calibration: return pts / self.zoom -_COLOR_CACHE : Dict[Tuple[int, int, int], Any] = {} +_COLOR_CACHE : dict[tuple[int, int, int], Any] = {} def find_color(lidar_surface, color): if color in _COLOR_CACHE: return _COLOR_CACHE[color] diff --git a/tools/sim/bridge/common.py b/tools/sim/bridge/common.py index 91ab0b6f07..10e2a055a3 100644 --- a/tools/sim/bridge/common.py +++ b/tools/sim/bridge/common.py @@ -4,7 +4,6 @@ import functools from multiprocessing import Process, Queue, Value from abc import ABC, abstractmethod -from typing import Optional from openpilot.common.params import Params from openpilot.common.numpy_fast import clip @@ -44,7 +43,7 @@ class SimulatorBridge(ABC): self._exit = threading.Event() self.simulator_state = SimulatorState() - self.world: Optional[World] = None + self.world: World | None = None self.past_startup_engaged = False diff --git a/tools/sim/bridge/metadrive/metadrive_bridge.py b/tools/sim/bridge/metadrive/metadrive_bridge.py index 1b1e5ffea6..c94fca2b95 100644 --- a/tools/sim/bridge/metadrive/metadrive_bridge.py +++ b/tools/sim/bridge/metadrive/metadrive_bridge.py @@ -30,14 +30,14 @@ class CopyRamRGBCamera(RGBCamera): class RGBCameraWide(CopyRamRGBCamera): def __init__(self, *args, **kwargs): - super(RGBCameraWide, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) lens = self.get_lens() lens.setFov(120) lens.setNear(0.1) class RGBCameraRoad(CopyRamRGBCamera): def __init__(self, *args, **kwargs): - super(RGBCameraRoad, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) lens = self.get_lens() lens.setFov(40) lens.setNear(0.1) @@ -85,7 +85,7 @@ class MetaDriveBridge(SimulatorBridge): def __init__(self, dual_camera, high_quality): self.should_render = False - super(MetaDriveBridge, self).__init__(dual_camera, high_quality) + super().__init__(dual_camera, high_quality) def spawn_world(self): sensors = { diff --git a/tools/sim/lib/manual_ctrl.py b/tools/sim/lib/manual_ctrl.py index 5e826e7baa..8a72296538 100755 --- a/tools/sim/lib/manual_ctrl.py +++ b/tools/sim/lib/manual_ctrl.py @@ -4,7 +4,7 @@ import array import os import struct from fcntl import ioctl -from typing import NoReturn, Dict, List +from typing import NoReturn # Iterate over the joystick devices. print('Available devices:') @@ -13,8 +13,8 @@ for fn in os.listdir('/dev/input'): print(f' /dev/input/{fn}') # We'll store the states here. -axis_states: Dict[str, float] = {} -button_states: Dict[str, float] = {} +axis_states: dict[str, float] = {} +button_states: dict[str, float] = {} # These constants were borrowed from linux/input.h axis_names = { @@ -88,8 +88,8 @@ button_names = { 0x2c3 : 'dpad_down', } -axis_name_list: List[str] = [] -button_name_list: List[str] = [] +axis_name_list: list[str] = [] +button_name_list: list[str] = [] def wheel_poll_thread(q: 'Queue[str]') -> NoReturn: # Open the joystick device.