diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 103bb38e2642a0..f98bca606070ec 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -44,7 +44,7 @@ repos: - --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.1 + rev: v0.2.2 hooks: - id: ruff exclude: '^(third_party/)|(cereal/)|(panda/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(teleoprtc/)|(teleoprtc_repo/)' @@ -74,6 +74,14 @@ repos: # https://google.github.io/styleguide/cppguide.html # relevant rules are whitelisted, see all options with: cpplint --filter= - --filter=-build,-legal,-readability,-runtime,-whitespace,+build/include_subdir,+build/forward_decl,+build/include_what_you_use,+build/deprecated,+whitespace/comma,+whitespace/line_length,+whitespace/empty_if_body,+whitespace/empty_loop_body,+whitespace/empty_conditional_body,+whitespace/forcolon,+whitespace/parens,+whitespace/semicolon,+whitespace/tab,+readability/braces +- repo: https://github.com/MarcoGorelli/cython-lint + rev: v0.16.0 + hooks: + - id: cython-lint + exclude: '^(third_party/)|(cereal/)|(body/)|(rednose/)|(rednose_repo/)|(opendbc/)|(panda/)|(generated/)' + args: + - --max-line-length=240 + - --ignore=E111, E302, E305 - repo: local hooks: - id: test_translations diff --git a/RELEASES.md b/RELEASES.md index 215b9759742def..096fa691ac615b 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,4 +1,8 @@ -Version 0.9.6 (2024-02-22) +Version 0.9.7 (2024-XX-XX) +======================== +* New driving model + +Version 0.9.6 (2024-02-27) ======================== * New driving model * Vision model trained on more data @@ -9,8 +13,9 @@ Version 0.9.6 (2024-02-22) * AGNOS 9 * comma body streaming and controls over WebRTC * Improved fuzzy fingerprinting for many makes and models +* Alpha longitudinal support for new Toyota models * Chevrolet Equinox 2019-22 support thanks to JasonJShuler and nworb-cire! -* Dodge Duranago 2020-21 support +* Dodge Durango 2020-21 support * Hyundai Staria 2023 support thanks to sunnyhaibin! * Kia Niro Plug-in Hybrid 2022 support thanks to sunnyhaibin! * Lexus LC 2024 support thanks to nelsonjchen! diff --git a/cereal b/cereal index e86a48cf413c48..b891fcca7abc7a 160000 --- a/cereal +++ b/cereal @@ -1 +1 @@ -Subproject commit e86a48cf413c48e8d5a0d247f3b64fdcb800096f +Subproject commit b891fcca7abc7a25e7322afa8b4ab381d695564c diff --git a/common/params.cc b/common/params.cc index 0733aeaee9394a..0f08501da4f66c 100644 --- a/common/params.cc +++ b/common/params.cc @@ -125,6 +125,7 @@ std::unordered_map keys = { {"ForcePowerDown", PERSISTENT}, {"GitBranch", PERSISTENT}, {"GitCommit", PERSISTENT}, + {"GitCommitDate", PERSISTENT}, {"GitDiff", PERSISTENT}, {"GithubSshKeys", PERSISTENT}, {"GithubUsername", PERSISTENT}, diff --git a/common/params_pyx.pyx b/common/params_pyx.pyx index 47d2075df25eb3..535514e521a6d9 100644 --- a/common/params_pyx.pyx +++ b/common/params_pyx.pyx @@ -29,7 +29,7 @@ cdef extern from "common/params.h": def ensure_bytes(v): - return v.encode() if isinstance(v, str) else v; + return v.encode() if isinstance(v, str) else v class UnknownKeyName(Exception): pass diff --git a/common/transformations/transformations.pxd b/common/transformations/transformations.pxd index 7af009870198ad..964adf06ece9e8 100644 --- a/common/transformations/transformations.pxd +++ b/common/transformations/transformations.pxd @@ -1,4 +1,4 @@ -#cython: language_level=3 +# cython: language_level=3 from libcpp cimport bool cdef extern from "orientation.cc": diff --git a/common/transformations/transformations.pyx b/common/transformations/transformations.pyx index c5cb9e00568e96..ae045c369d7506 100644 --- a/common/transformations/transformations.pyx +++ b/common/transformations/transformations.pyx @@ -17,7 +17,6 @@ from openpilot.common.transformations.transformations cimport ecef2geodetic as e from openpilot.common.transformations.transformations cimport LocalCoord_c -import cython import numpy as np cimport numpy as np @@ -34,14 +33,14 @@ cdef Matrix3 numpy2matrix(np.ndarray[double, ndim=2, mode="fortran"] m): return Matrix3(m.data) cdef ECEF list2ecef(ecef): - cdef ECEF e; + cdef ECEF e e.x = ecef[0] e.y = ecef[1] e.z = ecef[2] return e cdef NED list2ned(ned): - cdef NED n; + cdef NED n n.n = ned[0] n.e = ned[1] n.d = ned[2] @@ -61,7 +60,7 @@ def euler2quat_single(euler): def quat2euler_single(quat): cdef Quaternion q = Quaternion(quat[0], quat[1], quat[2], quat[3]) - cdef Vector3 e = quat2euler_c(q); + cdef Vector3 e = quat2euler_c(q) return [e(0), e(1), e(2)] def quat2rot_single(quat): diff --git a/common/version.h b/common/version.h index a0147beadf3052..dce92e08019cd3 100644 --- a/common/version.h +++ b/common/version.h @@ -1 +1 @@ -#define COMMA_VERSION "0.9.6.0" +#define COMMA_VERSION "0.9.6.1" diff --git a/conftest.py b/conftest.py index e4dc6640c0df48..0d2a4a8fc4d6ba 100644 --- a/conftest.py +++ b/conftest.py @@ -1,3 +1,4 @@ +import contextlib import gc import os import pytest @@ -25,42 +26,42 @@ def pytest_runtest_call(item): yield -@pytest.fixture(scope="function", autouse=True) -def openpilot_function_fixture(request): +@contextlib.contextmanager +def clean_env(): starting_env = dict(os.environ) + yield + os.environ.clear() + os.environ.update(starting_env) - random.seed(0) - # setup a clean environment for each test - with OpenpilotPrefix(shared_download_cache=request.node.get_closest_marker("shared_download_cache") is not None) as prefix: - prefix = os.environ["OPENPILOT_PREFIX"] +@pytest.fixture(scope="function", autouse=True) +def openpilot_function_fixture(request): + random.seed(0) - yield + with clean_env(): + # setup a clean environment for each test + with OpenpilotPrefix(shared_download_cache=request.node.get_closest_marker("shared_download_cache") is not None) as prefix: + prefix = os.environ["OPENPILOT_PREFIX"] - # ensure the test doesn't change the prefix - assert "OPENPILOT_PREFIX" in os.environ and prefix == os.environ["OPENPILOT_PREFIX"] + yield - os.environ.clear() - os.environ.update(starting_env) + # ensure the test doesn't change the prefix + assert "OPENPILOT_PREFIX" in os.environ and prefix == os.environ["OPENPILOT_PREFIX"] - # cleanup any started processes - manager.manager_cleanup() + # cleanup any started processes + manager.manager_cleanup() - # some processes disable gc for performance, re-enable here - if not gc.isenabled(): - gc.enable() - gc.collect() + # some processes disable gc for performance, re-enable here + if not gc.isenabled(): + gc.enable() + gc.collect() # If you use setUpClass, the environment variables won't be cleared properly, # so we need to hook both the function and class pytest fixtures @pytest.fixture(scope="class", autouse=True) def openpilot_class_fixture(): - starting_env = dict(os.environ) - - yield - - os.environ.clear() - os.environ.update(starting_env) + with clean_env(): + yield @pytest.fixture(scope="function") diff --git a/docs/CARS.md b/docs/CARS.md index 48e2e77e624a29..6d3ee716598f6a 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -216,9 +216,9 @@ A supported vehicle is one that just works when you install a comma device. All |Toyota|Avalon Hybrid 2019-21|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
|| |Toyota|Avalon Hybrid 2022|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
|| |Toyota|C-HR 2017-20|All|Stock|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
|| -|Toyota|C-HR 2021|All|Stock|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
|| +|Toyota|C-HR 2021|All|openpilot available[1](#footnotes)|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
|| |Toyota|C-HR Hybrid 2017-20|All|Stock|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
|| -|Toyota|C-HR Hybrid 2021-22|All|Stock|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
|| +|Toyota|C-HR Hybrid 2021-22|All|openpilot available[1](#footnotes)|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
|| |Toyota|Camry 2018-20|All|Stock|0 mph[9](#footnotes)|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
|| |Toyota|Camry 2021-24|All|openpilot|0 mph[9](#footnotes)|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
|| |Toyota|Camry Hybrid 2018-20|All|Stock|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
|| @@ -244,13 +244,13 @@ A supported vehicle is one that just works when you install a comma device. All |Toyota|RAV4 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 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
|| |Toyota|RAV4 2017-18|All|openpilot available[2](#footnotes)|19 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
|| |Toyota|RAV4 2019-21|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
|| -|Toyota|RAV4 2022|All|Stock|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
|| -|Toyota|RAV4 2023-24|All|Stock|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
|| +|Toyota|RAV4 2022|All|openpilot available[1](#footnotes)|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
|| +|Toyota|RAV4 2023-24|All|openpilot available[1](#footnotes)|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
|| |Toyota|RAV4 Hybrid 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|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
|| |Toyota|RAV4 Hybrid 2017-18|All|openpilot available[2](#footnotes)|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
|| |Toyota|RAV4 Hybrid 2019-21|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
|| -|Toyota|RAV4 Hybrid 2022|All|Stock|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
|| -|Toyota|RAV4 Hybrid 2023-24|All|Stock|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
|| +|Toyota|RAV4 Hybrid 2022|All|openpilot available[1](#footnotes)|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
|| +|Toyota|RAV4 Hybrid 2023-24|All|openpilot available[1](#footnotes)|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
|| |Toyota|Sienna 2018-20|All|openpilot available[2](#footnotes)|19 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
|| |Volkswagen|Arteon 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 J533 connector
- 1 USB-C coupler
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Volkswagen|Arteon eHybrid 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 J533 connector
- 1 USB-C coupler
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| diff --git a/launch_chffrplus.sh b/launch_chffrplus.sh index 9271c154368737..a7f61411c867de 100755 --- a/launch_chffrplus.sh +++ b/launch_chffrplus.sh @@ -15,6 +15,11 @@ function agnos_init { # set success flag for current boot slot sudo abctl --set_success + # TODO: do this without udev in AGNOS + # udev does this, but sometimes we startup faster + sudo chgrp gpu /dev/adsprpc-smd /dev/ion /dev/kgsl-3d0 + sudo chmod 660 /dev/adsprpc-smd /dev/ion /dev/kgsl-3d0 + # Check if AGNOS update is required if [ $(< /VERSION) != "$AGNOS_VERSION" ]; then AGNOS_PY="$DIR/system/hardware/tici/agnos.py" diff --git a/opendbc b/opendbc index a49eea191e4957..e8e8ed72221e16 160000 --- a/opendbc +++ b/opendbc @@ -1 +1 @@ -Subproject commit a49eea191e4957fc149178c44ee418d3387194c3 +Subproject commit e8e8ed72221e161cc2952ce09deb776673ffbff6 diff --git a/panda b/panda index 30af1bcdaacb0d..c3f5332ea0b800 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 30af1bcdaacb0d1f5173a12585f244965351eb10 +Subproject commit c3f5332ea0b800454a4d284ce735aa55ba1a1873 diff --git a/poetry.lock b/poetry.lock index 4333f01267bf91..9972cd77322f63 100644 --- a/poetry.lock +++ b/poetry.lock @@ -825,43 +825,43 @@ files = [ [[package]] name = "cryptography" -version = "42.0.2" +version = "42.0.3" 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.2-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:701171f825dcab90969596ce2af253143b93b08f1a716d4b2a9d2db5084ef7be"}, - {file = "cryptography-42.0.2-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:61321672b3ac7aade25c40449ccedbc6db72c7f5f0fdf34def5e2f8b51ca530d"}, - {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea2c3ffb662fec8bbbfce5602e2c159ff097a4631d96235fcf0fb00e59e3ece4"}, - {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b15c678f27d66d247132cbf13df2f75255627bcc9b6a570f7d2fd08e8c081d2"}, - {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8e88bb9eafbf6a4014d55fb222e7360eef53e613215085e65a13290577394529"}, - {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a047682d324ba56e61b7ea7c7299d51e61fd3bca7dad2ccc39b72bd0118d60a1"}, - {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:36d4b7c4be6411f58f60d9ce555a73df8406d484ba12a63549c88bd64f7967f1"}, - {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:a00aee5d1b6c20620161984f8ab2ab69134466c51f58c052c11b076715e72929"}, - {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b97fe7d7991c25e6a31e5d5e795986b18fbbb3107b873d5f3ae6dc9a103278e9"}, - {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5fa82a26f92871eca593b53359c12ad7949772462f887c35edaf36f87953c0e2"}, - {file = "cryptography-42.0.2-cp37-abi3-win32.whl", hash = "sha256:4b063d3413f853e056161eb0c7724822a9740ad3caa24b8424d776cebf98e7ee"}, - {file = "cryptography-42.0.2-cp37-abi3-win_amd64.whl", hash = "sha256:841ec8af7a8491ac76ec5a9522226e287187a3107e12b7d686ad354bb78facee"}, - {file = "cryptography-42.0.2-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:55d1580e2d7e17f45d19d3b12098e352f3a37fe86d380bf45846ef257054b242"}, - {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28cb2c41f131a5758d6ba6a0504150d644054fd9f3203a1e8e8d7ac3aea7f73a"}, - {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9097a208875fc7bbeb1286d0125d90bdfed961f61f214d3f5be62cd4ed8a446"}, - {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:44c95c0e96b3cb628e8452ec060413a49002a247b2b9938989e23a2c8291fc90"}, - {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2f9f14185962e6a04ab32d1abe34eae8a9001569ee4edb64d2304bf0d65c53f3"}, - {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:09a77e5b2e8ca732a19a90c5bca2d124621a1edb5438c5daa2d2738bfeb02589"}, - {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad28cff53f60d99a928dfcf1e861e0b2ceb2bc1f08a074fdd601b314e1cc9e0a"}, - {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:130c0f77022b2b9c99d8cebcdd834d81705f61c68e91ddd614ce74c657f8b3ea"}, - {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:fa3dec4ba8fb6e662770b74f62f1a0c7d4e37e25b58b2bf2c1be4c95372b4a33"}, - {file = "cryptography-42.0.2-cp39-abi3-win32.whl", hash = "sha256:3dbd37e14ce795b4af61b89b037d4bc157f2cb23e676fa16932185a04dfbf635"}, - {file = "cryptography-42.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:8a06641fb07d4e8f6c7dda4fc3f8871d327803ab6542e33831c7ccfdcb4d0ad6"}, - {file = "cryptography-42.0.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:087887e55e0b9c8724cf05361357875adb5c20dec27e5816b653492980d20380"}, - {file = "cryptography-42.0.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a7ef8dd0bf2e1d0a27042b231a3baac6883cdd5557036f5e8df7139255feaac6"}, - {file = "cryptography-42.0.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4383b47f45b14459cab66048d384614019965ba6c1a1a141f11b5a551cace1b2"}, - {file = "cryptography-42.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:fbeb725c9dc799a574518109336acccaf1303c30d45c075c665c0793c2f79a7f"}, - {file = "cryptography-42.0.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:320948ab49883557a256eab46149df79435a22d2fefd6a66fe6946f1b9d9d008"}, - {file = "cryptography-42.0.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5ef9bc3d046ce83c4bbf4c25e1e0547b9c441c01d30922d812e887dc5f125c12"}, - {file = "cryptography-42.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:52ed9ebf8ac602385126c9a2fe951db36f2cb0c2538d22971487f89d0de4065a"}, - {file = "cryptography-42.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:141e2aa5ba100d3788c0ad7919b288f89d1fe015878b9659b307c9ef867d3a65"}, - {file = "cryptography-42.0.2.tar.gz", hash = "sha256:e0ec52ba3c7f1b7d813cd52649a5b3ef1fc0d433219dc8c93827c57eab6cf888"}, + {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"}, ] [package.dependencies] @@ -989,22 +989,22 @@ files = [ [[package]] name = "dnspython" -version = "2.5.0" +version = "2.6.1" description = "DNS toolkit" optional = false python-versions = ">=3.8" files = [ - {file = "dnspython-2.5.0-py3-none-any.whl", hash = "sha256:6facdf76b73c742ccf2d07add296f178e629da60be23ce4b0a9c927b1e02c3a6"}, - {file = "dnspython-2.5.0.tar.gz", hash = "sha256:a0034815a59ba9ae888946be7ccca8f7c157b286f8455b379c692efb51022a15"}, + {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, + {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, ] [package.extras] -dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=5.0.3)", "mypy (>=1.0.1)", "pylint (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0.0)", "sphinx (>=7.0.0)", "twine (>=4.0.0)", "wheel (>=0.41.0)"] +dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "sphinx (>=7.2.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] dnssec = ["cryptography (>=41)"] -doh = ["h2 (>=4.1.0)", "httpcore (>=0.17.3)", "httpx (>=0.25.1)"] -doq = ["aioquic (>=0.9.20)"] -idna = ["idna (>=2.1)"] -trio = ["trio (>=0.14)"] +doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] +doq = ["aioquic (>=0.9.25)"] +idna = ["idna (>=3.6)"] +trio = ["trio (>=0.23)"] wmi = ["wmi (>=1.5.1)"] [[package]] @@ -1131,53 +1131,53 @@ files = [ [[package]] name = "fonttools" -version = "4.48.1" +version = "4.49.0" description = "Tools to manipulate font files" optional = false python-versions = ">=3.8" files = [ - {file = "fonttools-4.48.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:702ae93058c81f46461dc4b2c79f11d3c3d8fd7296eaf8f75b4ba5bbf813cd5f"}, - {file = "fonttools-4.48.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:97f0a49fa6aa2d6205c6f72f4f98b74ef4b9bfdcb06fd78e6fe6c7af4989b63e"}, - {file = "fonttools-4.48.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3260db55f1843e57115256e91247ad9f68cb02a434b51262fe0019e95a98738"}, - {file = "fonttools-4.48.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e740a7602c2bb71e1091269b5dbe89549749a8817dc294b34628ffd8b2bf7124"}, - {file = "fonttools-4.48.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4108b1d247953dd7c90ec8f457a2dec5fceb373485973cc852b14200118a51ee"}, - {file = "fonttools-4.48.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56339ec557f0c342bddd7c175f5e41c45fc21282bee58a86bd9aa322bec715f2"}, - {file = "fonttools-4.48.1-cp310-cp310-win32.whl", hash = "sha256:bff5b38d0e76eb18e0b8abbf35d384e60b3371be92f7be36128ee3e67483b3ec"}, - {file = "fonttools-4.48.1-cp310-cp310-win_amd64.whl", hash = "sha256:f7449493886da6a17472004d3818cc050ba3f4a0aa03fb47972e4fa5578e6703"}, - {file = "fonttools-4.48.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:18b35fd1a850ed7233a99bbd6774485271756f717dac8b594958224b54118b61"}, - {file = "fonttools-4.48.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cad5cfd044ea2e306fda44482b3dd32ee47830fa82dfa4679374b41baa294f5f"}, - {file = "fonttools-4.48.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f30e605c7565d0da6f0aec75a30ec372072d016957cd8fc4469721a36ea59b7"}, - {file = "fonttools-4.48.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aee76fd81a8571c68841d6ef0da750d5ff08ff2c5f025576473016f16ac3bcf7"}, - {file = "fonttools-4.48.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5057ade278e67923000041e2b195c9ea53e87f227690d499b6a4edd3702f7f01"}, - {file = "fonttools-4.48.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b10633aafc5932995a391ec07eba5e79f52af0003a1735b2306b3dab8a056d48"}, - {file = "fonttools-4.48.1-cp311-cp311-win32.whl", hash = "sha256:0d533f89819f9b3ee2dbedf0fed3825c425850e32bdda24c558563c71be0064e"}, - {file = "fonttools-4.48.1-cp311-cp311-win_amd64.whl", hash = "sha256:d20588466367f05025bb1efdf4e5d498ca6d14bde07b6928b79199c588800f0a"}, - {file = "fonttools-4.48.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0a2417547462e468edf35b32e3dd06a6215ac26aa6316b41e03b8eeaf9f079ea"}, - {file = "fonttools-4.48.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cf5a0cd974f85a80b74785db2d5c3c1fd6cc09a2ba3c837359b2b5da629ee1b0"}, - {file = "fonttools-4.48.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0452fcbfbce752ba596737a7c5ec5cf76bc5f83847ce1781f4f90eab14ece252"}, - {file = "fonttools-4.48.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578c00f93868f64a4102ecc5aa600a03b49162c654676c3fadc33de2ddb88a81"}, - {file = "fonttools-4.48.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:63dc592a16cd08388d8c4c7502b59ac74190b23e16dfc863c69fe1ea74605b68"}, - {file = "fonttools-4.48.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9b58638d8a85e3a1b32ec0a91d9f8171a877b4b81c408d4cb3257d0dee63e092"}, - {file = "fonttools-4.48.1-cp312-cp312-win32.whl", hash = "sha256:d10979ef14a8beaaa32f613bb698743f7241d92f437a3b5e32356dfb9769c65d"}, - {file = "fonttools-4.48.1-cp312-cp312-win_amd64.whl", hash = "sha256:cdfd7557d1bd294a200bd211aa665ca3b02998dcc18f8211a5532da5b8fad5c5"}, - {file = "fonttools-4.48.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3cdb9a92521b81bf717ebccf592bd0292e853244d84115bfb4db0c426de58348"}, - {file = "fonttools-4.48.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9b4ec6d42a7555f5ae35f3b805482f0aad0f1baeeef54859492ea3b782959d4a"}, - {file = "fonttools-4.48.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:902e9c4e9928301912f34a6638741b8ae0b64824112b42aaf240e06b735774b1"}, - {file = "fonttools-4.48.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8c8b54bd1420c184a995f980f1a8076f87363e2bb24239ef8c171a369d85a31"}, - {file = "fonttools-4.48.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:12ee86abca46193359ea69216b3a724e90c66ab05ab220d39e3fc068c1eb72ac"}, - {file = "fonttools-4.48.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6978bade7b6c0335095bdd0bd97f8f3d590d2877b370f17e03e0865241694eb5"}, - {file = "fonttools-4.48.1-cp38-cp38-win32.whl", hash = "sha256:bcd77f89fc1a6b18428e7a55dde8ef56dae95640293bfb8f4e929929eba5e2a2"}, - {file = "fonttools-4.48.1-cp38-cp38-win_amd64.whl", hash = "sha256:f40441437b039930428e04fb05ac3a132e77458fb57666c808d74a556779e784"}, - {file = "fonttools-4.48.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0d2b01428f7da26f229a5656defc824427b741e454b4e210ad2b25ed6ea2aed4"}, - {file = "fonttools-4.48.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:df48798f9a4fc4c315ab46e17873436c8746f5df6eddd02fad91299b2af7af95"}, - {file = "fonttools-4.48.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2eb4167bde04e172a93cf22c875d8b0cff76a2491f67f5eb069566215302d45d"}, - {file = "fonttools-4.48.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c900508c46274d32d308ae8e82335117f11aaee1f7d369ac16502c9a78930b0a"}, - {file = "fonttools-4.48.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:594206b31c95fcfa65f484385171fabb4ec69f7d2d7f56d27f17db26b7a31814"}, - {file = "fonttools-4.48.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:292922dc356d7f11f5063b4111a8b719efb8faea92a2a88ed296408d449d8c2e"}, - {file = "fonttools-4.48.1-cp39-cp39-win32.whl", hash = "sha256:4709c5bf123ba10eac210d2d5c9027d3f472591d9f1a04262122710fa3d23199"}, - {file = "fonttools-4.48.1-cp39-cp39-win_amd64.whl", hash = "sha256:63c73b9dd56a94a3cbd2f90544b5fca83666948a9e03370888994143b8d7c070"}, - {file = "fonttools-4.48.1-py3-none-any.whl", hash = "sha256:e3e33862fc5261d46d9aae3544acb36203b1a337d00bdb5d3753aae50dac860e"}, - {file = "fonttools-4.48.1.tar.gz", hash = "sha256:8b8a45254218679c7f1127812761e7854ed5c8e34349aebf581e8c9204e7495a"}, + {file = "fonttools-4.49.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d970ecca0aac90d399e458f0b7a8a597e08f95de021f17785fb68e2dc0b99717"}, + {file = "fonttools-4.49.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac9a745b7609f489faa65e1dc842168c18530874a5f5b742ac3dd79e26bca8bc"}, + {file = "fonttools-4.49.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ba0e00620ca28d4ca11fc700806fd69144b463aa3275e1b36e56c7c09915559"}, + {file = "fonttools-4.49.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdee3ab220283057e7840d5fb768ad4c2ebe65bdba6f75d5d7bf47f4e0ed7d29"}, + {file = "fonttools-4.49.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ce7033cb61f2bb65d8849658d3786188afd80f53dad8366a7232654804529532"}, + {file = "fonttools-4.49.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:07bc5ea02bb7bc3aa40a1eb0481ce20e8d9b9642a9536cde0218290dd6085828"}, + {file = "fonttools-4.49.0-cp310-cp310-win32.whl", hash = "sha256:86eef6aab7fd7c6c8545f3ebd00fd1d6729ca1f63b0cb4d621bccb7d1d1c852b"}, + {file = "fonttools-4.49.0-cp310-cp310-win_amd64.whl", hash = "sha256:1fac1b7eebfce75ea663e860e7c5b4a8831b858c17acd68263bc156125201abf"}, + {file = "fonttools-4.49.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:edc0cce355984bb3c1d1e89d6a661934d39586bb32191ebff98c600f8957c63e"}, + {file = "fonttools-4.49.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:83a0d9336de2cba86d886507dd6e0153df333ac787377325a39a2797ec529814"}, + {file = "fonttools-4.49.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36c8865bdb5cfeec88f5028e7e592370a0657b676c6f1d84a2108e0564f90e22"}, + {file = "fonttools-4.49.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33037d9e56e2562c710c8954d0f20d25b8386b397250d65581e544edc9d6b942"}, + {file = "fonttools-4.49.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8fb022d799b96df3eaa27263e9eea306bd3d437cc9aa981820850281a02b6c9a"}, + {file = "fonttools-4.49.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:33c584c0ef7dc54f5dd4f84082eabd8d09d1871a3d8ca2986b0c0c98165f8e86"}, + {file = "fonttools-4.49.0-cp311-cp311-win32.whl", hash = "sha256:cbe61b158deb09cffdd8540dc4a948d6e8f4d5b4f3bf5cd7db09bd6a61fee64e"}, + {file = "fonttools-4.49.0-cp311-cp311-win_amd64.whl", hash = "sha256:fc11e5114f3f978d0cea7e9853627935b30d451742eeb4239a81a677bdee6bf6"}, + {file = "fonttools-4.49.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d647a0e697e5daa98c87993726da8281c7233d9d4ffe410812a4896c7c57c075"}, + {file = "fonttools-4.49.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f3bbe672df03563d1f3a691ae531f2e31f84061724c319652039e5a70927167e"}, + {file = "fonttools-4.49.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bebd91041dda0d511b0d303180ed36e31f4f54b106b1259b69fade68413aa7ff"}, + {file = "fonttools-4.49.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4145f91531fd43c50f9eb893faa08399816bb0b13c425667c48475c9f3a2b9b5"}, + {file = "fonttools-4.49.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ea329dafb9670ffbdf4dbc3b0e5c264104abcd8441d56de77f06967f032943cb"}, + {file = "fonttools-4.49.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c076a9e548521ecc13d944b1d261ff3d7825048c338722a4bd126d22316087b7"}, + {file = "fonttools-4.49.0-cp312-cp312-win32.whl", hash = "sha256:b607ea1e96768d13be26d2b400d10d3ebd1456343eb5eaddd2f47d1c4bd00880"}, + {file = "fonttools-4.49.0-cp312-cp312-win_amd64.whl", hash = "sha256:a974c49a981e187381b9cc2c07c6b902d0079b88ff01aed34695ec5360767034"}, + {file = "fonttools-4.49.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b85ec0bdd7bdaa5c1946398cbb541e90a6dfc51df76dfa88e0aaa41b335940cb"}, + {file = "fonttools-4.49.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:af20acbe198a8a790618ee42db192eb128afcdcc4e96d99993aca0b60d1faeb4"}, + {file = "fonttools-4.49.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d418b1fee41a1d14931f7ab4b92dc0bc323b490e41d7a333eec82c9f1780c75"}, + {file = "fonttools-4.49.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b44a52b8e6244b6548851b03b2b377a9702b88ddc21dcaf56a15a0393d425cb9"}, + {file = "fonttools-4.49.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7c7125068e04a70739dad11857a4d47626f2b0bd54de39e8622e89701836eabd"}, + {file = "fonttools-4.49.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:29e89d0e1a7f18bc30f197cfadcbef5a13d99806447c7e245f5667579a808036"}, + {file = "fonttools-4.49.0-cp38-cp38-win32.whl", hash = "sha256:9d95fa0d22bf4f12d2fb7b07a46070cdfc19ef5a7b1c98bc172bfab5bf0d6844"}, + {file = "fonttools-4.49.0-cp38-cp38-win_amd64.whl", hash = "sha256:768947008b4dc552d02772e5ebd49e71430a466e2373008ce905f953afea755a"}, + {file = "fonttools-4.49.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:08877e355d3dde1c11973bb58d4acad1981e6d1140711230a4bfb40b2b937ccc"}, + {file = "fonttools-4.49.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fdb54b076f25d6b0f0298dc706acee5052de20c83530fa165b60d1f2e9cbe3cb"}, + {file = "fonttools-4.49.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0af65c720520710cc01c293f9c70bd69684365c6015cc3671db2b7d807fe51f2"}, + {file = "fonttools-4.49.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f255ce8ed7556658f6d23f6afd22a6d9bbc3edb9b96c96682124dc487e1bf42"}, + {file = "fonttools-4.49.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d00af0884c0e65f60dfaf9340e26658836b935052fdd0439952ae42e44fdd2be"}, + {file = "fonttools-4.49.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:263832fae27481d48dfafcc43174644b6706639661e242902ceb30553557e16c"}, + {file = "fonttools-4.49.0-cp39-cp39-win32.whl", hash = "sha256:0404faea044577a01bb82d47a8fa4bc7a54067fa7e324785dd65d200d6dd1133"}, + {file = "fonttools-4.49.0-cp39-cp39-win_amd64.whl", hash = "sha256:b050d362df50fc6e38ae3954d8c29bf2da52be384649ee8245fdb5186b620836"}, + {file = "fonttools-4.49.0-py3-none-any.whl", hash = "sha256:af281525e5dd7fa0b39fb1667b8d5ca0e2a9079967e14c4bfe90fd1cd13e0f18"}, + {file = "fonttools-4.49.0.tar.gz", hash = "sha256:ebf46e7f01b7af7861310417d7c49591a85d99146fc23a5ba82fdb28af156321"}, ] [package.extras] @@ -1563,13 +1563,13 @@ zoneinfo = ["backports.zoneinfo (>=0.2.1)", "tzdata (>=2022.1)"] [[package]] name = "identify" -version = "2.5.34" +version = "2.5.35" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.5.34-py2.py3-none-any.whl", hash = "sha256:a4316013779e433d08b96e5eabb7f641e6c7942e4ab5d4c509ebd2e7a8994aed"}, - {file = "identify-2.5.34.tar.gz", hash = "sha256:ee17bc9d499899bc9eaec1ac7bf2dc9eedd480db9d88b96d123d3b64a9d34f5d"}, + {file = "identify-2.5.35-py2.py3-none-any.whl", hash = "sha256:c4de0081837b211594f8e877a6b4fad7ca32bbfc1a9307fdd61c28bfe923f13e"}, + {file = "identify-2.5.35.tar.gz", hash = "sha256:10a7ca245cfcd756a554a7288159f72ff105ad233c7c4b9c6f0f4d108f5f6791"}, ] [package.extras] @@ -2116,39 +2116,39 @@ files = [ [[package]] name = "matplotlib" -version = "3.8.2" +version = "3.8.3" description = "Python plotting package" optional = false python-versions = ">=3.9" files = [ - {file = "matplotlib-3.8.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:09796f89fb71a0c0e1e2f4bdaf63fb2cefc84446bb963ecdeb40dfee7dfa98c7"}, - {file = "matplotlib-3.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6f9c6976748a25e8b9be51ea028df49b8e561eed7809146da7a47dbecebab367"}, - {file = "matplotlib-3.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b78e4f2cedf303869b782071b55fdde5987fda3038e9d09e58c91cc261b5ad18"}, - {file = "matplotlib-3.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e208f46cf6576a7624195aa047cb344a7f802e113bb1a06cfd4bee431de5e31"}, - {file = "matplotlib-3.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:46a569130ff53798ea5f50afce7406e91fdc471ca1e0e26ba976a8c734c9427a"}, - {file = "matplotlib-3.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:830f00640c965c5b7f6bc32f0d4ce0c36dfe0379f7dd65b07a00c801713ec40a"}, - {file = "matplotlib-3.8.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d86593ccf546223eb75a39b44c32788e6f6440d13cfc4750c1c15d0fcb850b63"}, - {file = "matplotlib-3.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a5430836811b7652991939012f43d2808a2db9b64ee240387e8c43e2e5578c8"}, - {file = "matplotlib-3.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9576723858a78751d5aacd2497b8aef29ffea6d1c95981505877f7ac28215c6"}, - {file = "matplotlib-3.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ba9cbd8ac6cf422f3102622b20f8552d601bf8837e49a3afed188d560152788"}, - {file = "matplotlib-3.8.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:03f9d160a29e0b65c0790bb07f4f45d6a181b1ac33eb1bb0dd225986450148f0"}, - {file = "matplotlib-3.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:3773002da767f0a9323ba1a9b9b5d00d6257dbd2a93107233167cfb581f64717"}, - {file = "matplotlib-3.8.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:4c318c1e95e2f5926fba326f68177dee364aa791d6df022ceb91b8221bd0a627"}, - {file = "matplotlib-3.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:091275d18d942cf1ee9609c830a1bc36610607d8223b1b981c37d5c9fc3e46a4"}, - {file = "matplotlib-3.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b0f3b8ea0e99e233a4bcc44590f01604840d833c280ebb8fe5554fd3e6cfe8d"}, - {file = "matplotlib-3.8.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b1704a530395aaf73912be741c04d181f82ca78084fbd80bc737be04848331"}, - {file = "matplotlib-3.8.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:533b0e3b0c6768eef8cbe4b583731ce25a91ab54a22f830db2b031e83cca9213"}, - {file = "matplotlib-3.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:0f4fc5d72b75e2c18e55eb32292659cf731d9d5b312a6eb036506304f4675630"}, - {file = "matplotlib-3.8.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:deaed9ad4da0b1aea77fe0aa0cebb9ef611c70b3177be936a95e5d01fa05094f"}, - {file = "matplotlib-3.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:172f4d0fbac3383d39164c6caafd3255ce6fa58f08fc392513a0b1d3b89c4f89"}, - {file = "matplotlib-3.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7d36c2209d9136cd8e02fab1c0ddc185ce79bc914c45054a9f514e44c787917"}, - {file = "matplotlib-3.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5864bdd7da445e4e5e011b199bb67168cdad10b501750367c496420f2ad00843"}, - {file = "matplotlib-3.8.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ef8345b48e95cee45ff25192ed1f4857273117917a4dcd48e3905619bcd9c9b8"}, - {file = "matplotlib-3.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:7c48d9e221b637c017232e3760ed30b4e8d5dfd081daf327e829bf2a72c731b4"}, - {file = "matplotlib-3.8.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:aa11b3c6928a1e496c1a79917d51d4cd5d04f8a2e75f21df4949eeefdf697f4b"}, - {file = "matplotlib-3.8.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1095fecf99eeb7384dabad4bf44b965f929a5f6079654b681193edf7169ec20"}, - {file = "matplotlib-3.8.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:bddfb1db89bfaa855912261c805bd0e10218923cc262b9159a49c29a7a1c1afa"}, - {file = "matplotlib-3.8.2.tar.gz", hash = "sha256:01a978b871b881ee76017152f1f1a0cbf6bd5f7b8ff8c96df0df1bd57d8755a1"}, + {file = "matplotlib-3.8.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:cf60138ccc8004f117ab2a2bad513cc4d122e55864b4fe7adf4db20ca68a078f"}, + {file = "matplotlib-3.8.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5f557156f7116be3340cdeef7f128fa99b0d5d287d5f41a16e169819dcf22357"}, + {file = "matplotlib-3.8.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f386cf162b059809ecfac3bcc491a9ea17da69fa35c8ded8ad154cd4b933d5ec"}, + {file = "matplotlib-3.8.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3c5f96f57b0369c288bf6f9b5274ba45787f7e0589a34d24bdbaf6d3344632f"}, + {file = "matplotlib-3.8.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:83e0f72e2c116ca7e571c57aa29b0fe697d4c6425c4e87c6e994159e0c008635"}, + {file = "matplotlib-3.8.3-cp310-cp310-win_amd64.whl", hash = "sha256:1c5c8290074ba31a41db1dc332dc2b62def469ff33766cbe325d32a3ee291aea"}, + {file = "matplotlib-3.8.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5184e07c7e1d6d1481862ee361905b7059f7fe065fc837f7c3dc11eeb3f2f900"}, + {file = "matplotlib-3.8.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d7e7e0993d0758933b1a241a432b42c2db22dfa37d4108342ab4afb9557cbe3e"}, + {file = "matplotlib-3.8.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:04b36ad07eac9740fc76c2aa16edf94e50b297d6eb4c081e3add863de4bb19a7"}, + {file = "matplotlib-3.8.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c42dae72a62f14982f1474f7e5c9959fc4bc70c9de11cc5244c6e766200ba65"}, + {file = "matplotlib-3.8.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bf5932eee0d428192c40b7eac1399d608f5d995f975cdb9d1e6b48539a5ad8d0"}, + {file = "matplotlib-3.8.3-cp311-cp311-win_amd64.whl", hash = "sha256:40321634e3a05ed02abf7c7b47a50be50b53ef3eaa3a573847431a545585b407"}, + {file = "matplotlib-3.8.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:09074f8057917d17ab52c242fdf4916f30e99959c1908958b1fc6032e2d0f6d4"}, + {file = "matplotlib-3.8.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5745f6d0fb5acfabbb2790318db03809a253096e98c91b9a31969df28ee604aa"}, + {file = "matplotlib-3.8.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b97653d869a71721b639714b42d87cda4cfee0ee74b47c569e4874c7590c55c5"}, + {file = "matplotlib-3.8.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:242489efdb75b690c9c2e70bb5c6550727058c8a614e4c7716f363c27e10bba1"}, + {file = "matplotlib-3.8.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:83c0653c64b73926730bd9ea14aa0f50f202ba187c307a881673bad4985967b7"}, + {file = "matplotlib-3.8.3-cp312-cp312-win_amd64.whl", hash = "sha256:ef6c1025a570354297d6c15f7d0f296d95f88bd3850066b7f1e7b4f2f4c13a39"}, + {file = "matplotlib-3.8.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c4af3f7317f8a1009bbb2d0bf23dfaba859eb7dd4ccbd604eba146dccaaaf0a4"}, + {file = "matplotlib-3.8.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4c6e00a65d017d26009bac6808f637b75ceade3e1ff91a138576f6b3065eeeba"}, + {file = "matplotlib-3.8.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7b49ab49a3bea17802df6872f8d44f664ba8f9be0632a60c99b20b6db2165b7"}, + {file = "matplotlib-3.8.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6728dde0a3997396b053602dbd907a9bd64ec7d5cf99e728b404083698d3ca01"}, + {file = "matplotlib-3.8.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:813925d08fb86aba139f2d31864928d67511f64e5945ca909ad5bc09a96189bb"}, + {file = "matplotlib-3.8.3-cp39-cp39-win_amd64.whl", hash = "sha256:cd3a0c2be76f4e7be03d34a14d49ded6acf22ef61f88da600a18a5cd8b3c5f3c"}, + {file = "matplotlib-3.8.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:fa93695d5c08544f4a0dfd0965f378e7afc410d8672816aff1e81be1f45dbf2e"}, + {file = "matplotlib-3.8.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9764df0e8778f06414b9d281a75235c1e85071f64bb5d71564b97c1306a2afc"}, + {file = "matplotlib-3.8.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:5e431a09e6fab4012b01fc155db0ce6dccacdbabe8198197f523a4ef4805eb26"}, + {file = "matplotlib-3.8.3.tar.gz", hash = "sha256:7b416239e9ae38be54b028abbf9048aff5054a9aba5416bef0bd17f9162ce161"}, ] [package.dependencies] @@ -3041,17 +3041,17 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "polyline" -version = "2.0.1" +version = "2.0.2" description = "A Python implementation of Google's Encoded Polyline Algorithm Format." optional = false python-versions = ">=3.7" files = [ - {file = "polyline-2.0.1-py3-none-any.whl", hash = "sha256:7b1ff647be393143c1b9268738d9efb98a327dd0b29f9c3b84552dff0e34ea3c"}, - {file = "polyline-2.0.1.tar.gz", hash = "sha256:74cb5cea098dddf09d1a5a1f17af9184d371cbf3e9723de0194e530ec39ca1f6"}, + {file = "polyline-2.0.2-py3-none-any.whl", hash = "sha256:389655c893bdabf2863c6aaa49490cf83dcdcec86ae715f67044ee98be57bef5"}, + {file = "polyline-2.0.2.tar.gz", hash = "sha256:10541e759c5fd51f746ee304e9af94744089a4055b6257b293b3afd1df64e369"}, ] [package.extras] -dev = ["pylint (>=2.15.10,<2.16.0)", "pytest (>=7.0,<8.0)", "pytest-cov (>=4.0,<5.0)", "sphinx (>=4.2.0,<4.3.0)", "sphinx-rtd-theme (>=1.0.0,<1.1.0)", "toml (>=0.10.2,<0.11.0)"] +dev = ["pylint (>=3.0.3,<3.1.0)", "pytest (>=7.0,<8.0)", "pytest-cov (>=4.0,<5.0)", "sphinx (>=5.3.0,<5.4.0)", "sphinx-rtd-theme (>=1.2.0,<1.3.0)", "toml (>=0.10.2,<0.11.0)"] publish = ["build (>=0.8,<1.0)", "twine (>=4.0,<5.0)"] [[package]] @@ -3085,13 +3085,13 @@ files = [ [[package]] name = "pre-commit" -version = "3.6.1" +version = "3.6.2" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.9" files = [ - {file = "pre_commit-3.6.1-py2.py3-none-any.whl", hash = "sha256:9fe989afcf095d2c4796ce7c553cf28d4d4a9b9346de3cda079bcf40748454a4"}, - {file = "pre_commit-3.6.1.tar.gz", hash = "sha256:c90961d8aa706f75d60935aba09469a6b0bcb8345f127c3fbee4bdc5f114cf4b"}, + {file = "pre_commit-3.6.2-py2.py3-none-any.whl", hash = "sha256:ba637c2d7a670c10daedc059f5c49b5bd0aadbccfcd7ec15592cf9665117532c"}, + {file = "pre_commit-3.6.2.tar.gz", hash = "sha256:c3ef34f463045c88658c5b99f38c1e297abdcc0ff13f98d3370055fbbfabc67e"}, ] [package.dependencies] @@ -3113,22 +3113,22 @@ files = [ [[package]] name = "protobuf" -version = "4.25.2" +version = "4.25.3" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-4.25.2-cp310-abi3-win32.whl", hash = "sha256:b50c949608682b12efb0b2717f53256f03636af5f60ac0c1d900df6213910fd6"}, - {file = "protobuf-4.25.2-cp310-abi3-win_amd64.whl", hash = "sha256:8f62574857ee1de9f770baf04dde4165e30b15ad97ba03ceac65f760ff018ac9"}, - {file = "protobuf-4.25.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:2db9f8fa64fbdcdc93767d3cf81e0f2aef176284071507e3ede160811502fd3d"}, - {file = "protobuf-4.25.2-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:10894a2885b7175d3984f2be8d9850712c57d5e7587a2410720af8be56cdaf62"}, - {file = "protobuf-4.25.2-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:fc381d1dd0516343f1440019cedf08a7405f791cd49eef4ae1ea06520bc1c020"}, - {file = "protobuf-4.25.2-cp38-cp38-win32.whl", hash = "sha256:33a1aeef4b1927431d1be780e87b641e322b88d654203a9e9d93f218ee359e61"}, - {file = "protobuf-4.25.2-cp38-cp38-win_amd64.whl", hash = "sha256:47f3de503fe7c1245f6f03bea7e8d3ec11c6c4a2ea9ef910e3221c8a15516d62"}, - {file = "protobuf-4.25.2-cp39-cp39-win32.whl", hash = "sha256:5e5c933b4c30a988b52e0b7c02641760a5ba046edc5e43d3b94a74c9fc57c1b3"}, - {file = "protobuf-4.25.2-cp39-cp39-win_amd64.whl", hash = "sha256:d66a769b8d687df9024f2985d5137a337f957a0916cf5464d1513eee96a63ff0"}, - {file = "protobuf-4.25.2-py3-none-any.whl", hash = "sha256:a8b7a98d4ce823303145bf3c1a8bdb0f2f4642a414b196f04ad9853ed0c8f830"}, - {file = "protobuf-4.25.2.tar.gz", hash = "sha256:fe599e175cb347efc8ee524bcd4b902d11f7262c0e569ececcb89995c15f0a5e"}, + {file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"}, + {file = "protobuf-4.25.3-cp310-abi3-win_amd64.whl", hash = "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8"}, + {file = "protobuf-4.25.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d"}, + {file = "protobuf-4.25.3-cp38-cp38-win32.whl", hash = "sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2"}, + {file = "protobuf-4.25.3-cp38-cp38-win_amd64.whl", hash = "sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4"}, + {file = "protobuf-4.25.3-cp39-cp39-win32.whl", hash = "sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4"}, + {file = "protobuf-4.25.3-cp39-cp39-win_amd64.whl", hash = "sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c"}, + {file = "protobuf-4.25.3-py3-none-any.whl", hash = "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9"}, + {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"}, ] [[package]] @@ -6468,13 +6468,13 @@ cp2110 = ["hidapi"] [[package]] name = "pytest" -version = "8.0.0" +version = "8.0.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.0.0-py3-none-any.whl", hash = "sha256:50fb9cbe836c3f20f0dfa99c565201fb75dc54c8d76373cd1bde06b06657bdb6"}, - {file = "pytest-8.0.0.tar.gz", hash = "sha256:249b1b0864530ba251b7438274c4d251c58d868edaaec8762893ad4a0d71c36c"}, + {file = "pytest-8.0.1-py3-none-any.whl", hash = "sha256:3e4f16fe1c0a9dc9d9389161c127c3edc5d810c38d6793042fb81d9f48a59fca"}, + {file = "pytest-8.0.1.tar.gz", hash = "sha256:267f6563751877d772019b13aacbe4e860d73fe8f651f28112e9ac37de7513ae"}, ] [package.dependencies] @@ -6639,12 +6639,12 @@ numpy = ["numpy (>=1.6.0)"] [[package]] name = "pytweening" -version = "1.0.7" +version = "1.1.0" description = "A collection of tweening / easing functions." optional = false python-versions = "*" files = [ - {file = "pytweening-1.0.7.tar.gz", hash = "sha256:767134f1bf57b76c1ce9f692dd1cfc776d9a279de6724e8d04854508fd7ededb"}, + {file = "pytweening-1.1.0.tar.gz", hash = "sha256:0d8e14af529dd816ad4aa4a86757dfb5fe2fc2897e06f5db60183706a9370828"}, ] [[package]] @@ -6924,28 +6924,28 @@ docs = ["furo (==2023.9.10)", "pyenchant (==3.2.2)", "sphinx (==7.1.2)", "sphinx [[package]] name = "ruff" -version = "0.2.1" +version = "0.2.2" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.2.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:dd81b911d28925e7e8b323e8d06951554655021df8dd4ac3045d7212ac4ba080"}, - {file = "ruff-0.2.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:dc586724a95b7d980aa17f671e173df00f0a2eef23f8babbeee663229a938fec"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c92db7101ef5bfc18e96777ed7bc7c822d545fa5977e90a585accac43d22f18a"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:13471684694d41ae0f1e8e3a7497e14cd57ccb7dd72ae08d56a159d6c9c3e30e"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a11567e20ea39d1f51aebd778685582d4c56ccb082c1161ffc10f79bebe6df35"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:00a818e2db63659570403e44383ab03c529c2b9678ba4ba6c105af7854008105"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be60592f9d218b52f03384d1325efa9d3b41e4c4d55ea022cd548547cc42cd2b"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbd2288890b88e8aab4499e55148805b58ec711053588cc2f0196a44f6e3d855"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3ef052283da7dec1987bba8d8733051c2325654641dfe5877a4022108098683"}, - {file = "ruff-0.2.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7022d66366d6fded4ba3889f73cd791c2d5621b2ccf34befc752cb0df70f5fad"}, - {file = "ruff-0.2.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0a725823cb2a3f08ee743a534cb6935727d9e47409e4ad72c10a3faf042ad5ba"}, - {file = "ruff-0.2.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0034d5b6323e6e8fe91b2a1e55b02d92d0b582d2953a2b37a67a2d7dedbb7acc"}, - {file = "ruff-0.2.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e5cb5526d69bb9143c2e4d2a115d08ffca3d8e0fddc84925a7b54931c96f5c02"}, - {file = "ruff-0.2.1-py3-none-win32.whl", hash = "sha256:6b95ac9ce49b4fb390634d46d6ece32ace3acdd52814671ccaf20b7f60adb232"}, - {file = "ruff-0.2.1-py3-none-win_amd64.whl", hash = "sha256:e3affdcbc2afb6f5bd0eb3130139ceedc5e3f28d206fe49f63073cb9e65988e0"}, - {file = "ruff-0.2.1-py3-none-win_arm64.whl", hash = "sha256:efababa8e12330aa94a53e90a81eb6e2d55f348bc2e71adbf17d9cad23c03ee6"}, - {file = "ruff-0.2.1.tar.gz", hash = "sha256:3b42b5d8677cd0c72b99fcaf068ffc62abb5a19e71b4a3b9cfa50658a0af02f1"}, + {file = "ruff-0.2.2-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0a9efb032855ffb3c21f6405751d5e147b0c6b631e3ca3f6b20f917572b97eb6"}, + {file = "ruff-0.2.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d450b7fbff85913f866a5384d8912710936e2b96da74541c82c1b458472ddb39"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecd46e3106850a5c26aee114e562c329f9a1fbe9e4821b008c4404f64ff9ce73"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e22676a5b875bd72acd3d11d5fa9075d3a5f53b877fe7b4793e4673499318ba"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1695700d1e25a99d28f7a1636d85bafcc5030bba9d0578c0781ba1790dbcf51c"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b0c232af3d0bd8f521806223723456ffebf8e323bd1e4e82b0befb20ba18388e"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f63d96494eeec2fc70d909393bcd76c69f35334cdbd9e20d089fb3f0640216ca"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a61ea0ff048e06de273b2e45bd72629f470f5da8f71daf09fe481278b175001"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1439c8f407e4f356470e54cdecdca1bd5439a0673792dbe34a2b0a551a2fe3"}, + {file = "ruff-0.2.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:940de32dc8853eba0f67f7198b3e79bc6ba95c2edbfdfac2144c8235114d6726"}, + {file = "ruff-0.2.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0c126da55c38dd917621552ab430213bdb3273bb10ddb67bc4b761989210eb6e"}, + {file = "ruff-0.2.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:3b65494f7e4bed2e74110dac1f0d17dc8e1f42faaa784e7c58a98e335ec83d7e"}, + {file = "ruff-0.2.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1ec49be4fe6ddac0503833f3ed8930528e26d1e60ad35c2446da372d16651ce9"}, + {file = "ruff-0.2.2-py3-none-win32.whl", hash = "sha256:d920499b576f6c68295bc04e7b17b6544d9d05f196bb3aac4358792ef6f34325"}, + {file = "ruff-0.2.2-py3-none-win_amd64.whl", hash = "sha256:cc9a91ae137d687f43a44c900e5d95e9617cb37d4c989e462980ba27039d239d"}, + {file = "ruff-0.2.2-py3-none-win_arm64.whl", hash = "sha256:c9d15fc41e6054bfc7200478720570078f0b41c9ae4f010bcc16bd6f4d1aacdd"}, + {file = "ruff-0.2.2.tar.gz", hash = "sha256:e62ed7f36b3068a30ba39193a14274cd706bc486fad521276458022f7bccb31d"}, ] [[package]] @@ -7027,13 +7027,13 @@ stats = ["scipy (>=1.7)", "statsmodels (>=0.12)"] [[package]] name = "sentry-sdk" -version = "1.40.3" +version = "1.40.5" description = "Python client for Sentry (https://sentry.io)" optional = false python-versions = "*" files = [ - {file = "sentry-sdk-1.40.3.tar.gz", hash = "sha256:3c2b027979bb400cd65a47970e64f8cef8acda86b288a27f42a98692505086cd"}, - {file = "sentry_sdk-1.40.3-py2.py3-none-any.whl", hash = "sha256:73383f28311ae55602bb6cc3b013830811135ba5521e41333a6e68f269413502"}, + {file = "sentry-sdk-1.40.5.tar.gz", hash = "sha256:d2dca2392cc5c9a2cc9bb874dd7978ebb759682fe4fe889ee7e970ee8dd1c61e"}, + {file = "sentry_sdk-1.40.5-py2.py3-none-any.whl", hash = "sha256:d188b407c9bacbe2a50a824e1f8fb99ee1aeb309133310488c570cb6d7056643"}, ] [package.dependencies] @@ -7188,56 +7188,56 @@ testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jar [[package]] name = "shapely" -version = "2.0.2" +version = "2.0.3" description = "Manipulation and analysis of geometric objects" optional = false python-versions = ">=3.7" files = [ - {file = "shapely-2.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6ca8cffbe84ddde8f52b297b53f8e0687bd31141abb2c373fd8a9f032df415d6"}, - {file = "shapely-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:baa14fc27771e180c06b499a0a7ba697c7988c7b2b6cba9a929a19a4d2762de3"}, - {file = "shapely-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:36480e32c434d168cdf2f5e9862c84aaf4d714a43a8465ae3ce8ff327f0affb7"}, - {file = "shapely-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ef753200cbffd4f652efb2c528c5474e5a14341a473994d90ad0606522a46a2"}, - {file = "shapely-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9a41ff4323fc9d6257759c26eb1cf3a61ebc7e611e024e6091f42977303fd3a"}, - {file = "shapely-2.0.2-cp310-cp310-win32.whl", hash = "sha256:72b5997272ae8c25f0fd5b3b967b3237e87fab7978b8d6cd5fa748770f0c5d68"}, - {file = "shapely-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:34eac2337cbd67650248761b140d2535855d21b969d76d76123317882d3a0c1a"}, - {file = "shapely-2.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5b0c052709c8a257c93b0d4943b0b7a3035f87e2d6a8ac9407b6a992d206422f"}, - {file = "shapely-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2d217e56ae067e87b4e1731d0dc62eebe887ced729ba5c2d4590e9e3e9fdbd88"}, - {file = "shapely-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94ac128ae2ab4edd0bffcd4e566411ea7bdc738aeaf92c32a8a836abad725f9f"}, - {file = "shapely-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa3ee28f5e63a130ec5af4dc3c4cb9c21c5788bb13c15e89190d163b14f9fb89"}, - {file = "shapely-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:737dba15011e5a9b54a8302f1748b62daa207c9bc06f820cd0ad32a041f1c6f2"}, - {file = "shapely-2.0.2-cp311-cp311-win32.whl", hash = "sha256:45ac6906cff0765455a7b49c1670af6e230c419507c13e2f75db638c8fc6f3bd"}, - {file = "shapely-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:dc9342fc82e374130db86a955c3c4525bfbf315a248af8277a913f30911bed9e"}, - {file = "shapely-2.0.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:06f193091a7c6112fc08dfd195a1e3846a64306f890b151fa8c63b3e3624202c"}, - {file = "shapely-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:eebe544df5c018134f3c23b6515877f7e4cd72851f88a8d0c18464f414d141a2"}, - {file = "shapely-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7e92e7c255f89f5cdf777690313311f422aa8ada9a3205b187113274e0135cd8"}, - {file = "shapely-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be46d5509b9251dd9087768eaf35a71360de6afac82ce87c636990a0871aa18b"}, - {file = "shapely-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5533a925d8e211d07636ffc2fdd9a7f9f13d54686d00577eeb11d16f00be9c4"}, - {file = "shapely-2.0.2-cp312-cp312-win32.whl", hash = "sha256:084b023dae8ad3d5b98acee9d3bf098fdf688eb0bb9b1401e8b075f6a627b611"}, - {file = "shapely-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:ea84d1cdbcf31e619d672b53c4532f06253894185ee7acb8ceb78f5f33cbe033"}, - {file = "shapely-2.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ed1e99702125e7baccf401830a3b94d810d5c70b329b765fe93451fe14cf565b"}, - {file = "shapely-2.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7d897e6bdc6bc64f7f65155dbbb30e49acaabbd0d9266b9b4041f87d6e52b3a"}, - {file = "shapely-2.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0521d76d1e8af01e712db71da9096b484f081e539d4f4a8c97342e7971d5e1b4"}, - {file = "shapely-2.0.2-cp37-cp37m-win32.whl", hash = "sha256:5324be299d4c533ecfcfd43424dfd12f9428fd6f12cda38a4316da001d6ef0ea"}, - {file = "shapely-2.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:78128357a0cee573257a0c2c388d4b7bf13cb7dbe5b3fe5d26d45ebbe2a39e25"}, - {file = "shapely-2.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:87dc2be34ac3a3a4a319b963c507ac06682978a5e6c93d71917618b14f13066e"}, - {file = "shapely-2.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:42997ac806e4583dad51c80a32d38570fd9a3d4778f5e2c98f9090aa7db0fe91"}, - {file = "shapely-2.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ccfd5fa10a37e67dbafc601c1ddbcbbfef70d34c3f6b0efc866ddbdb55893a6c"}, - {file = "shapely-2.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7c95d3379ae3abb74058938a9fcbc478c6b2e28d20dace38f8b5c587dde90aa"}, - {file = "shapely-2.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a21353d28209fb0d8cc083e08ca53c52666e0d8a1f9bbe23b6063967d89ed24"}, - {file = "shapely-2.0.2-cp38-cp38-win32.whl", hash = "sha256:03e63a99dfe6bd3beb8d5f41ec2086585bb969991d603f9aeac335ad396a06d4"}, - {file = "shapely-2.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:c6fd29fbd9cd76350bd5cc14c49de394a31770aed02d74203e23b928f3d2f1aa"}, - {file = "shapely-2.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1f217d28ecb48e593beae20a0082a95bd9898d82d14b8fcb497edf6bff9a44d7"}, - {file = "shapely-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:394e5085b49334fd5b94fa89c086edfb39c3ecab7f669e8b2a4298b9d523b3a5"}, - {file = "shapely-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fd3ad17b64466a033848c26cb5b509625c87d07dcf39a1541461cacdb8f7e91c"}, - {file = "shapely-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d41a116fcad58048d7143ddb01285e1a8780df6dc1f56c3b1e1b7f12ed296651"}, - {file = "shapely-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dea9a0651333cf96ef5bb2035044e3ad6a54f87d90e50fe4c2636debf1b77abc"}, - {file = "shapely-2.0.2-cp39-cp39-win32.whl", hash = "sha256:b8eb0a92f7b8c74f9d8fdd1b40d395113f59bd8132ca1348ebcc1f5aece94b96"}, - {file = "shapely-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:794affd80ca0f2c536fc948a3afa90bd8fb61ebe37fe873483ae818e7f21def4"}, - {file = "shapely-2.0.2.tar.gz", hash = "sha256:1713cc04c171baffc5b259ba8531c58acc2a301707b7f021d88a15ed090649e7"}, -] - -[package.dependencies] -numpy = ">=1.14" + {file = "shapely-2.0.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:af7e9abe180b189431b0f490638281b43b84a33a960620e6b2e8d3e3458b61a1"}, + {file = "shapely-2.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:98040462b36ced9671e266b95c326b97f41290d9d17504a1ee4dc313a7667b9c"}, + {file = "shapely-2.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:71eb736ef2843f23473c6e37f6180f90f0a35d740ab284321548edf4e55d9a52"}, + {file = "shapely-2.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:881eb9dbbb4a6419667e91fcb20313bfc1e67f53dbb392c6840ff04793571ed1"}, + {file = "shapely-2.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f10d2ccf0554fc0e39fad5886c839e47e207f99fdf09547bc687a2330efda35b"}, + {file = "shapely-2.0.3-cp310-cp310-win32.whl", hash = "sha256:6dfdc077a6fcaf74d3eab23a1ace5abc50c8bce56ac7747d25eab582c5a2990e"}, + {file = "shapely-2.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:64c5013dacd2d81b3bb12672098a0b2795c1bf8190cfc2980e380f5ef9d9e4d9"}, + {file = "shapely-2.0.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:56cee3e4e8159d6f2ce32e421445b8e23154fd02a0ac271d6a6c0b266a8e3cce"}, + {file = "shapely-2.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:619232c8276fded09527d2a9fd91a7885ff95c0ff9ecd5e3cb1e34fbb676e2ae"}, + {file = "shapely-2.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b2a7d256db6f5b4b407dc0c98dd1b2fcf1c9c5814af9416e5498d0a2e4307a4b"}, + {file = "shapely-2.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e45f0c8cd4583647db3216d965d49363e6548c300c23fd7e57ce17a03f824034"}, + {file = "shapely-2.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13cb37d3826972a82748a450328fe02a931dcaed10e69a4d83cc20ba021bc85f"}, + {file = "shapely-2.0.3-cp311-cp311-win32.whl", hash = "sha256:9302d7011e3e376d25acd30d2d9e70d315d93f03cc748784af19b00988fc30b1"}, + {file = "shapely-2.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:6b464f2666b13902835f201f50e835f2f153f37741db88f68c7f3b932d3505fa"}, + {file = "shapely-2.0.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e86e7cb8e331a4850e0c2a8b2d66dc08d7a7b301b8d1d34a13060e3a5b4b3b55"}, + {file = "shapely-2.0.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c91981c99ade980fc49e41a544629751a0ccd769f39794ae913e53b07b2f78b9"}, + {file = "shapely-2.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd45d456983dc60a42c4db437496d3f08a4201fbf662b69779f535eb969660af"}, + {file = "shapely-2.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:882fb1ffc7577e88c1194f4f1757e277dc484ba096a3b94844319873d14b0f2d"}, + {file = "shapely-2.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9f2d93bff2ea52fa93245798cddb479766a18510ea9b93a4fb9755c79474889"}, + {file = "shapely-2.0.3-cp312-cp312-win32.whl", hash = "sha256:99abad1fd1303b35d991703432c9481e3242b7b3a393c186cfb02373bf604004"}, + {file = "shapely-2.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:6f555fe3304a1f40398977789bc4fe3c28a11173196df9ece1e15c5bc75a48db"}, + {file = "shapely-2.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a983cc418c1fa160b7d797cfef0e0c9f8c6d5871e83eae2c5793fce6a837fad9"}, + {file = "shapely-2.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18bddb8c327f392189a8d5d6b9a858945722d0bb95ccbd6a077b8e8fc4c7890d"}, + {file = "shapely-2.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:442f4dcf1eb58c5a4e3428d88e988ae153f97ab69a9f24e07bf4af8038536325"}, + {file = "shapely-2.0.3-cp37-cp37m-win32.whl", hash = "sha256:31a40b6e3ab00a4fd3a1d44efb2482278642572b8e0451abdc8e0634b787173e"}, + {file = "shapely-2.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:59b16976c2473fec85ce65cc9239bef97d4205ab3acead4e6cdcc72aee535679"}, + {file = "shapely-2.0.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:705efbce1950a31a55b1daa9c6ae1c34f1296de71ca8427974ec2f27d57554e3"}, + {file = "shapely-2.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:601c5c0058a6192df704cb889439f64994708563f57f99574798721e9777a44b"}, + {file = "shapely-2.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f24ecbb90a45c962b3b60d8d9a387272ed50dc010bfe605f1d16dfc94772d8a1"}, + {file = "shapely-2.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8c2a2989222c6062f7a0656e16276c01bb308bc7e5d999e54bf4e294ce62e76"}, + {file = "shapely-2.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42bceb9bceb3710a774ce04908fda0f28b291323da2688f928b3f213373b5aee"}, + {file = "shapely-2.0.3-cp38-cp38-win32.whl", hash = "sha256:54d925c9a311e4d109ec25f6a54a8bd92cc03481a34ae1a6a92c1fe6729b7e01"}, + {file = "shapely-2.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:300d203b480a4589adefff4c4af0b13919cd6d760ba3cbb1e56275210f96f654"}, + {file = "shapely-2.0.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:083d026e97b6c1f4a9bd2a9171c7692461092ed5375218170d91705550eecfd5"}, + {file = "shapely-2.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:27b6e1910094d93e9627f2664121e0e35613262fc037051680a08270f6058daf"}, + {file = "shapely-2.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:71b2de56a9e8c0e5920ae5ddb23b923490557ac50cb0b7fa752761bf4851acde"}, + {file = "shapely-2.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d279e56bbb68d218d63f3efc80c819cedcceef0e64efbf058a1df89dc57201b"}, + {file = "shapely-2.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88566d01a30f0453f7d038db46bc83ce125e38e47c5f6bfd4c9c287010e9bf74"}, + {file = "shapely-2.0.3-cp39-cp39-win32.whl", hash = "sha256:58afbba12c42c6ed44c4270bc0e22f3dadff5656d711b0ad335c315e02d04707"}, + {file = "shapely-2.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:5026b30433a70911979d390009261b8c4021ff87c7c3cbd825e62bb2ffa181bc"}, + {file = "shapely-2.0.3.tar.gz", hash = "sha256:4d65d0aa7910af71efa72fd6447e02a8e5dd44da81a983de9d736d6e6ccbe674"}, +] + +[package.dependencies] +numpy = ">=1.14,<2" [package.extras] docs = ["matplotlib", "numpydoc (==1.1.*)", "sphinx", "sphinx-book-theme", "sphinx-remove-toctrees"] @@ -7594,13 +7594,13 @@ telegram = ["requests"] [[package]] name = "types-requests" -version = "2.31.0.20240125" +version = "2.31.0.20240218" description = "Typing stubs for requests" optional = false python-versions = ">=3.8" files = [ - {file = "types-requests-2.31.0.20240125.tar.gz", hash = "sha256:03a28ce1d7cd54199148e043b2079cdded22d6795d19a2c2a6791a4b2b5e2eb5"}, - {file = "types_requests-2.31.0.20240125-py3-none-any.whl", hash = "sha256:9592a9a4cb92d6d75d9b491a41477272b710e021011a2a3061157e2fb1f1a5d1"}, + {file = "types-requests-2.31.0.20240218.tar.gz", hash = "sha256:f1721dba8385958f504a5386240b92de4734e047a08a40751c1654d1ac3349c5"}, + {file = "types_requests-2.31.0.20240218-py3-none-any.whl", hash = "sha256:a82807ec6ddce8f00fe0e949da6d6bc1fbf1715420218a9640d695f70a9e5a9b"}, ] [package.dependencies] @@ -7641,13 +7641,13 @@ files = [ [[package]] name = "urllib3" -version = "2.2.0" +version = "2.2.1" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.0-py3-none-any.whl", hash = "sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224"}, - {file = "urllib3-2.2.0.tar.gz", hash = "sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20"}, + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, ] [package.extras] diff --git a/selfdrive/athena/athenad.py b/selfdrive/athena/athenad.py index 833bf841f5c9a3..cc3ab5e95b7e5a 100755 --- a/selfdrive/athena/athenad.py +++ b/selfdrive/athena/athenad.py @@ -206,13 +206,17 @@ def retry_upload(tid: int, end_event: threading.Event, increase_count: bool = Tr break -def cb(sm, item, tid, sz: int, cur: int) -> None: +def cb(sm, item, tid, end_event: threading.Event, sz: int, cur: int) -> None: # Abort transfer if connection changed to metered after starting upload + # or if athenad is shutting down to re-connect the websocket sm.update(0) metered = sm['deviceState'].networkMetered if metered and (not item.allow_cellular): raise AbortTransferException + if end_event.is_set(): + raise AbortTransferException + cur_upload_items[tid] = replace(item, progress=cur / sz if sz else 1) @@ -252,7 +256,7 @@ def upload_handler(end_event: threading.Event) -> None: sz = -1 cloudlog.event("athena.upload_handler.upload_start", fn=fn, sz=sz, network_type=network_type, metered=metered, retry_count=item.retry_count) - response = _do_upload(item, partial(cb, sm, item, tid)) + response = _do_upload(item, partial(cb, sm, item, tid, end_event)) if response.status_code not in (200, 201, 401, 403, 412): cloudlog.event("athena.upload_handler.retry", status_code=response.status_code, fn=fn, sz=sz, network_type=network_type, metered=metered) @@ -746,6 +750,9 @@ def ws_manage(ws: WebSocket, end_event: threading.Event) -> None: onroad_prev = onroad if sock is not None: + # While not sending data, onroad, we can expect to time out in 7 + (7 * 2) = 21s + # offroad, we can expect to time out in 30 + (10 * 3) = 60s + # FIXME: TCP_USER_TIMEOUT is effectively 2x for some reason (32s), so it's mostly unused sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, 16000 if onroad else 0) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 7 if onroad else 30) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 7 if onroad else 10) diff --git a/selfdrive/athena/tests/test_athenad.py b/selfdrive/athena/tests/test_athenad.py index 2fecab1b1b5761..d59058d7e29b34 100755 --- a/selfdrive/athena/tests/test_athenad.py +++ b/selfdrive/athena/tests/test_athenad.py @@ -233,6 +233,7 @@ def test_upload_handler(self, host): time.sleep(0.1) # TODO: verify that upload actually succeeded + # TODO: also check that end_event and metered network raises AbortTransferException self.assertEqual(athenad.upload_queue.qsize(), 0) @parameterized.expand([(500, True), (412, False)]) diff --git a/selfdrive/athena/tests/test_athenad_ping.py b/selfdrive/athena/tests/test_athenad_ping.py index 5231b0475f90ec..7f32f60cb444d6 100755 --- a/selfdrive/athena/tests/test_athenad_ping.py +++ b/selfdrive/athena/tests/test_athenad_ping.py @@ -12,6 +12,8 @@ from openpilot.selfdrive.manager.helpers import write_onroad_params from openpilot.system.hardware import TICI +TIMEOUT_TOLERANCE = 20 # seconds + def wifi_radio(on: bool) -> None: if not TICI: @@ -55,7 +57,7 @@ def tearDown(self) -> None: self.exit_event.set() self.athenad.join() - @mock.patch('openpilot.selfdrive.athena.athenad.create_connection', autospec=True) + @mock.patch('openpilot.selfdrive.athena.athenad.create_connection', new_callable=lambda: mock.MagicMock(wraps=athenad.create_connection)) def assertTimeout(self, reconnect_time: float, mock_create_connection: mock.MagicMock) -> None: self.athenad.start() @@ -63,7 +65,7 @@ def assertTimeout(self, reconnect_time: float, mock_create_connection: mock.Magi mock_create_connection.assert_called_once() mock_create_connection.reset_mock() - # check normal behaviour + # check normal behaviour, server pings on connection with self.subTest("Wi-Fi: receives ping"), Timeout(70, "no ping received"): while not self._received_ping(): time.sleep(0.1) @@ -92,12 +94,12 @@ def assertTimeout(self, reconnect_time: float, mock_create_connection: mock.Magi @unittest.skipIf(not TICI, "only run on desk") def test_offroad(self) -> None: write_onroad_params(False, self.params) - self.assertTimeout(100) # expect approx 90s + self.assertTimeout(60 + TIMEOUT_TOLERANCE) # based using TCP keepalive settings @unittest.skipIf(not TICI, "only run on desk") def test_onroad(self) -> None: write_onroad_params(True, self.params) - self.assertTimeout(30) # expect 20-30s + self.assertTimeout(21 + TIMEOUT_TOLERANCE) if __name__ == "__main__": diff --git a/selfdrive/boardd/boardd.cc b/selfdrive/boardd/boardd.cc index 9e2a9600557381..b2b59d3752fc2c 100644 --- a/selfdrive/boardd/boardd.cc +++ b/selfdrive/boardd/boardd.cc @@ -362,11 +362,11 @@ std::optional send_panda_states(PubMaster *pm, const std::vector ps.setHeartbeatLost((bool)(health.heartbeat_lost_pkt)); ps.setAlternativeExperience(health.alternative_experience_pkt); ps.setHarnessStatus(cereal::PandaState::HarnessStatus(health.car_harness_status_pkt)); - ps.setInterruptLoad(health.interrupt_load); + ps.setInterruptLoad(health.interrupt_load_pkt); ps.setFanPower(health.fan_power); ps.setFanStallCount(health.fan_stall_count); - ps.setSafetyRxChecksInvalid((bool)(health.safety_rx_checks_invalid)); - ps.setSpiChecksumErrorCount(health.spi_checksum_error_count); + ps.setSafetyRxChecksInvalid((bool)(health.safety_rx_checks_invalid_pkt)); + ps.setSpiChecksumErrorCount(health.spi_checksum_error_count_pkt); ps.setSbu1Voltage(health.sbu1_voltage_mV / 1000.0f); ps.setSbu2Voltage(health.sbu2_voltage_mV / 1000.0f); diff --git a/selfdrive/boardd/pandad.py b/selfdrive/boardd/pandad.py index 672678778ba901..a87613c8034b51 100755 --- a/selfdrive/boardd/pandad.py +++ b/selfdrive/boardd/pandad.py @@ -93,6 +93,11 @@ def main() -> NoReturn: cloudlog.event("pandad.flash_and_connect", count=count) params.remove("PandaSignatures") + # TODO: remove this in the next AGNOS + # wait until USB is up before counting + if time.monotonic() < 25.: + no_internal_panda_count = 0 + # Handle missing internal panda if no_internal_panda_count > 0: if no_internal_panda_count == 3: diff --git a/selfdrive/car/__init__.py b/selfdrive/car/__init__.py index c90ae50ab9b771..ab0a26ca0c00c7 100644 --- a/selfdrive/car/__init__.py +++ b/selfdrive/car/__init__.py @@ -1,11 +1,14 @@ # functions common among cars from collections import namedtuple -from typing import Dict, List, Optional +from dataclasses import dataclass +from enum import ReprEnum +from typing import Dict, List, Optional, Union import capnp from cereal import car from openpilot.common.numpy_fast import clip, interp +from openpilot.selfdrive.car.docs_definitions import CarInfo # kg of standard extra cargo to count for drive, gas, etc... @@ -73,7 +76,9 @@ def scale_tire_stiffness(mass, wheelbase, center_to_front, tire_stiffness_factor return tire_stiffness_front, tire_stiffness_rear -def dbc_dict(pt_dbc, radar_dbc, chassis_dbc=None, body_dbc=None) -> 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} @@ -236,3 +241,33 @@ def update(self, current_value, current_counter): self.previous_value = current_value return self.rate + + +CarInfos = Union[CarInfo, List[CarInfo]] + +@dataclass(order=True) +class PlatformConfig: + platform_str: str + car_info: CarInfos + dbc_dict: DbcDict + + def __hash__(self) -> int: + return hash(self.platform_str) + + +class Platforms(str, ReprEnum): + config: PlatformConfig + + def __new__(cls, platform_config: PlatformConfig): + member = str.__new__(cls, platform_config.platform_str) + member.config = platform_config + member._value_ = platform_config.platform_str + return member + + @classmethod + def create_dbc_map(cls) -> Dict[str, DbcDict]: + return {p.config.platform_str: 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} diff --git a/selfdrive/car/body/values.py b/selfdrive/car/body/values.py index 33119bf0fdfc37..441905f28b840c 100644 --- a/selfdrive/car/body/values.py +++ b/selfdrive/car/body/values.py @@ -1,8 +1,5 @@ -from enum import StrEnum -from typing import Dict - from cereal import car -from openpilot.selfdrive.car import dbc_dict +from openpilot.selfdrive.car import PlatformConfig, Platforms, dbc_dict from openpilot.selfdrive.car.docs_definitions import CarInfo from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries @@ -22,13 +19,12 @@ def __init__(self, CP): pass -class CAR(StrEnum): - BODY = "COMMA BODY" - - -CAR_INFO: Dict[str, CarInfo] = { - CAR.BODY: CarInfo("comma body", package="All"), -} +class CAR(Platforms): + BODY = PlatformConfig( + "COMMA BODY", + CarInfo("comma body", package="All"), + dbc_dict('comma_body', None), + ) FW_QUERY_CONFIG = FwQueryConfig( @@ -41,7 +37,5 @@ class CAR(StrEnum): ], ) - -DBC = { - CAR.BODY: dbc_dict('comma_body', None), -} +CAR_INFO = CAR.create_carinfo_map() +DBC = CAR.create_dbc_map() diff --git a/selfdrive/car/car_helpers.py b/selfdrive/car/car_helpers.py index 7654475358fcbd..95028e10c5d06c 100644 --- a/selfdrive/car/car_helpers.py +++ b/selfdrive/car/car_helpers.py @@ -162,7 +162,7 @@ def fingerprint(logcan, sendcan, num_pandas): cloudlog.warning("VIN %s", vin) params.put("CarVin", vin) - # disable OBD multiplexing for potential ECU knockouts + # disable OBD multiplexing for CAN fingerprinting and potential ECU knockouts set_obd_multiplexing(params, False) params.put_bool("FirmwareQueryDone", True) diff --git a/selfdrive/car/chrysler/fingerprints.py b/selfdrive/car/chrysler/fingerprints.py index 952a297e8f07c8..1df514e79b96ef 100644 --- a/selfdrive/car/chrysler/fingerprints.py +++ b/selfdrive/car/chrysler/fingerprints.py @@ -401,6 +401,7 @@ b'68527383AD', b'68527387AE', b'68527403AC', + b'68546047AF', b'68631938AA', b'68631942AA', ], diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py index c080e02299b1e0..979cc6d20a6e3d 100644 --- a/selfdrive/car/ford/values.py +++ b/selfdrive/car/ford/values.py @@ -111,6 +111,11 @@ def init_make(self, CP: car.CarParams): requests=[ # CAN and CAN FD queries are combined. # FIXME: For CAN FD, ECUs respond with frames larger than 8 bytes on the powertrain bus + Request( + [StdQueries.TESTER_PRESENT_REQUEST, StdQueries.MANUFACTURER_SOFTWARE_VERSION_REQUEST], + [StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.MANUFACTURER_SOFTWARE_VERSION_RESPONSE], + logging=True, + ), Request( [StdQueries.TESTER_PRESENT_REQUEST, StdQueries.MANUFACTURER_SOFTWARE_VERSION_REQUEST], [StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.MANUFACTURER_SOFTWARE_VERSION_RESPONSE], diff --git a/selfdrive/car/fw_query_definitions.py b/selfdrive/car/fw_query_definitions.py index ed886a69dbea33..36e6794d2c1a50 100755 --- a/selfdrive/car/fw_query_definitions.py +++ b/selfdrive/car/fw_query_definitions.py @@ -17,7 +17,7 @@ # 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 # TODO: move these to each brand's FW query config -STANDARD_VIN_ADDRS = [0x7e0, 0x7e2, 0x760, 0x7c4, 0x18da10f1, 0x18da0ef1] +STANDARD_VIN_ADDRS = [0x7e0, 0x7e2, 0x760, 0x7c6, 0x18da10f1, 0x18da0ef1] def p16(val): @@ -65,6 +65,9 @@ class StdQueries: GM_VIN_REQUEST = b'\x1a\x90' GM_VIN_RESPONSE = b'\x5a\x90' + KWP_VIN_REQUEST = b'\x21\x81' + KWP_VIN_RESPONSE = b'\x61\x81' + @dataclass class Request: diff --git a/selfdrive/car/honda/carstate.py b/selfdrive/car/honda/carstate.py index 988d54338ce991..ef2386c6ffb88a 100644 --- a/selfdrive/car/honda/carstate.py +++ b/selfdrive/car/honda/carstate.py @@ -7,8 +7,8 @@ from opendbc.can.parser import CANParser from openpilot.selfdrive.car.honda.hondacan import get_cruise_speed_conversion, get_pt_bus from openpilot.selfdrive.car.honda.values import CAR, DBC, STEER_THRESHOLD, HONDA_BOSCH, \ - HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_ALT_BRAKE_SIGNAL, \ - HONDA_BOSCH_RADARLESS, SERIAL_STEERING + HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_RADARLESS, \ + HondaFlags, SERIAL_STEERING from openpilot.selfdrive.car.interfaces import CarStateBase TransmissionType = car.CarParams.TransmissionType @@ -44,7 +44,7 @@ def get_can_messages(CP, gearbox_msg): else: messages.append((gearbox_msg, 100)) - if CP.carFingerprint in HONDA_BOSCH_ALT_BRAKE_SIGNAL: + if CP.flags & HondaFlags.BOSCH_ALT_BRAKE: messages.append(("BRAKE_MODULE", 50)) if CP.carFingerprint in (HONDA_BOSCH | {CAR.CIVIC, CAR.ODYSSEY, CAR.ODYSSEY_CHN, CAR.CLARITY}): @@ -221,7 +221,7 @@ def update(self, cp, cp_cam, cp_body): else: ret.cruiseState.speed = cp.vl["CRUISE"]["CRUISE_SPEED_PCM"] * CV.KPH_TO_MS - if self.CP.carFingerprint in HONDA_BOSCH_ALT_BRAKE_SIGNAL: + if self.CP.flags & HondaFlags.BOSCH_ALT_BRAKE: ret.brakePressed = cp.vl["BRAKE_MODULE"]["BRAKE_PRESSED"] != 0 else: # brake switch has shown some single time step noise, so only considered when diff --git a/selfdrive/car/honda/fingerprints.py b/selfdrive/car/honda/fingerprints.py index 172557f7a76676..1922c44c4b8a1a 100644 --- a/selfdrive/car/honda/fingerprints.py +++ b/selfdrive/car/honda/fingerprints.py @@ -101,10 +101,6 @@ b'39990-TVA-X040\x00\x00', b'39990-TVE-H130\x00\x00', ], - (Ecu.unknown, 0x18da3af1, None): [ - b'39390-TVA-A020\x00\x00', - b'39390-TVA-A120\x00\x00', - ], (Ecu.srs, 0x18da53f1, None): [ b'77959-TBX-H230\x00\x00', b'77959-TVA-A460\x00\x00', @@ -313,6 +309,7 @@ (Ecu.srs, 0x18da53f1, None): [ b'77959-TBA-A030\x00\x00', b'77959-TBA-A040\x00\x00', + b'77959-TBG-A020\x00\x00', b'77959-TBG-A030\x00\x00', b'77959-TEA-Q820\x00\x00', ], diff --git a/selfdrive/car/honda/interface.py b/selfdrive/car/honda/interface.py index ee9eec9765f6d7..ecac9d92448aad 100755 --- a/selfdrive/car/honda/interface.py +++ b/selfdrive/car/honda/interface.py @@ -3,8 +3,9 @@ from panda import Panda from openpilot.common.conversions import Conversions as CV from openpilot.common.numpy_fast import interp +from openpilot.selfdrive.car.honda.hondacan import get_pt_bus from openpilot.selfdrive.car.honda.values import CarControllerParams, CruiseButtons, HondaFlags, CAR, HONDA_BOSCH, HONDA_NIDEC_ALT_SCM_MESSAGES, \ - HONDA_BOSCH_ALT_BRAKE_SIGNAL, HONDA_BOSCH_RADARLESS + HONDA_BOSCH_RADARLESS from openpilot.selfdrive.car import create_button_events, get_safety_config from openpilot.selfdrive.car.interfaces import CarInterfaceBase from openpilot.selfdrive.car.disable_ecu import disable_ecu @@ -310,7 +311,8 @@ def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs): raise ValueError(f"unsupported car {candidate}") # These cars use alternate user brake msg (0x1BE) - if candidate in HONDA_BOSCH_ALT_BRAKE_SIGNAL: + if 0x1BE in fingerprint[get_pt_bus(candidate)] and candidate in HONDA_BOSCH: + ret.flags |= HondaFlags.BOSCH_ALT_BRAKE.value ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HONDA_ALT_BRAKE # These cars use alternate SCM messages (SCM_FEEDBACK AND SCM_BUTTON) diff --git a/selfdrive/car/honda/values.py b/selfdrive/car/honda/values.py index e539f7422800e1..3a7c706cd09fca 100644 --- a/selfdrive/car/honda/values.py +++ b/selfdrive/car/honda/values.py @@ -49,6 +49,7 @@ def __init__(self, CP): class HondaFlags(IntFlag): # Bosch models with alternate set of LKAS_HUD messages BOSCH_EXT_HUD = 1 + BOSCH_ALT_BRAKE = 2 # Car button codes @@ -267,7 +268,6 @@ def init_make(self, CP: car.CarParams): CAR.PILOT, CAR.RIDGELINE, CAR.ACCORD_NIDEC_4CYL} HONDA_BOSCH = {CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_5G, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022, CAR.HRV_3G} -HONDA_BOSCH_ALT_BRAKE_SIGNAL = {CAR.ACCORD, CAR.CRV_5G, CAR.ACURA_RDX_3G, CAR.HRV_3G} HONDA_BOSCH_RADARLESS = {CAR.CIVIC_2022, CAR.HRV_3G} SERIAL_STEERING = {CAR.ACCORD_NIDEC_4CYL, } diff --git a/selfdrive/car/hyundai/fingerprints.py b/selfdrive/car/hyundai/fingerprints.py index 0671dbe1819b47..11f7919b69df58 100644 --- a/selfdrive/car/hyundai/fingerprints.py +++ b/selfdrive/car/hyundai/fingerprints.py @@ -1549,6 +1549,7 @@ b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.06 99211-GI000 210813', b'\xf1\x00NE1 MFC AT EUR RHD 1.00 1.01 99211-GI010 211007', b'\xf1\x00NE1 MFC AT EUR RHD 1.00 1.02 99211-GI010 211206', + b'\xf1\x00NE1 MFC AT KOR LHD 1.00 1.00 99211-GI020 230719', b'\xf1\x00NE1 MFC AT KOR LHD 1.00 1.05 99211-GI010 220614', b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.00 99211-GI020 230719', b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.01 99211-GI010 211007', @@ -1556,7 +1557,6 @@ b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.03 99211-GI010 220401', b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.05 99211-GI010 220614', b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.06 99211-GI010 230110', - b'\xf1\x00NE1 MFC AT KOR LHD 1.00 1.00 99211-GI020 230719', ], }, CAR.IONIQ_6: { diff --git a/selfdrive/car/nissan/fingerprints.py b/selfdrive/car/nissan/fingerprints.py index 6cd0df4c9b761f..19267ded4633db 100644 --- a/selfdrive/car/nissan/fingerprints.py +++ b/selfdrive/car/nissan/fingerprints.py @@ -46,6 +46,7 @@ CAR.LEAF: { (Ecu.abs, 0x740, None): [ b'476605SA1C', + b'476605SA7D', b'476605SC2D', b'476606WK7B', b'476606WK9B', @@ -65,6 +66,7 @@ ], (Ecu.gateway, 0x18dad0f1, None): [ b'284U25SA3C', + b'284U25SP0C', b'284U25SP1C', b'284U26WK0A', b'284U26WK0C', diff --git a/selfdrive/car/subaru/fingerprints.py b/selfdrive/car/subaru/fingerprints.py index ad8ebe87cdebd5..90fa6093d97922 100644 --- a/selfdrive/car/subaru/fingerprints.py +++ b/selfdrive/car/subaru/fingerprints.py @@ -509,17 +509,20 @@ (Ecu.fwdCamera, 0x787, None): [ b'\x04!\x01\x1eD\x07!\x00\x04,', b'\x04!\x08\x01.\x07!\x08\x022', + b'\r!\x08\x017\n!\x08\x003', ], (Ecu.engine, 0x7e0, None): [ b'\xd5"`0\x07', b'\xd5"a0\x07', b'\xf1"`q\x07', b'\xf1"aq\x07', + b'\xfa"ap\x07', ], (Ecu.transmission, 0x7e1, None): [ b'\x1d\x86B0\x00', b'\x1d\xf6B0\x00', b'\x1e\x86B0\x00', + b'\x1e\x86F0\x00', b'\x1e\xf6D0\x00', ], }, diff --git a/selfdrive/car/subaru/values.py b/selfdrive/car/subaru/values.py index a2bd81ac08e7bd..1c6c4f9dd93e5b 100644 --- a/selfdrive/car/subaru/values.py +++ b/selfdrive/car/subaru/values.py @@ -135,7 +135,7 @@ def init_make(self, CP: car.CarParams): 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-23", "All", car_parts=CarParts.common([CarHarness.subaru_c])), + 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])), } diff --git a/selfdrive/car/tests/routes.py b/selfdrive/car/tests/routes.py index b874e31b6aec04..f7ae4e038e7df5 100755 --- a/selfdrive/car/tests/routes.py +++ b/selfdrive/car/tests/routes.py @@ -187,6 +187,7 @@ class CarTestRoute(NamedTuple): CarTestRoute("5f5afb36036506e4|2019-05-14--02-09-54", TOYOTA.COROLLA_TSS2), CarTestRoute("5ceff72287a5c86c|2019-10-19--10-59-02", TOYOTA.COROLLA_TSS2), # hybrid CarTestRoute("d2525c22173da58b|2021-04-25--16-47-04", TOYOTA.PRIUS), + CarTestRoute("b14c5b4742e6fc85|2020-07-28--19-50-11", TOYOTA.RAV4), CarTestRoute("32a7df20486b0f70|2020-02-06--16-06-50", TOYOTA.RAV4H), CarTestRoute("cdf2f7de565d40ae|2019-04-25--03-53-41", TOYOTA.RAV4_TSS2), CarTestRoute("a5c341bb250ca2f0|2022-05-18--16-05-17", TOYOTA.RAV4_TSS2_2022), @@ -223,7 +224,7 @@ class CarTestRoute(NamedTuple): CarTestRoute("ea8fbe72b96a185c|2023-02-08--15-11-46", TOYOTA.CHR_TSS2), CarTestRoute("ea8fbe72b96a185c|2023-02-22--09-20-34", TOYOTA.CHR_TSS2), # openpilot longitudinal, with smartDSU CarTestRoute("6719965b0e1d1737|2023-02-09--22-44-05", TOYOTA.CHR_TSS2), # hybrid - # CarTestRoute("6719965b0e1d1737|2023-08-29--06-40-05", TOYOTA.CHR_TSS2), # hybrid, openpilot longitudinal, radar disabled + CarTestRoute("6719965b0e1d1737|2023-08-29--06-40-05", TOYOTA.CHR_TSS2), # hybrid, openpilot longitudinal, radar disabled CarTestRoute("14623aae37e549f3|2021-10-24--01-20-49", TOYOTA.PRIUS_V), CarTestRoute("202c40641158a6e5|2021-09-21--09-43-24", VOLKSWAGEN.ARTEON_MK1), @@ -291,7 +292,6 @@ class CarTestRoute(NamedTuple): # Segments that test specific issues # Controls mismatch due to interceptor threshold CarTestRoute("cfb32f0fb91b173b|2022-04-06--14-54-45", HONDA.CIVIC, segment=21), - CarTestRoute("5a8762b91fc70467|2022-04-14--21-26-20", TOYOTA.RAV4, segment=2), # Controls mismatch due to standstill threshold CarTestRoute("bec2dcfde6a64235|2022-04-08--14-21-32", HONDA.CRV_HYBRID, segment=22), ] diff --git a/selfdrive/car/tests/test_car_interfaces.py b/selfdrive/car/tests/test_car_interfaces.py index f2625abe572a20..a454f616cbf38f 100755 --- a/selfdrive/car/tests/test_car_interfaces.py +++ b/selfdrive/car/tests/test_car_interfaces.py @@ -74,6 +74,10 @@ def test_car_interfaces(self, car_name, data): self.assertEqual(len(car_params.longitudinalTuning.kiV), len(car_params.longitudinalTuning.kiBP)) self.assertEqual(len(car_params.longitudinalTuning.deadzoneV), len(car_params.longitudinalTuning.deadzoneBP)) + # If we're using the interceptor for gasPressed, we should be commanding gas with it + if car_params.enableGasInterceptor: + self.assertTrue(car_params.openpilotLongitudinalControl) + # Lateral sanity checks if car_params.steerControlType != car.CarParams.SteerControlType.angle: tune = car_params.lateralTuning diff --git a/selfdrive/car/tests/test_fw_fingerprint.py b/selfdrive/car/tests/test_fw_fingerprint.py index fa006e51a66858..e1ebd5cf183568 100755 --- a/selfdrive/car/tests/test_fw_fingerprint.py +++ b/selfdrive/car/tests/test_fw_fingerprint.py @@ -33,18 +33,29 @@ def assertFingerprints(self, candidates, expected): self.assertEqual(len(candidates), 1, f"got more than one candidate: {candidates}") self.assertEqual(candidates[0], expected) - @parameterized.expand([(b, c, e[c]) for b, e in VERSIONS.items() for c in e]) - def test_exact_match(self, brand, car_model, ecus): + @parameterized.expand([(b, c, e[c], n) for b, e in VERSIONS.items() for c in e for n in (True, False)]) + def test_exact_match(self, brand, car_model, ecus, test_non_essential): + config = FW_QUERY_CONFIGS[brand] CP = car.CarParams.new_message() - for _ in range(200): + for _ in range(100): fw = [] for ecu, fw_versions in ecus.items(): + # Assume non-essential ECUs apply to all cars, so we catch cases where Car A with + # missing ECUs won't match to Car B where only Car B has labeled non-essential ECUs + if ecu[0] in config.non_essential_ecus and test_non_essential: + continue + ecu_name, addr, sub_addr = ecu fw.append({"ecu": ecu_name, "fwVersion": random.choice(fw_versions), 'brand': brand, "address": addr, "subAddress": 0 if sub_addr is None else sub_addr}) CP.carFw = fw _, matches = match_fw_to_car(CP.carFw, allow_fuzzy=False) - self.assertFingerprints(matches, car_model) + if not test_non_essential: + self.assertFingerprints(matches, car_model) + else: + # if we're removing ECUs we expect some match loss, but it shouldn't mismatch + if len(matches) != 0: + self.assertFingerprints(matches, car_model) @parameterized.expand([(b, c, e[c]) for b, e in VERSIONS.items() for c in e]) def test_custom_fuzzy_match(self, brand, car_model, ecus): @@ -225,7 +236,7 @@ def _assert_timing(self, avg_time, ref_time): def test_startup_timing(self): # Tests worse-case VIN query time and typical present ECU query time - vin_ref_times = {'worst': 1.0, 'best': 0.5} # best assumes we go through all queries to get a match + vin_ref_times = {'worst': 1.2, 'best': 0.6} # best assumes we go through all queries to get a match present_ecu_ref_time = 0.75 def fake_get_ecu_addrs(*_, timeout): @@ -252,13 +263,13 @@ def fake_get_ecu_addrs(*_, timeout): print(f'get_vin {name} case, query time={self.total_time / self.N} seconds') def test_fw_query_timing(self): - total_ref_time = {1: 5.95, 2: 6.85} + total_ref_time = {1: 6.05, 2: 6.95} brand_ref_times = { 1: { 'gm': 0.5, 'body': 0.1, 'chrysler': 0.3, - 'ford': 0.1, + 'ford': 0.2, 'honda': 0.55, 'hyundai': 1.05, 'mazda': 0.1, @@ -269,7 +280,7 @@ def test_fw_query_timing(self): 'volkswagen': 0.2, }, 2: { - 'ford': 0.2, + 'ford': 0.3, 'hyundai': 1.85, } } diff --git a/selfdrive/car/toyota/interface.py b/selfdrive/car/toyota/interface.py index 7add9baaa7ba77..2df852e8ccf729 100644 --- a/selfdrive/car/toyota/interface.py +++ b/selfdrive/car/toyota/interface.py @@ -226,20 +226,16 @@ def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs): found_ecus = [fw.ecu for fw in car_fw] ret.enableDsu = len(found_ecus) > 0 and Ecu.dsu not in found_ecus and candidate not in (NO_DSU_CAR | UNSUPPORTED_DSU_CAR) \ and not (ret.flags & ToyotaFlags.SMART_DSU) - ret.enableGasInterceptor = 0x201 in fingerprint[0] - - if ret.enableGasInterceptor: - ret.safetyConfigs[0].safetyParam |= Panda.FLAG_TOYOTA_GAS_INTERCEPTOR # if the smartDSU is detected, openpilot can send ACC_CONTROL and the smartDSU will block it from the DSU or radar. # since we don't yet parse radar on TSS2/TSS-P radar-based ACC cars, gate longitudinal behind experimental toggle use_sdsu = bool(ret.flags & ToyotaFlags.SMART_DSU) if candidate in (RADAR_ACC_CAR | NO_DSU_CAR): - ret.experimentalLongitudinalAvailable = use_sdsu + ret.experimentalLongitudinalAvailable = use_sdsu or candidate in RADAR_ACC_CAR if not use_sdsu: # Disabling radar is only supported on TSS2 radar-ACC cars - if experimental_long and candidate in RADAR_ACC_CAR and False: # TODO: disabling radar isn't supported yet + if experimental_long and candidate in RADAR_ACC_CAR: ret.flags |= ToyotaFlags.DISABLE_RADAR.value else: use_sdsu = use_sdsu and experimental_long @@ -254,10 +250,14 @@ def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs): # - TSS-P DSU-less cars w/ CAN filter installed (no radar parser yet) ret.openpilotLongitudinalControl = use_sdsu or ret.enableDsu or candidate in (TSS2_CAR - RADAR_ACC_CAR) or bool(ret.flags & ToyotaFlags.DISABLE_RADAR.value) ret.autoResumeSng = ret.openpilotLongitudinalControl and candidate in NO_STOP_TIMER_CAR + ret.enableGasInterceptor = 0x201 in fingerprint[0] and ret.openpilotLongitudinalControl if not ret.openpilotLongitudinalControl: ret.safetyConfigs[0].safetyParam |= Panda.FLAG_TOYOTA_STOCK_LONGITUDINAL + if ret.enableGasInterceptor: + ret.safetyConfigs[0].safetyParam |= Panda.FLAG_TOYOTA_GAS_INTERCEPTOR + # min speed to enable ACC. if car can do stop and go, then set enabling speed # to a negative value, so it won't matter. ret.minEnableSpeed = -1. if (stop_and_go or ret.enableGasInterceptor) else MIN_ACC_SPEED diff --git a/selfdrive/car/vin.py b/selfdrive/car/vin.py index 184455af17922f..e668c35f7d5086 100755 --- a/selfdrive/car/vin.py +++ b/selfdrive/car/vin.py @@ -22,6 +22,7 @@ def get_vin(logcan, sendcan, buses, timeout=0.1, retry=2, debug=False): (StdQueries.UDS_VIN_REQUEST, StdQueries.UDS_VIN_RESPONSE, (0, 1), STANDARD_VIN_ADDRS, FUNCTIONAL_ADDRS, 0x8), (StdQueries.OBD_VIN_REQUEST, StdQueries.OBD_VIN_RESPONSE, (0, 1), STANDARD_VIN_ADDRS, FUNCTIONAL_ADDRS, 0x8), (StdQueries.GM_VIN_REQUEST, StdQueries.GM_VIN_RESPONSE, (0,), [0x24b], None, 0x400), # Bolt fwdCamera + (StdQueries.KWP_VIN_REQUEST, StdQueries.KWP_VIN_RESPONSE, (0,), [0x797], None, 0x3), # Nissan Leaf VCM ): if bus not in valid_buses: continue @@ -40,8 +41,8 @@ def get_vin(logcan, sendcan, buses, timeout=0.1, retry=2, debug=False): for addr in vin_addrs: vin = results.get((addr, None)) if vin is not None: - # Ford pads with null bytes - if len(vin) == 24: + # Ford and Nissan pads with null bytes + if len(vin) in (19, 24): vin = re.sub(b'\x00*$', b'', vin) # Honda Bosch response starts with a length, trim to correct length diff --git a/selfdrive/car/volkswagen/fingerprints.py b/selfdrive/car/volkswagen/fingerprints.py index 8e5a0667bd91ef..eab2bc0090140c 100644 --- a/selfdrive/car/volkswagen/fingerprints.py +++ b/selfdrive/car/volkswagen/fingerprints.py @@ -1043,6 +1043,7 @@ ], (Ecu.srs, 0x715, None): [ b'\xf1\x873Q0959655AC\xf1\x890200\xf1\x82\r11120011100010022212110200', + b'\xf1\x873Q0959655AK\xf1\x890306\xf1\x82\r31210031210021033733310331', b'\xf1\x873Q0959655AP\xf1\x890305\xf1\x82\r11110011110011213331312131', b'\xf1\x873Q0959655AQ\xf1\x890200\xf1\x82\r11120011100010312212113100', b'\xf1\x873Q0959655AS\xf1\x890200\xf1\x82\r11120011100010022212110200', @@ -1062,6 +1063,7 @@ (Ecu.fwdRadar, 0x757, None): [ b'\xf1\x875Q0907572D \xf1\x890304\xf1\x82\x0101', b'\xf1\x875Q0907572F \xf1\x890400\xf1\x82\x0101', + b'\xf1\x875Q0907572H \xf1\x890620', b'\xf1\x875Q0907572J \xf1\x890654', b'\xf1\x875Q0907572K \xf1\x890402\xf1\x82\x0101', b'\xf1\x875Q0907572P \xf1\x890682', diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 70dc8472128cf5..c6b9ef038e8f3a 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -5,28 +5,34 @@ import threading from typing import SupportsFloat -from cereal import car, log -from openpilot.common.numpy_fast import clip -from openpilot.common.realtime import config_realtime_process, Priority, Ratekeeper, DT_CTRL -from openpilot.common.params import Params import cereal.messaging as messaging + +from cereal import car, log from cereal.visionipc import VisionIpcClient, VisionStreamType -from openpilot.common.conversions import Conversions as CV + from panda import ALTERNATIVE_EXPERIENCE + +from openpilot.common.conversions import Conversions as CV +from openpilot.common.numpy_fast import clip +from openpilot.common.params import Params +from openpilot.common.realtime import config_realtime_process, Priority, Ratekeeper, DT_CTRL from openpilot.common.swaglog import cloudlog -from openpilot.system.version import get_short_branch + from openpilot.selfdrive.boardd.boardd import can_list_to_can_capnp from openpilot.selfdrive.car.car_helpers import get_car, get_startup_event, get_one_can +from openpilot.selfdrive.car.interfaces import CarInterfaceBase +from openpilot.selfdrive.controls.lib.alertmanager import AlertManager, set_offroad_alert from openpilot.selfdrive.controls.lib.drive_helpers import VCruiseHelper, clip_curvature +from openpilot.selfdrive.controls.lib.events import Events, ET from openpilot.selfdrive.controls.lib.latcontrol import LatControl, MIN_LATERAL_CONTROL_SPEED -from openpilot.selfdrive.controls.lib.longcontrol import LongControl from openpilot.selfdrive.controls.lib.latcontrol_pid import LatControlPID from openpilot.selfdrive.controls.lib.latcontrol_angle import LatControlAngle, STEER_ANGLE_SATURATION_THRESHOLD from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque -from openpilot.selfdrive.controls.lib.events import Events, ET -from openpilot.selfdrive.controls.lib.alertmanager import AlertManager, set_offroad_alert +from openpilot.selfdrive.controls.lib.longcontrol import LongControl from openpilot.selfdrive.controls.lib.vehicle_model import VehicleModel + from openpilot.system.hardware import HARDWARE +from openpilot.system.version import get_short_branch SOFT_DISABLE_TIME = 3 # seconds LDW_MIN_SPEED = 31 * CV.MPH_TO_MS @@ -55,33 +61,19 @@ ENABLED_STATES = (State.preEnabled, *ACTIVE_STATES) -class Controls: - def __init__(self, CI=None): - config_realtime_process(4, Priority.CTRL_HIGH) - - # Ensure the current branch is cached, otherwise the first iteration of controlsd lags - self.branch = get_short_branch() - - # Setup sockets - self.pm = messaging.PubMaster(['sendcan', 'controlsState', 'carState', - 'carControl', 'onroadEvents', 'carParams']) - - self.sensor_packets = ["accelerometer", "gyroscope"] - self.camera_packets = ["roadCameraState", "driverCameraState", "wideRoadCameraState"] +class CarD: + CI: CarInterfaceBase + CS: car.CarState - self.log_sock = messaging.sub_sock('androidLog') + def __init__(self, CI=None): self.can_sock = messaging.sub_sock('can', timeout=20) + self.sm = messaging.SubMaster(['pandaStates']) + self.pm = messaging.PubMaster(['sendcan', 'carState', 'carParams']) + + self.can_rcv_timeout_counter = 0 # conseuctive timeout count + self.can_rcv_cum_timeout_counter = 0 # cumulative timeout count self.params = Params() - ignore = self.sensor_packets + ['testJoystick'] - if SIMULATION: - ignore += ['driverCameraState', 'managerState'] - self.sm = messaging.SubMaster(['deviceState', 'pandaStates', 'peripheralState', 'modelV2', 'liveCalibration', - 'driverMonitoringState', 'longitudinalPlan', 'liveLocationKalman', - 'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters', - 'testJoystick', 'longitudinalPlanSP', 'modelV2SP'] + self.camera_packets + self.sensor_packets, - ignore_alive=ignore, ignore_avg_freq=ignore+['radarState', 'testJoystick'], ignore_valid=['testJoystick', ], - frequency=int(1/DT_CTRL)) if CI is None: # wait for one pandaState and one CAN packet @@ -94,25 +86,17 @@ def __init__(self, CI=None): else: self.CI, self.CP = CI, CI.CP - self.joystick_mode = self.params.get_bool("JoystickDebugMode") - # set alternative experiences from parameters - self.disengage_on_accelerator = self.params.get_bool("DisengageOnAccelerator") + disengage_on_accelerator = self.params.get_bool("DisengageOnAccelerator") self.CP.alternativeExperience = 0 - if not self.disengage_on_accelerator: + if not disengage_on_accelerator: self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS - # read params - self.is_metric = self.params.get_bool("IsMetric") - self.is_ldw_enabled = self.params.get_bool("IsLdwEnabled") - openpilot_enabled_toggle = self.params.get_bool("OpenpilotEnabledToggle") - - # detect sound card presence and ensure successful init - sounds_available = HARDWARE.get_sound_card_online() - car_recognized = self.CP.carName != 'mock' + openpilot_enabled_toggle = self.params.get_bool("OpenpilotEnabledToggle") controller_available = self.CI.CC is not None and openpilot_enabled_toggle and not self.CP.dashcamOnly + self.CP.passive = not car_recognized or not controller_available or self.CP.dashcamOnly if self.CP.passive: safety_config = car.CarParams.SafetyConfig.new_message() @@ -130,6 +114,113 @@ def __init__(self, CI=None): self.params.put_nonblocking("CarParamsCache", cp_bytes) self.params.put_nonblocking("CarParamsPersistent", cp_bytes) + def initialize(self): + """Initialize CarInterface, once controls are ready""" + self.CI.init(self.CP, self.can_sock, self.pm.sock['sendcan']) + + def state_update(self, CC: car.CarControl): + """carState update loop, driven by can""" + + # TODO: This should not depend on carControl + + # Update carState from CAN + can_strs = messaging.drain_sock_raw(self.can_sock, wait_for_one=True) + self.CS = self.CI.update(CC, can_strs) + + self.sm.update(0) + + can_rcv_valid = len(can_strs) > 0 + + # Check for CAN timeout + if not can_rcv_valid: + self.can_rcv_timeout_counter += 1 + self.can_rcv_cum_timeout_counter += 1 + else: + self.can_rcv_timeout_counter = 0 + + self.can_rcv_timeout = self.can_rcv_timeout_counter >= 5 + + if can_rcv_valid and REPLAY: + self.can_log_mono_time = messaging.log_from_bytes(can_strs[0]).logMonoTime + + return self.CS + + def state_publish(self, car_events): + """carState and carParams publish loop""" + + # TODO: carState should be independent of the event loop + + # carState + cs_send = messaging.new_message('carState') + cs_send.valid = self.CS.canValid + cs_send.carState = self.CS + cs_send.carState.events = car_events + self.pm.send('carState', cs_send) + + # carParams - logged every 50 seconds (> 1 per segment) + if (self.sm.frame % int(50. / DT_CTRL) == 0): + cp_send = messaging.new_message('carParams') + cp_send.valid = True + cp_send.carParams = self.CP + self.pm.send('carParams', cp_send) + + def controls_update(self, CC: car.CarControl): + """control update loop, driven by carControl""" + + # send car controls over can + now_nanos = self.can_log_mono_time if REPLAY else int(time.monotonic() * 1e9) + actuators_output, can_sends = self.CI.apply(CC, now_nanos) + self.pm.send('sendcan', can_list_to_can_capnp(can_sends, msgtype='sendcan', valid=self.CS.canValid)) + + return actuators_output + + +class Controls: + def __init__(self, CI=None): + self.card = CarD(CI) + + self.CP = self.card.CP + self.CI = self.card.CI + + config_realtime_process(4, Priority.CTRL_HIGH) + + # Ensure the current branch is cached, otherwise the first iteration of controlsd lags + self.branch = get_short_branch() + + # Setup sockets + self.pm = messaging.PubMaster(['controlsState', 'carControl', 'onroadEvents']) + + self.sensor_packets = ["accelerometer", "gyroscope"] + self.camera_packets = ["roadCameraState", "driverCameraState", "wideRoadCameraState"] + + self.log_sock = messaging.sub_sock('androidLog') + + self.params = Params() + ignore = self.sensor_packets + ['testJoystick'] + if SIMULATION: + ignore += ['driverCameraState', 'managerState'] + self.sm = messaging.SubMaster(['deviceState', 'pandaStates', 'peripheralState', 'modelV2', 'liveCalibration', + 'driverMonitoringState', 'longitudinalPlan', 'liveLocationKalman', + 'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters', + 'testJoystick', 'longitudinalPlanSP', 'modelV2SP'] + self.camera_packets + self.sensor_packets, + ignore_alive=ignore, ignore_avg_freq=ignore+['radarState', 'testJoystick'], ignore_valid=['testJoystick', ], + frequency=int(1/DT_CTRL)) + + self.joystick_mode = self.params.get_bool("JoystickDebugMode") + + # read params + self.disengage_on_accelerator = self.params.get_bool("DisengageOnAccelerator") + self.is_metric = self.params.get_bool("IsMetric") + self.is_ldw_enabled = self.params.get_bool("IsLdwEnabled") + openpilot_enabled_toggle = self.params.get_bool("OpenpilotEnabledToggle") + + # detect sound card presence and ensure successful init + sounds_available = HARDWARE.get_sound_card_online() + + car_recognized = self.CP.carName != 'mock' + + controller_available = self.CI.CC is not None and openpilot_enabled_toggle and not self.CP.dashcamOnly + # cleanup old params if not self.CP.experimentalLongitudinalAvailable: self.params.remove("ExperimentalLongitudinalEnabled") @@ -159,8 +250,6 @@ def __init__(self, CI=None): self.soft_disable_timer = 0 self.mismatch_counter = 0 self.cruise_mismatch_counter = 0 - self.can_rcv_timeout_counter = 0 # conseuctive timeout count - self.can_rcv_cum_timeout_counter = 0 # cumulative timeout count self.last_blinker_frame = 0 self.last_steering_pressed_frame = 0 self.distance_traveled = 0 @@ -319,7 +408,8 @@ def update_events(self, CS): else: safety_mismatch = pandaState.safetyModel not in IGNORED_SAFETY_MODES - if safety_mismatch or pandaState.safetyRxChecksInvalid or self.mismatch_counter >= 200: + # safety mismatch allows some time for boardd to set the safety mode and publish it back from panda + if (safety_mismatch and self.sm.frame*DT_CTRL > 10.) or pandaState.safetyRxChecksInvalid or self.mismatch_counter >= 200: self.events.add(EventName.controlsMismatch) if log.PandaState.FaultType.relayMalfunction in pandaState.faults: @@ -354,10 +444,9 @@ def update_events(self, CS): self.events.add(EventName.canError) # generic catch-all. ideally, a more specific event should be added above instead - can_rcv_timeout = self.can_rcv_timeout_counter >= 5 has_disable_events = self.events.contains(ET.NO_ENTRY) and (self.events.contains(ET.SOFT_DISABLE) or self.events.contains(ET.IMMEDIATE_DISABLE)) no_system_errors = (not has_disable_events) or (len(self.events) == num_events) - if (not self.sm.all_checks() or can_rcv_timeout) and no_system_errors: + if (not self.sm.all_checks() or self.card.can_rcv_timeout) and no_system_errors: if not self.sm.all_alive(): self.events.add(EventName.commIssue) elif not self.sm.all_freq_ok(): @@ -369,7 +458,7 @@ def update_events(self, CS): 'invalid': [s for s, valid in self.sm.valid.items() if not valid], 'not_alive': [s for s, alive in self.sm.alive.items() if not alive], 'not_freq_ok': [s for s, freq_ok in self.sm.freq_ok.items() if not freq_ok], - 'can_rcv_timeout': can_rcv_timeout, + 'can_rcv_timeout': self.card.can_rcv_timeout, } if logs != self.logged_comm_issue: cloudlog.event("commIssue", error=True, **logs) @@ -418,9 +507,12 @@ def update_events(self, CS): # TODO: fix simulator if not SIMULATION or REPLAY: + # Not show in first 1 km to allow for driving out of garage. This event shows after 5 minutes if not self.sm['liveLocationKalman'].gpsOK and self.sm['liveLocationKalman'].inputsOK and (self.distance_traveled > 1000): - # Not show in first 1 km to allow for driving out of garage. This event shows after 5 minutes self.events.add(EventName.noGps) + if self.sm['liveLocationKalman'].gpsOK: + self.distance_traveled = 0 + self.distance_traveled += CS.vEgo * DT_CTRL if self.sm['modelV2'].frameDropPerc > 20: self.events.add(EventName.modeldLagging) @@ -428,17 +520,13 @@ def update_events(self, CS): def data_sample(self): """Receive data from sockets and update carState""" - # Update carState from CAN - can_strs = messaging.drain_sock_raw(self.can_sock, wait_for_one=True) - CS = self.CI.update(self.CC, can_strs) - if len(can_strs) and REPLAY: - self.can_log_mono_time = messaging.log_from_bytes(can_strs[0]).logMonoTime + CS = self.card.state_update(self.CC) self.sm.update(0) if not self.initialized: all_valid = CS.canValid and self.sm.all_checks() - timed_out = self.sm.frame * DT_CTRL > (6. if REPLAY else 3.5) + timed_out = self.sm.frame * DT_CTRL > 6. if all_valid or timed_out or (SIMULATION and not REPLAY): available_streams = VisionIpcClient.available_streams("camerad", block=False) if VisionStreamType.VISION_STREAM_ROAD not in available_streams: @@ -447,7 +535,7 @@ def data_sample(self): self.sm.ignore_alive.append('wideRoadCameraState') if not self.CP.passive: - self.CI.init(self.CP, self.can_sock, self.pm.sock['sendcan']) + self.card.initialize() self.initialized = True self.set_initial_state() @@ -464,13 +552,6 @@ def data_sample(self): error=True, ) - # Check for CAN timeout - if not can_strs: - self.can_rcv_timeout_counter += 1 - self.can_rcv_cum_timeout_counter += 1 - else: - self.can_rcv_timeout_counter = 0 - # When the panda and controlsd do not agree on controls_allowed # we want to disengage openpilot. However the status from the panda goes through # another socket other than the CAN messages and one can arrive earlier than the other. @@ -483,8 +564,6 @@ def data_sample(self): if ps.safetyModel not in IGNORED_SAFETY_MODES): self.mismatch_counter += 1 - self.distance_traveled += CS.vEgo * DT_CTRL - return CS def state_transition(self, CS): @@ -761,10 +840,7 @@ def publish_logs(self, CS, start_time, CC, lac_log): hudControl.visualAlert = current_alert.visual_alert if not self.CP.passive and self.initialized: - # send car controls over can - now_nanos = self.can_log_mono_time if REPLAY else int(time.monotonic() * 1e9) - self.last_actuators, can_sends = self.CI.apply(CC, now_nanos) - self.pm.send('sendcan', can_list_to_can_capnp(can_sends, msgtype='sendcan', valid=CS.canValid)) + self.last_actuators = self.card.controls_update(CC) CC.actuatorsOutput = self.last_actuators if self.CP.steerControlType == car.CarParams.SteerControlType.angle: self.steer_limited = abs(CC.actuators.steeringAngleDeg - CC.actuatorsOutput.steeringAngleDeg) > \ @@ -812,7 +888,7 @@ def publish_logs(self, CS, start_time, CC, lac_log): controlsState.cumLagMs = -self.rk.remaining * 1000. controlsState.startMonoTime = int(start_time * 1e9) controlsState.forceDecel = bool(force_decel) - controlsState.canErrorCounter = self.can_rcv_cum_timeout_counter + controlsState.canErrorCounter = self.card.can_rcv_cum_timeout_counter controlsState.experimentalMode = self.experimental_mode lat_tuning = self.CP.lateralTuning.which() @@ -827,13 +903,9 @@ def publish_logs(self, CS, start_time, CC, lac_log): self.pm.send('controlsState', dat) - # carState car_events = self.events.to_msg() - cs_send = messaging.new_message('carState') - cs_send.valid = CS.canValid - cs_send.carState = CS - cs_send.carState.events = car_events - self.pm.send('carState', cs_send) + + self.card.state_publish(car_events) # onroadEvents - logged every second or on change if (self.sm.frame % int(1. / DT_CTRL) == 0) or (self.events.names != self.events_prev): @@ -843,13 +915,6 @@ def publish_logs(self, CS, start_time, CC, lac_log): self.pm.send('onroadEvents', ce_send) self.events_prev = self.events.names.copy() - # carParams - logged every 50 seconds (> 1 per segment) - if (self.sm.frame % int(50. / DT_CTRL) == 0): - cp_send = messaging.new_message('carParams') - cp_send.valid = True - cp_send.carParams = self.CP - self.pm.send('carParams', cp_send) - # carControl cc_send = messaging.new_message('carControl') cc_send.valid = CS.canValid diff --git a/selfdrive/controls/lib/events.py b/selfdrive/controls/lib/events.py index 80a04bb878a022..0cf4708296f1a5 100755 --- a/selfdrive/controls/lib/events.py +++ b/selfdrive/controls/lib/events.py @@ -809,12 +809,12 @@ def speed_limit_adjust_alert(CP: car.CarParams, CS: car.CarState, sm: messaging. # is thrown. This can mean a service crashed, did not broadcast a message for # ten times the regular interval, or the average interval is more than 10% too high. EventName.commIssue: { - ET.SOFT_DISABLE: soft_disable_alert("Communication Issue between Processes"), + ET.SOFT_DISABLE: soft_disable_alert("Communication Issue Between Processes"), ET.NO_ENTRY: comm_issue_alert, }, EventName.commIssueAvgFreq: { - ET.SOFT_DISABLE: soft_disable_alert("Low Communication Rate between Processes"), - ET.NO_ENTRY: NoEntryAlert("Low Communication Rate between Processes"), + ET.SOFT_DISABLE: soft_disable_alert("Low Communication Rate Between Processes"), + ET.NO_ENTRY: NoEntryAlert("Low Communication Rate Between Processes"), }, EventName.controlsdLagging: { diff --git a/selfdrive/debug/count_events.py b/selfdrive/debug/count_events.py index 0d545b3153671b..30e9bf8ec5ba75 100755 --- a/selfdrive/debug/count_events.py +++ b/selfdrive/debug/count_events.py @@ -10,48 +10,52 @@ from openpilot.tools.lib.logreader import LogReader, ReadMode if __name__ == "__main__": - cnt_valid: Counter = Counter() cnt_events: Counter = Counter() 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]] = [] start_time = math.inf end_time = -math.inf ignition_off = None for msg in LogReader(sys.argv[1], ReadMode.QLOG): + t = (msg.logMonoTime - start_time) / 1e9 end_time = max(end_time, msg.logMonoTime) start_time = min(start_time, msg.logMonoTime) if msg.which() == 'onroadEvents': for e in msg.onroadEvents: cnt_events[e.name] += 1 + + ae = {str(e.name) for e in msg.onroadEvents if e.name not in ('pedalPressed', 'steerOverride', 'gasPressedOverride')} + if len(events) == 0 or ae != events[-1][1]: + events.append((t, ae)) + elif msg.which() == 'controlsState': at = msg.controlsState.alertType if "/override" not in at or "lanechange" in at.lower(): if len(alerts) == 0 or alerts[-1][1] != at: - t = (msg.logMonoTime - start_time) / 1e9 alerts.append((t, at)) elif msg.which() == 'pandaStates': if ignition_off is None: ign = any(ps.ignitionLine or ps.ignitionCan for ps in msg.pandaStates) if not ign: ignition_off = msg.logMonoTime + break elif msg.which() in cams: cnt_cameras[msg.which()] += 1 - if not msg.valid: - cnt_valid[msg.which()] += 1 - duration = (end_time - start_time) / 1e9 print("Events") pprint(cnt_events) print("\n") - print("Not valid") - pprint(cnt_valid) + print("Events") + for t, evt in events: + print(f"{t:8.2f} {evt}") print("\n") print("Cameras") @@ -64,9 +68,9 @@ print("Alerts") for t, a in alerts: print(f"{t:8.2f} {a}") + + print("\n") if ignition_off is not None: ignition_off = round((ignition_off - start_time) / 1e9, 2) print("Ignition off at", ignition_off) - - print("\n") print("Route duration", datetime.timedelta(seconds=duration)) diff --git a/selfdrive/debug/filter_log_message.py b/selfdrive/debug/filter_log_message.py index 20028f8fd204c7..9cbab0b41fe1c6 100755 --- a/selfdrive/debug/filter_log_message.py +++ b/selfdrive/debug/filter_log_message.py @@ -46,6 +46,7 @@ def print_androidlog(t, msg): if __name__ == "__main__": parser = argparse.ArgumentParser() + parser.add_argument('--absolute', action='store_true') parser.add_argument('--level', default='DEBUG') parser.add_argument('--addr', default='127.0.0.1') parser.add_argument("route", type=str, nargs='*', help="route name + segment number for offline usage") @@ -54,15 +55,18 @@ def print_androidlog(t, msg): min_level = LEVELS[args.level] if args.route: + st = None if not args.absolute else 0 for route in args.route: - lr = LogReader(route) + lr = LogReader(route, sort_by_time=True) for m in lr: + if st is None: + st = m.logMonoTime if m.which() == 'logMessage': - print_logmessage(m.logMonoTime, m.logMessage, min_level) + print_logmessage(m.logMonoTime-st, m.logMessage, min_level) elif m.which() == 'errorLogMessage': - print_logmessage(m.logMonoTime, m.errorLogMessage, min_level) + print_logmessage(m.logMonoTime-st, m.errorLogMessage, min_level) elif m.which() == 'androidLog': - print_androidlog(m.logMonoTime, m.androidLog) + print_androidlog(m.logMonoTime-st, m.androidLog) else: sm = messaging.SubMaster(['logMessage', 'androidLog'], addr=args.addr) while True: diff --git a/selfdrive/debug/test_fw_query_on_routes.py b/selfdrive/debug/test_fw_query_on_routes.py index dd6243a44c2329..cc6fc2ae17e89f 100755 --- a/selfdrive/debug/test_fw_query_on_routes.py +++ b/selfdrive/debug/test_fw_query_on_routes.py @@ -44,11 +44,15 @@ dongles = [] for route in tqdm(routes): - dongle_id = SegmentRange(route).dongle_id + sr = SegmentRange(route) + dongle_id = sr.dongle_id if dongle_id in dongles: continue + if sr.slice == '' and sr.selector is None: + route += '/0' + lr = LogReader(route, default_mode=ReadMode.QLOG) try: diff --git a/selfdrive/locationd/helpers.py b/selfdrive/locationd/helpers.py index 93e29291394a74..c292e9886c7e37 100644 --- a/selfdrive/locationd/helpers.py +++ b/selfdrive/locationd/helpers.py @@ -27,17 +27,17 @@ def __init__(self, x_bounds: List[Tuple[float, float]], min_points: List[float], self.buckets_min_points = dict(zip(x_bounds, min_points, strict=True)) self.min_points_total = min_points_total - def bucket_lengths(self) -> List[int]: - return [len(v) for v in self.buckets.values()] - def __len__(self) -> int: - return sum(self.bucket_lengths()) + return sum([len(v) for v in self.buckets.values()]) def is_valid(self) -> bool: individual_buckets_valid = all(len(v) >= min_pts for v, min_pts in zip(self.buckets.values(), self.buckets_min_points.values(), strict=True)) total_points_valid = self.__len__() >= self.min_points_total return individual_buckets_valid and total_points_valid + def is_calculable(self) -> bool: + return all(len(v) > 0 for v in self.buckets.values()) + def add_point(self, x: float, y: float, bucket_val: float) -> None: raise NotImplementedError diff --git a/selfdrive/locationd/torqued.py b/selfdrive/locationd/torqued.py index 51418f9c1e94a5..69bab8d1fab9e5 100755 --- a/selfdrive/locationd/torqued.py +++ b/selfdrive/locationd/torqued.py @@ -184,23 +184,23 @@ def get_msg(self, valid=True, with_points=False): liveTorqueParameters.version = VERSION liveTorqueParameters.useParams = self.use_params - if self.filtered_points.is_valid(): + # Calculate raw estimates when possible, only update filters when enough points are gathered + if self.filtered_points.is_calculable(): latAccelFactor, latAccelOffset, frictionCoeff = self.estimate_params() liveTorqueParameters.latAccelFactorRaw = float(latAccelFactor) liveTorqueParameters.latAccelOffsetRaw = float(latAccelOffset) liveTorqueParameters.frictionCoefficientRaw = float(frictionCoeff) - if any(val is None or np.isnan(val) for val in [latAccelFactor, latAccelOffset, frictionCoeff]): - cloudlog.exception("Live torque parameters are invalid.") - liveTorqueParameters.liveValid = False - self.reset() - else: - liveTorqueParameters.liveValid = True - latAccelFactor = np.clip(latAccelFactor, self.min_lataccel_factor, self.max_lataccel_factor) - frictionCoeff = np.clip(frictionCoeff, self.min_friction, self.max_friction) - self.update_params({'latAccelFactor': latAccelFactor, 'latAccelOffset': latAccelOffset, 'frictionCoefficient': frictionCoeff}) - else: - liveTorqueParameters.liveValid = False + if self.filtered_points.is_valid(): + if any(val is None or np.isnan(val) for val in [latAccelFactor, latAccelOffset, frictionCoeff]): + cloudlog.exception("Live torque parameters are invalid.") + liveTorqueParameters.liveValid = False + self.reset() + else: + liveTorqueParameters.liveValid = True + latAccelFactor = np.clip(latAccelFactor, self.min_lataccel_factor, self.max_lataccel_factor) + frictionCoeff = np.clip(frictionCoeff, self.min_friction, self.max_friction) + self.update_params({'latAccelFactor': latAccelFactor, 'latAccelOffset': latAccelOffset, 'frictionCoefficient': frictionCoeff}) if with_points: liveTorqueParameters.points = self.filtered_points.get_points()[:, [0, 2]].tolist() diff --git a/selfdrive/manager/manager.py b/selfdrive/manager/manager.py index aaa88df6d0a2f9..3a746f08f841b1 100755 --- a/selfdrive/manager/manager.py +++ b/selfdrive/manager/manager.py @@ -19,7 +19,7 @@ from openpilot.common.swaglog import cloudlog, add_file_handler from openpilot.system.version import is_dirty, get_commit, get_version, get_origin, get_short_branch, \ get_normalized_origin, terms_version, training_version, \ - is_tested_branch, is_release_branch + is_tested_branch, is_release_branch, get_commit_date def manager_init() -> None: @@ -126,6 +126,7 @@ def manager_init() -> None: params.put("TermsVersion", terms_version) params.put("TrainingVersion", training_version) params.put("GitCommit", get_commit()) + params.put("GitCommitDate", get_commit_date()) params.put("GitBranch", get_short_branch()) params.put("GitRemote", get_origin()) params.put_bool("IsTestedBranch", is_tested_branch()) diff --git a/selfdrive/manager/process.py b/selfdrive/manager/process.py index 523e1fd20933a3..ac1b4ac68c522f 100644 --- a/selfdrive/manager/process.py +++ b/selfdrive/manager/process.py @@ -281,11 +281,13 @@ def ensure_running(procs: ValuesView[ManagerProcess], started: bool, params=None running = [] for p in procs: if p.enabled and p.name not in not_run and p.should_run(started, params, CP): - p.start() running.append(p) else: p.stop(block=False) p.check_watchdog(started) + for p in running: + p.start() + return running diff --git a/selfdrive/modeld/fill_model_msg.py b/selfdrive/modeld/fill_model_msg.py index 93d1c7e77bac29..c76966867ae871 100644 --- a/selfdrive/modeld/fill_model_msg.py +++ b/selfdrive/modeld/fill_model_msg.py @@ -45,7 +45,7 @@ def fill_xyvat(builder, t, x, y, v, a, x_std=None, y_std=None, v_std=None, a_std 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, v_ego: float, steer_delay: float, valid: bool) -> None: + nav_enabled: bool, valid: bool) -> None: frame_age = frame_id - vipc_frame_id if frame_id > vipc_frame_id else 0 msg.valid = valid diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 020fa36fca7a90..266b94be8f2976 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -116,12 +116,16 @@ def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_ def main(demo=False): + cloudlog.warning("modeld init") + sentry.set_tag("daemon", PROCESS_NAME) cloudlog.bind(daemon=PROCESS_NAME) setproctitle(PROCESS_NAME) config_realtime_process(7, 54) + cloudlog.warning("setting up CL context") cl_context = CLContext() + cloudlog.warning("CL context ready; loading model") model = ModelState(cl_context) cloudlog.warning("models loaded, modeld starting") @@ -152,12 +156,8 @@ def main(demo=False): pm = PubMaster(["modelV2", "modelV2SP", "cameraOdometry"]) sm = SubMaster(["carState", "roadCameraState", "liveCalibration", "driverMonitoringState", "navModel", "navInstruction", "carControl"]) - publish_state = PublishState() params = Params() - with car.CarParams.from_bytes(params.get("CarParams", block=True)) as msg: - steer_delay = msg.steerActuatorDelay + .2 - #steer_delay = 0.4 # setup filter to track dropped frames frame_dropped_filter = FirstOrderFilter(0., 10., 1. / ModelConstants.MODEL_FREQ) @@ -177,13 +177,15 @@ def main(demo=False): if demo: CP = get_demo_car_params() - with car.CarParams.from_bytes(params.get("CarParams", block=True)) as msg: - CP = msg - cloudlog.info("plannerd got CarParams: %s", CP.carName) + else: + with car.CarParams.from_bytes(params.get("CarParams", block=True)) as msg: + CP = msg + cloudlog.info("modeld got CarParams: %s", CP.carName) + # TODO this needs more thought, use .2s extra for now to estimate other delays steer_delay = CP.steerActuatorDelay + .2 - DH = DesireHelper() + DH = DesireHelper() while True: # Keep receiving frames until we are at least 1 frame ahead of previous extra frame @@ -219,13 +221,10 @@ def main(demo=False): buf_extra = buf_main meta_extra = meta_main - # TODO: path planner timeout? sm.update(0) desire = DH.desire - v_ego = sm["carState"].vEgo is_rhd = sm["driverMonitoringState"].isRHD frame_id = sm["roadCameraState"].frameId - # TODO add lag lateral_control_params = np.array([sm["carState"].vEgo, steer_delay], dtype=np.float32) if sm.updated["liveCalibration"]: device_from_calib_euler = np.array(sm["liveCalibration"].rpyCalib, dtype=np.float32) @@ -294,7 +293,7 @@ def main(demo=False): modelv2_sp_send = messaging.new_message('modelV2SP') posenet_send = messaging.new_message('cameraOdometry') fill_model_msg(modelv2_send, model_output, publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id, frame_drop_ratio, - meta_main.timestamp_eof, timestamp_llk, model_execution_time, nav_enabled, v_ego, steer_delay, live_calib_seen) + meta_main.timestamp_eof, timestamp_llk, model_execution_time, nav_enabled, live_calib_seen) desire_state = modelv2_send.modelV2.meta.desireState l_lane_change_prob = desire_state[log.Desire.laneChangeLeft] diff --git a/selfdrive/modeld/models/commonmodel_pyx.pxd b/selfdrive/modeld/models/commonmodel_pyx.pxd index 21c0716de4c30e..97e391458892ad 100644 --- a/selfdrive/modeld/models/commonmodel_pyx.pxd +++ b/selfdrive/modeld/models/commonmodel_pyx.pxd @@ -7,7 +7,7 @@ cdef class CLContext(BaseCLContext): pass cdef class CLMem: - cdef cl_mem * mem; + cdef cl_mem * mem @staticmethod cdef create(void*) diff --git a/selfdrive/modeld/models/supercombo.onnx b/selfdrive/modeld/models/supercombo.onnx index dc93d84135ae81..ce346a406ad943 100644 --- a/selfdrive/modeld/models/supercombo.onnx +++ b/selfdrive/modeld/models/supercombo.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:763821410b35b06b598cacfa5bc3e312610b3f8de2729e0d5954d7571b6794be -size 48219112 +oid sha256:c11f99aab832242a604b1bb4c4cf391c9c3d5e90cbc07ab0d4c133473b56a3a4 +size 48193749 diff --git a/selfdrive/modeld/runners/runmodel_pyx.pyx b/selfdrive/modeld/runners/runmodel_pyx.pyx index cdc62a79be0c15..e1b201a6a92742 100644 --- a/selfdrive/modeld/runners/runmodel_pyx.pyx +++ b/selfdrive/modeld/runners/runmodel_pyx.pyx @@ -2,7 +2,6 @@ # cython: c_string_encoding=ascii from libcpp.string cimport string -from libc.string cimport memcpy from .runmodel cimport USE_CPU_RUNTIME, USE_GPU_RUNTIME, USE_DSP_RUNTIME from selfdrive.modeld.models.commonmodel_pyx cimport CLMem diff --git a/selfdrive/modeld/thneed/serialize.cc b/selfdrive/modeld/thneed/serialize.cc index 6ed5c08e811be3..3dc2bef41448f8 100644 --- a/selfdrive/modeld/thneed/serialize.cc +++ b/selfdrive/modeld/thneed/serialize.cc @@ -4,13 +4,14 @@ #include "third_party/json11/json11.hpp" #include "common/util.h" #include "common/clutil.h" +#include "common/swaglog.h" #include "selfdrive/modeld/thneed/thneed.h" using namespace json11; extern map g_program_source; void Thneed::load(const char *filename) { - printf("Thneed::load: loading from %s\n", filename); + LOGD("Thneed::load: loading from %s\n", filename); string buf = util::read_file(filename); int jsz = *(int *)buf.data(); @@ -74,8 +75,8 @@ void Thneed::load(const char *filename) { clbuf = clCreateImage(context, CL_MEM_READ_WRITE, &format, &desc, NULL, &errcode); #endif if (clbuf == NULL) { - printf("clError: %s create image %zux%zu rp %zu with buffer %p\n", cl_get_error_string(errcode), - desc.image_width, desc.image_height, desc.image_row_pitch, desc.buffer); + LOGE("clError: %s create image %zux%zu rp %zu with buffer %p\n", cl_get_error_string(errcode), + desc.image_width, desc.image_height, desc.image_row_pitch, desc.buffer); } assert(clbuf != NULL); } @@ -95,11 +96,11 @@ void Thneed::load(const char *filename) { cl_mem aa = real_mem[*(cl_mem*)(mobj["buffer_id"].string_value().data())]; input_clmem.push_back(aa); input_sizes.push_back(sz); - printf("Thneed::load: adding input %s with size %d\n", mobj["name"].string_value().data(), sz); + LOGD("Thneed::load: adding input %s with size %d\n", mobj["name"].string_value().data(), sz); cl_int cl_err; void *ret = clEnqueueMapBuffer(command_queue, aa, CL_TRUE, CL_MAP_WRITE, 0, sz, 0, NULL, NULL, &cl_err); - if (cl_err != CL_SUCCESS) printf("clError: %s map %p %d\n", cl_get_error_string(cl_err), aa, sz); + if (cl_err != CL_SUCCESS) LOGE("clError: %s map %p %d\n", cl_get_error_string(cl_err), aa, sz); assert(cl_err == CL_SUCCESS); inputs.push_back(ret); } @@ -107,7 +108,7 @@ void Thneed::load(const char *filename) { for (auto &obj : jdat["outputs"].array_items()) { auto mobj = obj.object_items(); int sz = mobj["size"].int_value(); - printf("Thneed::save: adding output with size %d\n", sz); + LOGD("Thneed::save: adding output with size %d\n", sz); // TODO: support multiple outputs output = real_mem[*(cl_mem*)(mobj["buffer_id"].string_value().data())]; assert(output != NULL); diff --git a/selfdrive/modeld/transforms/transform.cl b/selfdrive/modeld/transforms/transform.cl index 357ef87321371c..2ca25920cd19be 100644 --- a/selfdrive/modeld/transforms/transform.cl +++ b/selfdrive/modeld/transforms/transform.cl @@ -22,20 +22,20 @@ __kernel void warpPerspective(__global const uchar * src, W = W != 0.0f ? INTER_TAB_SIZE / W : 0.0f; int X = rint(X0 * W), Y = rint(Y0 * W); - short sx = convert_short_sat(X >> INTER_BITS); - short sy = convert_short_sat(Y >> INTER_BITS); + int sx = convert_short_sat(X >> INTER_BITS); + int sy = convert_short_sat(Y >> INTER_BITS); + + short sx_clamp = clamp(sx, 0, src_cols - 1); + short sx_p1_clamp = clamp(sx + 1, 0, src_cols - 1); + short sy_clamp = clamp(sy, 0, src_rows - 1); + short sy_p1_clamp = clamp(sy + 1, 0, src_rows - 1); + int v0 = convert_int(src[mad24(sy_clamp, src_row_stride, src_offset + sx_clamp*src_px_stride)]); + int v1 = convert_int(src[mad24(sy_clamp, src_row_stride, src_offset + sx_p1_clamp*src_px_stride)]); + int v2 = convert_int(src[mad24(sy_p1_clamp, src_row_stride, src_offset + sx_clamp*src_px_stride)]); + int v3 = convert_int(src[mad24(sy_p1_clamp, src_row_stride, src_offset + sx_p1_clamp*src_px_stride)]); + short ay = (short)(Y & (INTER_TAB_SIZE - 1)); short ax = (short)(X & (INTER_TAB_SIZE - 1)); - - int v0 = (sx >= 0 && sx < src_cols && sy >= 0 && sy < src_rows) ? - convert_int(src[mad24(sy, src_row_stride, src_offset + sx*src_px_stride)]) : 0; - int v1 = (sx+1 >= 0 && sx+1 < src_cols && sy >= 0 && sy < src_rows) ? - convert_int(src[mad24(sy, src_row_stride, src_offset + (sx+1)*src_px_stride)]) : 0; - int v2 = (sx >= 0 && sx < src_cols && sy+1 >= 0 && sy+1 < src_rows) ? - convert_int(src[mad24(sy+1, src_row_stride, src_offset + sx*src_px_stride)]) : 0; - int v3 = (sx+1 >= 0 && sx+1 < src_cols && sy+1 >= 0 && sy+1 < src_rows) ? - convert_int(src[mad24(sy+1, src_row_stride, src_offset + (sx+1)*src_px_stride)]) : 0; - float taby = 1.f/INTER_TAB_SIZE*ay; float tabx = 1.f/INTER_TAB_SIZE*ax; diff --git a/selfdrive/test/helpers.py b/selfdrive/test/helpers.py index a8b7ca0c4d0528..0e7912a989ace2 100644 --- a/selfdrive/test/helpers.py +++ b/selfdrive/test/helpers.py @@ -11,7 +11,6 @@ def set_params_enabled(): - os.environ['REPLAY'] = "1" os.environ['FINGERPRINT'] = "TOYOTA COROLLA TSS2 2019" os.environ['LOGPRINT'] = "debug" diff --git a/selfdrive/test/process_replay/model_replay_ref_commit b/selfdrive/test/process_replay/model_replay_ref_commit index a2f6896307ff87..3b7b04df808560 100644 --- a/selfdrive/test/process_replay/model_replay_ref_commit +++ b/selfdrive/test/process_replay/model_replay_ref_commit @@ -1 +1 @@ -fee90bcee1e545c7ec9a39d3c7d4e42cfefb9955 +fd6421f7551573c549480f9d29bb0dee4678344d diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 49e3461756aa46..fc38f873104b34 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -7d25b1f7d0bd3b506fa4e72ff893728894eb1a45 \ No newline at end of file +d0cdea7eb15f3cac8a921f7ace3eaa6baebb4fd5 diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py index c9064df870ba16..de8a4420b38c2a 100755 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -29,7 +29,7 @@ # Baseline CPU usage by process PROCS = { - "selfdrive.controls.controlsd": 39.0, + "selfdrive.controls.controlsd": 41.0, "./loggerd": 14.0, "./encoderd": 17.0, "./camerad": 14.5, @@ -114,6 +114,7 @@ def setUpClass(cls): params = Params() params.remove("CurrentRoute") set_params_enabled() + os.environ['REPLAY'] = '1' os.environ['TESTING_CLOSET'] = '1' if os.path.exists(Paths.log_root()): shutil.rmtree(Paths.log_root()) diff --git a/selfdrive/test/test_time_to_onroad.py b/selfdrive/test/test_time_to_onroad.py index 9288188a778f9a..a3f803e2210624 100755 --- a/selfdrive/test/test_time_to_onroad.py +++ b/selfdrive/test/test_time_to_onroad.py @@ -18,33 +18,40 @@ def test_time_to_onroad(): proc = subprocess.Popen(["python", manager_path]) start_time = time.monotonic() - sm = messaging.SubMaster(['controlsState', 'deviceState', 'onroadEvents']) + sm = messaging.SubMaster(['controlsState', 'deviceState', 'onroadEvents', 'sendcan']) try: - # wait for onroad - with Timeout(20, "timed out waiting to go onroad"): - while True: - sm.update(1000) - if sm['deviceState'].started: - break - time.sleep(1) + # wait for onroad. timeout assumes panda is up to date + with Timeout(10, "timed out waiting to go onroad"): + while not sm['deviceState'].started: + sm.update(100) # wait for engageability try: with Timeout(10, "timed out waiting for engageable"): + sendcan_frame = None while True: - sm.update(1000) - if sm['controlsState'].engageable: + sm.update(100) + + # sendcan is only sent once we're initialized + if sm.seen['controlsState'] and sendcan_frame is None: + sendcan_frame = sm.frame + + if sendcan_frame is not None and sm.recv_frame['sendcan'] > sendcan_frame: + sm.update(100) + assert sm['controlsState'].engageable, f"events: {sm['onroadEvents']}" break - time.sleep(1) finally: print(f"onroad events: {sm['onroadEvents']}") print(f"engageable after {time.monotonic() - start_time:.2f}s") - # once we're enageable, must be for the next few seconds - for _ in range(500): + # once we're enageable, must stay for the next few seconds + st = time.monotonic() + while (time.monotonic() - st) < 10.: sm.update(100) + assert sm.all_alive(), sm.alive assert sm['controlsState'].engageable, f"events: {sm['onroadEvents']}" + assert sm['controlsState'].cumLagMs < 10. finally: proc.terminate() - if proc.wait(60) is None: + if proc.wait(20) is None: proc.kill() diff --git a/selfdrive/thermald/power_monitoring.py b/selfdrive/thermald/power_monitoring.py index d10d642f0ba05b..962ab7b069b1f3 100644 --- a/selfdrive/thermald/power_monitoring.py +++ b/selfdrive/thermald/power_monitoring.py @@ -15,7 +15,6 @@ CAR_CHARGING_RATE_W = 45 VBATT_PAUSE_CHARGING = 11.8 # Lower limit on the LPF car battery voltage -VBATT_INSTANT_PAUSE_CHARGING = 7.0 # Lower limit on the instant car battery voltage measurements to avoid triggering on instant power loss MIN_ON_TIME_S = 3600 DELAY_SHUTDOWN_TIME_S = 300 # Wait at least DELAY_SHUTDOWN_TIME_S seconds after offroad_time to shutdown. VOLTAGE_SHUTDOWN_MIN_OFFROAD_TIME_S = 60 @@ -120,7 +119,6 @@ def should_shutdown(self, ignition: bool, in_car: bool, offroad_timestamp: Optio should_shutdown = False offroad_time = (now - offroad_timestamp) low_voltage_shutdown = (self.car_voltage_mV < (VBATT_PAUSE_CHARGING * 1e3) and - self.car_voltage_instant_mV > (VBATT_INSTANT_PAUSE_CHARGING * 1e3) and offroad_time > VOLTAGE_SHUTDOWN_MIN_OFFROAD_TIME_S) should_shutdown |= (offroad_time > max_time_offroad_s) if max_time_offroad_s != 0 else False should_shutdown |= low_voltage_shutdown diff --git a/selfdrive/thermald/thermald.py b/selfdrive/thermald/thermald.py index bfca2ccabbbeb2..e868f2ffdd05a7 100755 --- a/selfdrive/thermald/thermald.py +++ b/selfdrive/thermald/thermald.py @@ -83,7 +83,6 @@ def read_thermal(thermal_config): dat.deviceState.cpuTempC = [read_tz(z) / thermal_config.cpu[1] for z in thermal_config.cpu[0]] dat.deviceState.gpuTempC = [read_tz(z) / thermal_config.gpu[1] for z in thermal_config.gpu[0]] dat.deviceState.memoryTempC = read_tz(thermal_config.mem[0]) / thermal_config.mem[1] - dat.deviceState.ambientTempC = read_tz(thermal_config.ambient[0]) / thermal_config.ambient[1] dat.deviceState.pmicTempC = [read_tz(z) / thermal_config.pmic[1] for z in thermal_config.pmic[0]] return dat @@ -245,8 +244,10 @@ def thermald_thread(end_event, hw_queue) -> None: msg.deviceState.freeSpacePercent = get_available_percent(default=100.0) msg.deviceState.memoryUsagePercent = int(round(psutil.virtual_memory().percent)) - msg.deviceState.cpuUsagePercent = [int(round(n)) for n in psutil.cpu_percent(percpu=True)] msg.deviceState.gpuUsagePercent = int(round(HARDWARE.get_gpu_usage_percent())) + online_cpu_usage = [int(round(n)) for n in psutil.cpu_percent(percpu=True)] + offline_cpu_usage = [0., ] * (len(msg.deviceState.cpuTempC) - len(online_cpu_usage)) + msg.deviceState.cpuUsagePercent = online_cpu_usage + offline_cpu_usage msg.deviceState.networkType = last_hw_state.network_type msg.deviceState.networkMetered = last_hw_state.network_metered @@ -410,7 +411,6 @@ def thermald_thread(end_event, hw_queue) -> None: for i, temp in enumerate(msg.deviceState.gpuTempC): statlog.gauge(f"gpu{i}_temperature", temp) statlog.gauge("memory_temperature", msg.deviceState.memoryTempC) - statlog.gauge("ambient_temperature", msg.deviceState.ambientTempC) for i, temp in enumerate(msg.deviceState.pmicTempC): statlog.gauge(f"pmic{i}_temperature", temp) for i, temp in enumerate(last_hw_state.nvme_temps): diff --git a/selfdrive/ui/.gitignore b/selfdrive/ui/.gitignore index 104357f3969244..f9724816da6c5c 100644 --- a/selfdrive/ui/.gitignore +++ b/selfdrive/ui/.gitignore @@ -3,12 +3,13 @@ moc_* translations/main_test_en.* +_text +_spinner + ui mui watch3 installer/installers/* -qt/text -qt/spinner qt/setup/setup qt/setup/reset qt/setup/wifi diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index 92b43a82b00870..ea5734fe6eb2da 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -72,8 +72,8 @@ asset_obj = qt_env.Object("assets", assets) qt_env.SharedLibrary("qt/python_helpers", ["qt/qt_window.cc"], LIBS=qt_libs) # spinner and text window -qt_env.Program("qt/text", ["qt/text.cc"], LIBS=qt_libs) -qt_env.Program("qt/spinner", ["qt/spinner.cc"], LIBS=qt_libs) +qt_env.Program("_text", ["qt/text.cc"], LIBS=qt_libs) +qt_env.Program("_spinner", ["qt/spinner.cc"], LIBS=qt_libs) # build main UI qt_env.Program("ui", qt_src + [asset_obj], LIBS=qt_libs) diff --git a/selfdrive/ui/qt/util.cc b/selfdrive/ui/qt/util.cc index 02001fcc2320b7..b638381030a934 100644 --- a/selfdrive/ui/qt/util.cc +++ b/selfdrive/ui/qt/util.cc @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -114,6 +115,11 @@ void initApp(int argc, char *argv[], bool disable_hidpi) { qputenv("QT_DBL_CLICK_DIST", QByteArray::number(150)); + // ensure the current dir matches the exectuable's directory + QApplication tmp(argc, argv); + QString appDir = QCoreApplication::applicationDirPath(); + QDir::setCurrent(appDir); + setQtSurfaceFormat(); } diff --git a/selfdrive/ui/spinner b/selfdrive/ui/spinner index 35feab3f7e8f3d..965c8f581ab64a 100755 --- a/selfdrive/ui/spinner +++ b/selfdrive/ui/spinner @@ -1,7 +1,7 @@ #!/bin/sh -if [ -f /TICI ] && [ ! -f qt/spinner ]; then - cp qt/spinner_larch64 qt/spinner +if [ -f /TICI ] && [ ! -f _spinner ]; then + cp qt/spinner_larch64 _spinner fi -exec ./qt/spinner "$1" +exec ./_spinner "$1" diff --git a/selfdrive/ui/text b/selfdrive/ui/text index b44bec42bdd15e..b12235f4e6a00e 100755 --- a/selfdrive/ui/text +++ b/selfdrive/ui/text @@ -1,7 +1,7 @@ #!/bin/sh -if [ -f /TICI ] && [ ! -f qt/text ]; then - cp qt/text_larch64 qt/text +if [ -f /TICI ] && [ ! -f _text ]; then + cp qt/text_larch64 _text fi -exec ./qt/text "$1" +exec ./_text "$1" diff --git a/system/hardware/base.py b/system/hardware/base.py index 9c7a618337c409..7434bb61e8cafa 100644 --- a/system/hardware/base.py +++ b/system/hardware/base.py @@ -4,7 +4,7 @@ from cereal import log -ThermalConfig = namedtuple('ThermalConfig', ['cpu', 'gpu', 'mem', 'bat', 'ambient', 'pmic']) +ThermalConfig = namedtuple('ThermalConfig', ['cpu', 'gpu', 'mem', 'bat', 'pmic']) NetworkType = log.DeviceState.NetworkType diff --git a/system/hardware/pc/hardware.py b/system/hardware/pc/hardware.py index 4c2c104f94b5fd..719e272aea1639 100644 --- a/system/hardware/pc/hardware.py +++ b/system/hardware/pc/hardware.py @@ -57,7 +57,7 @@ def shutdown(self): print("SHUTDOWN!") def get_thermal_config(self): - return ThermalConfig(cpu=((None,), 1), gpu=((None,), 1), mem=(None, 1), bat=(None, 1), ambient=(None, 1), pmic=((None,), 1)) + return ThermalConfig(cpu=((None,), 1), gpu=((None,), 1), mem=(None, 1), bat=(None, 1), pmic=((None,), 1)) def set_screen_brightness(self, percentage): pass diff --git a/system/hardware/tici/agnos.py b/system/hardware/tici/agnos.py index ef7d9adb7956a5..342281b0f87a0e 100755 --- a/system/hardware/tici/agnos.py +++ b/system/hardware/tici/agnos.py @@ -20,7 +20,7 @@ class StreamingDecompressor: def __init__(self, url: str) -> None: self.buf = b"" - self.req = requests.get(url, stream=True, headers={'Accept-Encoding': None}, timeout=60) # type: ignore + self.req = requests.get(url, stream=True, headers={'Accept-Encoding': None}, timeout=60) self.it = self.req.iter_content(chunk_size=1024 * 1024) self.decompressor = lzma.LZMADecompressor(format=lzma.FORMAT_AUTO) self.eof = False diff --git a/system/hardware/tici/hardware.py b/system/hardware/tici/hardware.py index f6064220284230..5bb1032ba9749d 100644 --- a/system/hardware/tici/hardware.py +++ b/system/hardware/tici/hardware.py @@ -349,7 +349,6 @@ def get_thermal_config(self): gpu=(("gpu0-usr", "gpu1-usr"), 1000), mem=("ddr-usr", 1000), bat=(None, 1), - ambient=("xo-therm-adc", 1000), pmic=(("pm8998_tz", "pm8005_tz"), 1000)) def set_screen_brightness(self, percentage): diff --git a/system/loggerd/logger.cc b/system/loggerd/logger.cc index 2fc6492ad4744c..7a829a2f1f9cb0 100644 --- a/system/loggerd/logger.cc +++ b/system/loggerd/logger.cc @@ -44,6 +44,7 @@ kj::Array logger_build_init_data() { std::map params_map = params.readAll(); init.setGitCommit(params_map["GitCommit"]); + init.setGitCommitDate(params_map["GitCommitDate"]); init.setGitBranch(params_map["GitBranch"]); init.setGitRemote(params_map["GitRemote"]); init.setPassive(false); diff --git a/system/loggerd/tests/test_loggerd.py b/system/loggerd/tests/test_loggerd.py index 0cd854880962c4..963978926d4d92 100755 --- a/system/loggerd/tests/test_loggerd.py +++ b/system/loggerd/tests/test_loggerd.py @@ -107,6 +107,7 @@ def test_init_data_values(self): # param, initData field, value ("DongleId", "dongleId", dongle), ("GitCommit", "gitCommit", "commit"), + ("GitCommitDate", "gitCommitDate", "date"), ("GitBranch", "gitBranch", "branch"), ("GitRemote", "gitRemote", "remote"), ] diff --git a/system/loggerd/uploader.py b/system/loggerd/uploader.py index c5cfb9a7ae685e..9e357957aa9050 100755 --- a/system/loggerd/uploader.py +++ b/system/loggerd/uploader.py @@ -207,10 +207,10 @@ def upload(self, name: str, key: str, fn: str, network_type: int, metered: bool) return success - def step(self, network_type: int, metered: bool) -> bool: + def step(self, network_type: int, metered: bool) -> Optional[bool]: d = self.next_file_to_upload(metered) if d is None: - return True + return None name, key, fn = d @@ -280,12 +280,15 @@ def main(exit_event: Optional[threading.Event] = None) -> None: continue success = uploader.step(sm['deviceState'].networkType.raw, sm['deviceState'].networkMetered) - if success: + if success is None: + backoff = 60 if offroad else 5 + elif success: backoff = 0.1 - elif allow_sleep: + else: cloudlog.info("upload backoff %r", backoff) backoff = min(backoff*2, 120) - time.sleep(backoff + random.uniform(0, backoff)) + if allow_sleep: + time.sleep(backoff + random.uniform(0, backoff)) if __name__ == "__main__": diff --git a/system/qcomgpsd/qcomgpsd.py b/system/qcomgpsd/qcomgpsd.py index 3f723502340e61..e2a180df863415 100755 --- a/system/qcomgpsd/qcomgpsd.py +++ b/system/qcomgpsd/qcomgpsd.py @@ -94,11 +94,7 @@ def at_cmd(cmd: str) -> Optional[str]: return subprocess.check_output(f"mmcli -m any --timeout 30 --command='{cmd}'", shell=True, encoding='utf8') def gps_enabled() -> bool: - try: - p = subprocess.check_output("mmcli -m any --command=\"AT+QGPS?\"", shell=True) - return b"QGPS: 1" in p - except subprocess.CalledProcessError as exc: - raise Exception("failed to execute QGPS mmcli command") from exc + return "QGPS: 1" in at_cmd("AT+QGPS?") def download_assistance(): try: diff --git a/system/qcomgpsd/tests/test_qcomgpsd.py b/system/qcomgpsd/tests/test_qcomgpsd.py index 8291f2cc32a92a..6c93f7dd9382fb 100755 --- a/system/qcomgpsd/tests/test_qcomgpsd.py +++ b/system/qcomgpsd/tests/test_qcomgpsd.py @@ -68,13 +68,14 @@ def test_startup_time(self): def test_turns_off_gnss(self): for s in (0.1, 1, 5): - managed_processes['qcomgpsd'].start() - time.sleep(s) - managed_processes['qcomgpsd'].stop() + with self.subTest(runtime=s): + managed_processes['qcomgpsd'].start() + time.sleep(s) + managed_processes['qcomgpsd'].stop() - ls = subprocess.check_output("mmcli -m any --location-status --output-json", shell=True, encoding='utf-8') - loc_status = json.loads(ls) - assert set(loc_status['modem']['location']['enabled']) <= {'3gpp-lac-ci'} + ls = subprocess.check_output("mmcli -m any --location-status --output-json", shell=True, encoding='utf-8') + loc_status = json.loads(ls) + assert set(loc_status['modem']['location']['enabled']) <= {'3gpp-lac-ci'} def check_assistance(self, should_be_loaded): diff --git a/system/version.py b/system/version.py index 980a4fcc7cb584..e34458f16f964a 100755 --- a/system/version.py +++ b/system/version.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import os import subprocess -from typing import List, Optional, Callable, TypeVar +from typing import List, Callable, TypeVar from functools import lru_cache from openpilot.common.basedir import BASEDIR @@ -22,7 +22,7 @@ def run_cmd(cmd: List[str]) -> str: return subprocess.check_output(cmd, encoding='utf8').strip() -def run_cmd_default(cmd: List[str], default: Optional[str] = None) -> Optional[str]: +def run_cmd_default(cmd: List[str], default: str = "") -> str: try: return run_cmd(cmd) except subprocess.CalledProcessError: @@ -31,17 +31,22 @@ def run_cmd_default(cmd: List[str], default: Optional[str] = None) -> Optional[s @cache def get_commit(branch: str = "HEAD") -> str: - return run_cmd_default(["git", "rev-parse", branch]) or "" + return run_cmd_default(["git", "rev-parse", branch]) + + +@cache +def get_commit_date(commit: str = "HEAD") -> str: + return run_cmd_default(["git", "show", "--no-patch", "--format='%ct %ci'", commit]) @cache def get_short_branch() -> str: - return run_cmd_default(["git", "rev-parse", "--abbrev-ref", "HEAD"]) or "" + return run_cmd_default(["git", "rev-parse", "--abbrev-ref", "HEAD"]) @cache def get_branch() -> str: - return run_cmd_default(["git", "rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"]) or "" + return run_cmd_default(["git", "rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"]) @cache @@ -51,7 +56,7 @@ def get_origin() -> str: tracking_remote = run_cmd(["git", "config", "branch." + local_branch + ".remote"]) return run_cmd(["git", "config", "remote." + tracking_remote + ".url"]) except subprocess.CalledProcessError: # Not on a branch, fallback - return run_cmd_default(["git", "config", "--get", "remote.origin.url"]) or "" + return run_cmd_default(["git", "config", "--get", "remote.origin.url"]) @cache @@ -132,3 +137,4 @@ def is_dirty() -> bool: print(f"Branch: {get_branch()}") print(f"Short branch: {get_short_branch()}") print(f"Prebuilt: {is_prebuilt()}") + print(f"Commit date: {get_commit_date()}") diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index af2c23ef4898ec..7957712aff6602 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -73,7 +73,7 @@ class ReadMode(enum.StrEnum): QLOG = "q" # only read qlogs SANITIZED = "s" # read from the commaCarSegments database AUTO = "a" # default to rlogs, fallback to qlogs - AUTO_INTERACIVE = "i" # default to rlogs, fallback to qlogs with a prompt from the user + AUTO_INTERACTIVE = "i" # default to rlogs, fallback to qlogs with a prompt from the user LogPath = Optional[str] @@ -81,6 +81,8 @@ class ReadMode(enum.StrEnum): ValidFileCallable = Callable[[LogPath], bool] Source = Callable[[SegmentRange, ReadMode], LogPaths] +InternalUnavailableException = Exception("Internal source not available") + def default_valid_file(fn: LogPath) -> bool: return fn is not None and file_exists(fn) @@ -106,7 +108,7 @@ def apply_strategy(mode: ReadMode, rlog_paths: LogPaths, qlog_paths: LogPaths, v return qlog_paths elif mode == ReadMode.AUTO: return auto_strategy(rlog_paths, qlog_paths, False, valid_file) - elif mode == ReadMode.AUTO_INTERACIVE: + elif mode == ReadMode.AUTO_INTERACTIVE: return auto_strategy(rlog_paths, qlog_paths, True, valid_file) raise Exception(f"invalid mode: {mode}") @@ -126,7 +128,7 @@ def valid_file(fn): def internal_source(sr: SegmentRange, mode: ReadMode) -> LogPaths: if not internal_source_available(): - raise Exception("Internal source not available") + raise InternalUnavailableException def get_internal_url(sr: SegmentRange, seg, file): return f"cd:/{sr.dongle_id}/{sr.timestamp}/{seg}/{file}.bz2" @@ -160,7 +162,7 @@ def get_invalid_files(files): def check_source(source: Source, *args) -> LogPaths: files = source(*args) - assert next(get_invalid_files(files), None) is None + assert next(get_invalid_files(files), False) is False return files diff --git a/tools/lib/route.py b/tools/lib/route.py index aba95718d59de0..47ebdc7a514109 100644 --- a/tools/lib/route.py +++ b/tools/lib/route.py @@ -264,7 +264,7 @@ def timestamp(self) -> str: return self.m.group("timestamp") @property - def _slice(self) -> str: + def slice(self) -> str: return self.m.group("slice") or "" @property @@ -273,12 +273,12 @@ def selector(self) -> str | None: @property def seg_idxs(self) -> list[int]: - m = re.fullmatch(RE.SLICE, self._slice) - assert m is not None, f"Invalid slice: {self._slice}" + m = re.fullmatch(RE.SLICE, self.slice) + assert m is not None, f"Invalid slice: {self.slice}" start, end, step = (None if s is None else int(s) for s in m.groups()) # one segment specified - if start is not None and end is None and ':' not in self._slice: + if start is not None and end is None and ':' not in self.slice: if start < 0: start += get_max_seg_number_cached(self) + 1 return [start] @@ -291,7 +291,7 @@ def seg_idxs(self) -> list[int]: return list(range(end + 1))[s] def __str__(self) -> str: - return f"{self.dongle_id}/{self.timestamp}" + (f"/{self._slice}" if self._slice else "") + (f"/{self.selector}" if self.selector else "") + return f"{self.dongle_id}/{self.timestamp}" + (f"/{self.slice}" if self.slice else "") + (f"/{self.selector}" if self.selector else "") def __repr__(self) -> str: return self.__str__() diff --git a/tools/lib/tests/test_caching.py b/tools/lib/tests/test_caching.py index 4b73e0a301d9b0..bc92b013576891 100755 --- a/tools/lib/tests/test_caching.py +++ b/tools/lib/tests/test_caching.py @@ -2,11 +2,13 @@ from functools import partial import http.server import os +import shutil +import socket import unittest from parameterized import parameterized from openpilot.selfdrive.athena.tests.helpers import with_http_server - +from openpilot.system.hardware.hw import Paths from openpilot.tools.lib.url_file import URLFile @@ -15,7 +17,7 @@ class CachingTestRequestHandler(http.server.BaseHTTPRequestHandler): def do_GET(self): if self.FILE_EXISTS: - self.send_response(200, b'1234') + self.send_response(206 if "Range" in self.headers else 200, b'1234') else: self.send_response(404) self.end_headers() @@ -34,6 +36,34 @@ def do_HEAD(self): class TestFileDownload(unittest.TestCase): + @with_caching_server + def test_pipeline_defaults(self, host): + # TODO: parameterize the defaults so we don't rely on hard-coded values in xx + + self.assertEqual(URLFile.pool_manager().pools._maxsize, 10) # PoolManager num_pools param + pool_manager_defaults = { + "maxsize": 100, + "socket_options": [(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1),], + } + for k, v in pool_manager_defaults.items(): + self.assertEqual(URLFile.pool_manager().connection_pool_kw.get(k), v) + + retry_defaults = { + "total": 5, + "backoff_factor": 0.5, + "status_forcelist": [409, 429, 503, 504], + } + for k, v in retry_defaults.items(): + self.assertEqual(getattr(URLFile.pool_manager().connection_pool_kw["retries"], k), v) + + # ensure caching off by default and cache dir doesn't get created + os.environ.pop("FILEREADER_CACHE", None) + if os.path.exists(Paths.download_cache_root()): + shutil.rmtree(Paths.download_cache_root()) + URLFile(f"{host}/test.txt").get_length() + URLFile(f"{host}/test.txt").read() + self.assertEqual(os.path.exists(Paths.download_cache_root()), False) + def compare_loads(self, url, start=0, length=None): """Compares range between cached and non cached version""" file_cached = URLFile(url, cache=True) diff --git a/tools/lib/tests/test_comma_car_segments.py b/tools/lib/tests/test_comma_car_segments.py index 484a4aae0839fc..b355b0fe60dfe6 100644 --- a/tools/lib/tests/test_comma_car_segments.py +++ b/tools/lib/tests/test_comma_car_segments.py @@ -25,7 +25,7 @@ def test_download_segment(self): sr = SegmentRange(segment) - url = get_url(sr.route_name, sr._slice) + url = get_url(sr.route_name, sr.slice) resp = requests.get(url) self.assertEqual(resp.status_code, 200) diff --git a/tools/lib/tests/test_logreader.py b/tools/lib/tests/test_logreader.py index 53b78064ab316a..2141915b877231 100755 --- a/tools/lib/tests/test_logreader.py +++ b/tools/lib/tests/test_logreader.py @@ -1,4 +1,6 @@ #!/usr/bin/env python3 +import contextlib +import io import shutil import tempfile import os @@ -9,7 +11,7 @@ from parameterized import parameterized from unittest import mock -from openpilot.tools.lib.logreader import LogIterable, LogReader, comma_api_source, parse_indirect, ReadMode +from openpilot.tools.lib.logreader import LogIterable, LogReader, comma_api_source, parse_indirect, ReadMode, InternalUnavailableException from openpilot.tools.lib.route import SegmentRange from openpilot.tools.lib.url_file import URLFileException @@ -23,6 +25,24 @@ def noop(segment: LogIterable): return segment +@contextlib.contextmanager +def setup_source_scenario(is_internal=False): + with ( + mock.patch("openpilot.tools.lib.logreader.internal_source") as internal_source_mock, + mock.patch("openpilot.tools.lib.logreader.openpilotci_source") as openpilotci_source_mock, + mock.patch("openpilot.tools.lib.logreader.comma_api_source") as comma_api_source_mock, + ): + if is_internal: + internal_source_mock.return_value = [QLOG_FILE] + else: + internal_source_mock.side_effect = InternalUnavailableException + + openpilotci_source_mock.return_value = [None] + comma_api_source_mock.return_value = [QLOG_FILE] + + yield + + class TestLogReader(unittest.TestCase): @parameterized.expand([ (f"{TEST_ROUTE}", ALL_SEGS), @@ -168,10 +188,33 @@ def test_auto_mode(self): with mock.patch("openpilot.tools.lib.route.Route.log_paths") as log_paths_mock: log_paths_mock.return_value = [None] * NUM_SEGS # Should fall back to qlogs since rlogs are not available - lr = LogReader(f"{TEST_ROUTE}/0/a", default_source=comma_api_source) - log_len = len(list(lr)) - self.assertEqual(qlog_len, log_len) + with self.subTest("interactive_yes"): + with mock.patch("sys.stdin", new=io.StringIO("y\n")): + lr = LogReader(f"{TEST_ROUTE}/0", default_mode=ReadMode.AUTO_INTERACTIVE, default_source=comma_api_source) + log_len = len(list(lr)) + self.assertEqual(qlog_len, log_len) + + with self.subTest("interactive_no"): + with mock.patch("sys.stdin", new=io.StringIO("n\n")): + with self.assertRaises(AssertionError): + lr = LogReader(f"{TEST_ROUTE}/0", default_mode=ReadMode.AUTO_INTERACTIVE, default_source=comma_api_source) + + with self.subTest("non_interactive"): + lr = LogReader(f"{TEST_ROUTE}/0", default_mode=ReadMode.AUTO, default_source=comma_api_source) + log_len = len(list(lr)) + self.assertEqual(qlog_len, log_len) + + @parameterized.expand([(True,), (False,)]) + @pytest.mark.slow + def test_auto_source_scenarios(self, is_internal): + lr = LogReader(QLOG_FILE) + qlog_len = len(list(lr)) + + with setup_source_scenario(is_internal=is_internal): + lr = LogReader(f"{TEST_ROUTE}/0/q") + log_len = len(list(lr)) + self.assertEqual(qlog_len, log_len) if __name__ == "__main__": diff --git a/tools/plotjuggler/juggle.py b/tools/plotjuggler/juggle.py index cc2109541474b2..dc940628016c6e 100755 --- a/tools/plotjuggler/juggle.py +++ b/tools/plotjuggler/juggle.py @@ -73,7 +73,7 @@ def process(can, lr): return [d for d in lr if can or d.which() not in ['can', 'sendcan']] def juggle_route(route_or_segment_name, can, layout, dbc=None): - sr = LogReader(route_or_segment_name, default_mode=ReadMode.AUTO_INTERACIVE) + sr = LogReader(route_or_segment_name, default_mode=ReadMode.AUTO_INTERACTIVE) all_data = sr.run_across_segments(24, partial(process, can)) diff --git a/tools/plotjuggler/layouts/system_lag_debug.xml b/tools/plotjuggler/layouts/system_lag_debug.xml index b1699348cce46e..a90bba0e279627 100644 --- a/tools/plotjuggler/layouts/system_lag_debug.xml +++ b/tools/plotjuggler/layouts/system_lag_debug.xml @@ -16,7 +16,6 @@ - diff --git a/tools/replay/can_replay.py b/tools/replay/can_replay.py index ef6864f110edbb..c597df1f662c8a 100755 --- a/tools/replay/can_replay.py +++ b/tools/replay/can_replay.py @@ -10,45 +10,49 @@ from openpilot.common.realtime import config_realtime_process, Ratekeeper, DT_CTRL from openpilot.selfdrive.boardd.boardd import can_capnp_to_can_list from openpilot.tools.lib.logreader import LogReader -from panda import Panda, PandaJungle +from panda import PandaJungle + +# set both to cycle power or ignition +PWR_ON = int(os.getenv("PWR_ON", "0")) +PWR_OFF = int(os.getenv("PWR_OFF", "0")) +IGN_ON = int(os.getenv("ON", "0")) +IGN_OFF = int(os.getenv("OFF", "0")) +ENABLE_IGN = IGN_ON > 0 and IGN_OFF > 0 +ENABLE_PWR = PWR_ON > 0 and PWR_OFF > 0 + def send_thread(s, flock): - if "Jungle" in str(type(s)): - if "FLASH" in os.environ: - with flock: - s.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) - time.sleep(5) - s.set_ignition(True) - s.set_panda_power(True) - else: - s.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + if "FLASH" in os.environ: + with flock: + s.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) + time.sleep(5) + s.set_ignition(True) + s.set_panda_power(True) s.set_can_loopback(False) - idx = 0 - ign = True rk = Ratekeeper(1 / DT_CTRL, print_delay_threshold=None) while True: - # handle ignition cycling + # handle cycling + if ENABLE_PWR: + i = (rk.frame*DT_CTRL) % (PWR_ON + PWR_OFF) < PWR_ON + s.set_panda_power(i) if ENABLE_IGN: i = (rk.frame*DT_CTRL) % (IGN_ON + IGN_OFF) < IGN_ON - if i != ign: - ign = i - s.set_ignition(ign) + s.set_ignition(i) - snd = CAN_MSGS[idx] + snd = CAN_MSGS[rk.frame % len(CAN_MSGS)] snd = list(filter(lambda x: x[-1] <= 2, snd)) try: s.can_send_many(snd) except usb1.USBErrorTimeout: # timeout is fine, just means the CAN TX buffer is full pass - idx = (idx + 1) % len(CAN_MSGS) # Drain panda message buffer s.can_recv() @@ -98,10 +102,8 @@ def process(lr): print("Finished loading...") - # set both to cycle ignition - IGN_ON = int(os.getenv("ON", "0")) - IGN_OFF = int(os.getenv("OFF", "0")) - ENABLE_IGN = IGN_ON > 0 and IGN_OFF > 0 + if ENABLE_PWR: + print(f"Cycling power: on for {PWR_ON}s, off for {PWR_OFF}s") if ENABLE_IGN: print(f"Cycling ignition: on for {IGN_ON}s, off for {IGN_OFF}s") diff --git a/tools/sim/lib/simulated_car.py b/tools/sim/lib/simulated_car.py index 85b4ac23875604..f6319dd8190440 100644 --- a/tools/sim/lib/simulated_car.py +++ b/tools/sim/lib/simulated_car.py @@ -2,6 +2,7 @@ from opendbc.can.packer import CANPacker from opendbc.can.parser import CANParser +from openpilot.common.params import Params from openpilot.selfdrive.boardd.boardd_api_impl import can_list_to_can_capnp from openpilot.selfdrive.car import crc8_pedal from openpilot.tools.sim.lib.common import SimulatorState @@ -18,6 +19,8 @@ def __init__(self): self.sm = messaging.SubMaster(['carControl', 'controlsState', 'carParams']) self.cp = self.get_car_can_parser() self.idx = 0 + self.params = Params() + self.obd_multiplexing = False @staticmethod def get_car_can_parser(): @@ -100,6 +103,11 @@ def send_can_messages(self, simulator_state: SimulatorState): def send_panda_state(self, simulator_state): self.sm.update(0) + + if self.params.get_bool("ObdMultiplexingEnabled") != self.obd_multiplexing: + self.obd_multiplexing = not self.obd_multiplexing + self.params.put_bool("ObdMultiplexingChanged", True) + dat = messaging.new_message('pandaStates', 1) dat.valid = True dat.pandaStates[0] = { diff --git a/tools/sim/lib/simulated_sensors.py b/tools/sim/lib/simulated_sensors.py index ebbcc6c9e802ee..df6a0aeeff532f 100644 --- a/tools/sim/lib/simulated_sensors.py +++ b/tools/sim/lib/simulated_sensors.py @@ -3,7 +3,6 @@ from cereal import log import cereal.messaging as messaging -from openpilot.common.params import Params from openpilot.common.realtime import DT_DMON from openpilot.tools.sim.lib.camerad import Camerad @@ -80,7 +79,6 @@ def send_peripheral_state(self): 'current': 5678, 'fanSpeedRpm': 1000 } - Params().put_bool("ObdMultiplexingEnabled", False) self.pm.send('peripheralState', dat) def send_fake_driver_monitoring(self):