Compare commits

..

161 Commits

Author SHA1 Message Date
DevTekVE a77d1f7eb0 Add ButtonHoldTracker for button hold logic and tests
Introduce a new `ButtonHoldTracker` class to manage button hold durations, replacing manual timer handling in `ExperimentalSwitcher`. Updated `ExperimentalSwitcher` to leverage this implementation for cleaner and more modular code. Added comprehensive unit tests for both `ButtonHoldTracker` and `ExperimentalSwitcher` to ensure functionality and edge case coverage.
2025-01-19 12:58:29 +01:00
Jason Wen 6eb6af5785 Merge branch 'dec-redo' into master-dev-c3-new 2025-01-19 03:39:30 -05:00
Jason Wen 7471bd4029 init 2025-01-19 03:18:05 -05:00
Jason Wen 767f057112 Merge branch 'master-new' into dec-redo 2025-01-19 02:55:46 -05:00
Jason Wen 6ef47dafcf ui: display DEC status via mode button (#592) 2025-01-19 02:54:58 -05:00
Jason Wen 3f6ac7e77a Merge branch 'ui-model-selector-rich' into master-dev-c3-new
# Conflicts:
#	opendbc_repo
#	panda
2025-01-19 02:51:06 -05:00
Jason Wen d8040eea3f Merge branch 'master-new' into ui-model-selector-rich 2025-01-19 02:50:22 -05:00
Jason Wen d223135ee3 Merge branch 'master-new' into ui-model-selector-rich 2025-01-19 02:49:13 -05:00
Jason Wen 8f29ddba45 ui: Model Selector: display prompt with rich text widget 2025-01-19 02:48:32 -05:00
Jason Wen b7c3072a4c Merge branch 'mads-honda-states' into master-dev-c3-new 2025-01-18 23:57:32 -05:00
Jason Wen 3cba24f93a MADS: Honda: Allow steering 2025-01-18 23:57:05 -05:00
Jason Wen f2edd589ba fix 2025-01-18 22:04:30 -05:00
Jason Wen bf0a7b4494 migrate to sp only 2025-01-18 22:02:40 -05:00
Jason Wen 19b96003dc use another method for drawing 2025-01-18 21:51:49 -05:00
Kumar 10ee25d199 Update constants.py 2025-01-18 19:48:02 -07:00
Kumar cd6fb642bd Update dec.py 2025-01-18 19:46:19 -07:00
Jason Wen 3d35164287 even dimmer 2025-01-18 21:43:14 -05:00
Jason Wen a9ca9bbdf8 should be active and dimmer 2025-01-18 21:35:27 -05:00
Jason Wen 0737f311b8 add opacity 2025-01-18 21:31:01 -05:00
Jason Wen dc45d3043c try this 2025-01-18 21:22:55 -05:00
Jason Wen 2eefd0aaf5 visuals for DEC 2025-01-18 21:12:19 -05:00
Jason Wen cb9d92dc3c Merge branch 'master-new' into dec-redo 2025-01-18 20:55:35 -05:00
Jason Wen 2525fdc350 Merge branch 'mads-honda-states' into master-dev-c3-new 2025-01-18 20:41:18 -05:00
Jason Wen 9eb36a0b7e fix icons 2025-01-18 20:41:01 -05:00
Jason Wen b5a5d94a2c bring them back 2025-01-18 20:39:05 -05:00
Jason Wen a93dc0047d Merge branch 'mads-honda-states' into master-dev-c3-new 2025-01-18 16:35:34 -05:00
Jason Wen 25d195df72 try this 2025-01-18 16:35:27 -05:00
Jason Wen 3fc2f70eb8 nuke them all to test 2025-01-18 16:34:32 -05:00
Jason Wen 64058c4b7c Merge branch 'mads-honda-states' into master-dev-c3-new 2025-01-18 15:48:12 -05:00
Jason Wen 3044aa6133 bump opendbc 2025-01-18 15:48:01 -05:00
Jason Wen f39f76182a Merge branch 'mads-honda-states' into master-dev-c3-new 2025-01-18 15:45:07 -05:00
Jason Wen 15567bf605 Merge branch 'master-new' into master-dev-c3-new 2025-01-18 15:45:02 -05:00
Jason Wen 3b090e0b01 Merge branch 'master-new' into mads-honda-states 2025-01-18 15:44:51 -05:00
Jason Wen df3e848965 MADS: Honda: Cluster icons and events updates 2025-01-18 15:43:46 -05:00
DevTekVE b3e3d1b384 Fix lead detection fallback for weighted average check.
Add a fallback value of -1 when computing the weighted average to prevent errors caused by invalid or None values. This ensures robust lead detection and avoids potential crashes or undefined behavior.
2025-01-18 20:48:06 +01:00
DevTekVE b13356d267 Merge branch 'master-new' into dec-redo 2025-01-18 20:41:04 +01:00
rav4kumar 0089096d12 Update slow-down logic and constants for improved behavior
Adjust the slowdown scaling factor and anomaly handling to refine behavior without abrupt resets. Modify constants to increase window size and adjust probabilities and distances for smoother adaptation. Update version to reflect the new changes.
2025-01-18 11:26:02 -07:00
rav4kumar 3d135f9831 Revert "dec: how good is FirstOrderFilter?"
This reverts commit 01e06df542.
2025-01-18 11:12:10 -07:00
rav4kumar a219046981 Revert "Update dec.py"
This reverts commit 3f29ccbd99.
2025-01-18 11:12:03 -07:00
rav4kumar 2c9ffdbf03 Revert "dec: faster ?"
This reverts commit 40259cd22a.
2025-01-18 11:11:57 -07:00
Jason Wen 5014b42650 Merge branch 'ui-regulatory-hide' into master-dev-c3-new 2025-01-17 21:48:20 -05:00
Jason Wen c1f897168a ui: Hide regulatory button on non-comma devices 2025-01-17 21:45:51 -05:00
Jason Wen 301174b19a Merge branch 'ui-device-offroad' into master-dev-c3-new 2025-01-17 21:29:58 -05:00
Jason Wen 1b139c4ca1 ui: Transition offroad state for Device panel buttons 2025-01-17 21:29:46 -05:00
Jason Wen 745e7b0b28 Merge branch 'master-new' into master-dev-c3-new 2025-01-17 21:23:34 -05:00
Jason Wen 1f328c9fda Revert "ui: driver view window should use uiStateSP"
This reverts commit c08b18936d.
2025-01-17 15:02:49 -05:00
Jason Wen eab69c0f3d Revert "try this"
This reverts commit bd8a93af8b.
2025-01-17 15:02:47 -05:00
Jason Wen 97fda2f918 Revert "more"
This reverts commit 2f5f708c2b.
2025-01-17 15:02:44 -05:00
Jason Wen 03e10db82e Merge branch 'ui-dm-cam-freeze' into master-dev-c3-new 2025-01-17 14:59:14 -05:00
Jason Wen 2f5f708c2b more 2025-01-17 14:59:06 -05:00
Jason Wen 413fc15cf8 Merge branch 'ui-dm-cam-freeze' into master-dev-c3-new 2025-01-17 14:52:54 -05:00
Jason Wen bd8a93af8b try this 2025-01-17 14:52:42 -05:00
Jason Wen 928bec2acc Merge branch 'ui-dm-cam-freeze' into master-dev-c3-new 2025-01-17 14:37:39 -05:00
Jason Wen c08b18936d ui: driver view window should use uiStateSP 2025-01-17 14:37:23 -05:00
Jason Wen 5f94110b7a no need 2025-01-17 12:12:51 -05:00
Jason Wen bca0e7862f Merge branch 'sentry-sp' into master-dev-c3-new 2025-01-17 12:03:17 -05:00
Jason Wen 45c47d0c48 new endpoint 2025-01-17 12:01:11 -05:00
Jason Wen ab8dd45cda do this instead 2025-01-17 10:57:06 -05:00
Jason Wen 66eb3a2532 wrap them around 2025-01-17 10:47:25 -05:00
Jason Wen 59b68e42f6 don't use deprecated method 2025-01-17 10:29:17 -05:00
Jason Wen 976c9ac9bf typo 2025-01-17 02:40:27 -05:00
Jason Wen c65c8a535c no duplicate 2025-01-17 02:39:51 -05:00
Jason Wen 5e79eb774b Merge branch 'sentry-sp' into master-dev-c3-new 2025-01-17 02:06:22 -05:00
Jason Wen 0735fd5553 Merge branch 'dec-redo' into master-dev-c3-new 2025-01-17 02:06:17 -05:00
Jason Wen 5ad8b46158 cap 2025-01-17 02:04:59 -05:00
Jason Wen 3f4e372c34 remove 2025-01-17 01:55:15 -05:00
Jason Wen 7fde68ecf9 have to do this 2025-01-17 01:48:43 -05:00
Jason Wen df1ef23280 use deprecated 2025-01-17 01:41:54 -05:00
Jason Wen 378eb8a4d2 remove unused imports 2025-01-17 01:39:15 -05:00
rav4kumar 40259cd22a dec: faster ? 2025-01-16 23:25:55 -07:00
Kumar 3f29ccbd99 Update dec.py 2025-01-16 23:25:45 -07:00
Jason Wen 189e883116 no point 2025-01-17 01:23:04 -05:00
rav4kumar 01e06df542 dec: how good is FirstOrderFilter? 2025-01-16 23:16:54 -07:00
rav4kumar b1e8824187 revert smooht lead for now 2025-01-16 23:16:00 -07:00
Jason Wen cb1240846b sentry: log fingerprints and save exceptions 2025-01-17 01:15:22 -05:00
Jason Wen fa6ee806f5 Merge branch 'long-distance-btn-exp-mode' into master-dev-c3-new 2025-01-16 18:04:09 -05:00
Jason Wen fefb6a3a25 Merge branch 'dec-redo' into master-dev-c3-new 2025-01-16 18:04:03 -05:00
Jason Wen 13cb725652 Merge branch 'master-new' into long-distance-btn-exp-mode 2025-01-16 18:03:35 -05:00
Jason Wen b219e07ac1 Merge branch 'master-new' into dec-redo
# Conflicts:
#	selfdrive/car/card.py
2025-01-16 18:03:13 -05:00
Jason Wen 90d38a7968 fix tests 2025-01-16 14:24:27 -05:00
Jason Wen bb83e1fe8f Merge branch 'dec-redo' into master-dev-c3-new 2025-01-16 12:09:28 -05:00
Jason Wen baf0506033 use walrus (needs cleanup) 2025-01-16 12:08:15 -05:00
Jason Wen a3db53044e fix wrong typing and variable name 2025-01-16 12:00:20 -05:00
Jason Wen 8673c31e46 Revert "use walrus for None"
This reverts commit 5f2396d490.
2025-01-16 11:59:13 -05:00
Jason Wen 5f2396d490 use walrus for None 2025-01-16 11:56:07 -05:00
Jason Wen e2ff34417f Merge branch 'dec-redo' into master-dev-c3-new 2025-01-15 15:12:39 -05:00
Jason Wen 4ac4fc855f don't set to exp mode initial if DEC is active 2025-01-15 15:12:13 -05:00
Jason Wen cea20d1945 Merge branch 'long-distance-btn-exp-mode' into master-dev-c3-new 2025-01-15 13:41:45 -05:00
Jason Wen 353aa611e7 Merge branch 'dec-redo' into master-dev-c3-new
# Conflicts:
#	sunnypilot/selfdrive/controls/lib/longitudinal_planner.py
2025-01-15 13:41:22 -05:00
Jason Wen 391a3160a0 no need 2025-01-15 11:00:08 -05:00
Jason Wen 6b4ef00ae6 fix 2025-01-15 10:57:51 -05:00
Jason Wen d937062724 more 2025-01-15 01:51:22 -05:00
Jason Wen f94eeb9780 Revert "explicit type hints"
This reverts commit c205497b
2025-01-15 01:46:31 -05:00
Jason Wen c6474fc3ad move to constants py 2025-01-15 01:45:29 -05:00
Jason Wen d7f0e8dd04 unused 2025-01-15 01:28:15 -05:00
Jason Wen f3d94fc291 Longitudinal: Distance button hold to toggle Chill/Experimental Mode 2025-01-15 01:26:23 -05:00
Jason Wen c205497b15 explicit type hints 2025-01-15 00:18:47 -05:00
Jason Wen 45f3c70596 move around 2025-01-14 21:55:56 -05:00
Jason Wen 21793721cc rename 2025-01-14 21:29:42 -05:00
Jason Wen d01b02b185 more 2025-01-14 20:50:57 -05:00
Jason Wen f4af0aa422 update name 2025-01-14 16:42:41 -05:00
Jason Wen d3ecdbb850 fix dec engagement 2025-01-14 00:23:46 -05:00
Jason Wen 1f39c4ccfb remove to fail test 2025-01-14 00:04:48 -05:00
Jason Wen a56e1e6e69 slight cleanup 2025-01-14 00:01:31 -05:00
Jason Wen e630546250 more logs 2025-01-13 23:57:21 -05:00
Jason Wen 85faddc7af this is why it was never using DEC 2025-01-13 23:46:38 -05:00
Jason Wen 223abdc536 never used 2025-01-13 23:26:07 -05:00
Jason Wen dd12f86804 Merge branch 'master-new' into dec-redo
# Conflicts:
#	common/params.cc
2025-01-13 23:11:06 -05:00
Jason Wen dff2a5796d update name 2025-01-13 11:41:11 -05:00
Jason Wen 70918ccff3 Merge branch 'master-new' into dec-redo 2025-01-13 10:23:12 -05:00
Jason Wen f6ef036158 use internal frame 2025-01-12 18:57:22 -05:00
Jason Wen 67692babdc Revert "pass sm.frame from plannerd"
This reverts commit a8deaa69b8.
2025-01-12 18:55:38 -05:00
Jason Wen ea6cd3429b Revert "fix test"
This reverts commit 635b15f2bc.
2025-01-12 18:55:33 -05:00
Jason Wen 635b15f2bc fix test 2025-01-12 18:54:17 -05:00
Jason Wen b42c060b2e more explicit 2025-01-12 18:37:47 -05:00
Jason Wen 1c1ef06489 more 2025-01-12 18:28:20 -05:00
Jason Wen 1b2586914f more fixes 2025-01-12 18:24:58 -05:00
Jason Wen a8deaa69b8 pass sm.frame from plannerd 2025-01-12 18:20:06 -05:00
Jason Wen c34a21980e fix type hint 2025-01-12 18:13:52 -05:00
rav4kumar 6e86d242cd window time 2025-01-12 16:03:17 -07:00
rav4kumar 7113d4a76a smoother trans 2025-01-12 15:54:50 -07:00
Jason Wen 5074881d6d unused 2025-01-12 17:40:21 -05:00
Jason Wen bf3350b7f2 type hint 2025-01-12 17:38:49 -05:00
Jason Wen c64b679704 sync with stock 2025-01-12 17:16:34 -05:00
Jason Wen f645e2cdb0 check live param 2025-01-12 17:12:31 -05:00
Jason Wen 426d41a0a7 rename 2025-01-12 16:56:57 -05:00
Jason Wen de305a81d5 into their own 2025-01-12 16:52:40 -05:00
Jason Wen e4c29a2c58 stuff 2025-01-12 16:50:16 -05:00
Jason Wen c3fde45000 more cleanup 2025-01-12 16:48:44 -05:00
Jason Wen e7d27f0bb9 move around 2025-01-12 16:46:43 -05:00
Jason Wen 12981e02f7 static method 2025-01-12 16:46:15 -05:00
Jason Wen e1cf216c89 some more 2025-01-12 16:44:54 -05:00
Jason Wen cc507a5cd9 cleanup 2025-01-12 16:40:07 -05:00
rav4kumar 87ca1513f4 had moved to car_state 2025-01-12 14:29:22 -07:00
rav4kumar 1d422ce5cf static 2025-01-12 14:06:50 -07:00
rav4kumar cb04017945 Merge branch 'dec-new-refactored-rebased-no-ui' of https://github.com/sunnypilot/sunnypilot into dec-new-refactored-rebased-no-ui 2025-01-12 13:49:17 -07:00
rav4kumar 7d6c9d1a8c static test 2025-01-12 13:48:08 -07:00
Jason Wen ba6559d32a Merge branch 'master-new' into dec-new-refactored-rebased-no-ui 2025-01-12 15:40:47 -05:00
DevTekVE c0c74e3761 **Refactor DEC module structure for better organization**
Moved DEC-related files from `dec` to `lib` for improved clarity and consistency within the project structure. Updated all relevant import paths to reflect the new locations. Ensured functionality remains unaffected with these changes.
2025-01-12 21:03:38 +01:00
DevTekVE 898f5b89ed Refactor DEC into a dedicated longitudinal planner class
Move Dynamic Experimental Control (DEC) logic to a new `DecLongitudinalPlanner` class for better modularity and maintainability. This simplifies the `LongitudinalPlanner` by delegating DEC-specific behavior and consolidates related methods into a single file. Additionally, redundant code was removed to improve readability and reduce complexity.
2025-01-12 20:51:35 +01:00
DevTekVE f861e8d678 Format 2025-01-12 16:40:43 +01:00
DevTekVE fd6ec85e20 Refactor MpcSource definition and update references.
Moved MpcSource enum into LongitudinalPlanSP for better encapsulation. Updated references in helpers.py to use the new path. This change improves code organization and maintains functionality.
2025-01-12 16:40:20 +01:00
DevTekVE 4a46bccc20 rebase fix 2025-01-12 16:33:23 +01:00
DevTekVE 73e658bf8c Formatting 2025-01-12 16:33:23 +01:00
DevTekVE ec25ca070a Format 2025-01-12 16:33:23 +01:00
DevTekVE 84b6af094f Add missing import for messaging in helpers.py
The `messaging` module was added to resolve potential issues with undefined references. This change ensures all required imports are present, improving the reliability and maintainability of the code.
2025-01-12 16:33:23 +01:00
DevTekVE 55b6eae92e Refactor and modularize DynamicExperimentalController logic
Moved DynamicExperimentalController logic and helper functions to a dedicated module for better readability and maintainability. Simplified longitudinal planner logic by introducing reusable methods to manage MPC mode and longitudinal plan publishing. Adjusted file structure for dynamic controller-related components and updated relevant imports.
2025-01-12 16:33:22 +01:00
DevTekVE 760e7e847a new line... 2025-01-12 16:33:22 +01:00
DevTekVE 4b64f85f85 Replace unittest with pytest for dynamic controller tests
Migrated dynamic controller tests from unittest to pytest for improved readability and maintainability. Refactored mock setup using pytest fixtures and monkeypatching while preserving test coverage.
2025-01-12 16:33:22 +01:00
DevTekVE 8dd750fd83 Disabling unittest file to allow checks on the pipeline to succeed.
Pending to remove this, but leaving it to validate the move to pytest is okay before merging
2025-01-12 16:33:21 +01:00
DevTekVE 5e62ccad12 Integrate radar parameter into dynamic controller's pytest tests
Added a `has_radar` parameter to the test functions in the dynamic controller's pytest file. This allows each function to run both with and without radar inputs, thus enhancing the coverage of our test cases.
2025-01-12 16:33:21 +01:00
DevTekVE 2fb0545344 Migrated to pytest using claude 2025-01-12 16:33:21 +01:00
DevTekVE 684431e3f6 Refactor test indentation for dynamic controller tests
Adjust indentation and formatting in test_dynamic_controller.py to ensure consistency and readability. This change does not alter functionality but improves the maintainability of the test code.
2025-01-12 16:33:21 +01:00
DevTekVE ba9fbd52d7 Refactor test_dynamic_controller and fix formatting issues
Added a new import for STOP_AND_GO_FRAME and corrected a float initialization for v_ego in MockCarState. Also fixed indentation in the test_standstill_detection method for consistency.
2025-01-12 16:33:20 +01:00
rav4kumar 1dfaf347f8 unitee testt 2025-01-12 16:33:20 +01:00
rav4kumar 91922fb9b6 fix static test 2025-01-12 16:33:20 +01:00
rav4kumar c4ed5a4617 ff 2025-01-12 16:33:20 +01:00
rav4kumar 90435f0a92 fix static test 2025-01-12 16:33:19 +01:00
Kumar 3c434e78e2 Update sunnypilot/selfdrive/controls/lib/dynamic_experimental_controller.py
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
2025-01-12 16:33:19 +01:00
Kumar 75f6f73798 Update sunnypilot/selfdrive/controls/lib/dynamic_experimental_controller.py
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
2025-01-12 16:33:19 +01:00
rav4kumar c5524c6d42 init dec 2025-01-12 16:33:19 +01:00
88 changed files with 891 additions and 2720 deletions
-1
View File
@@ -1,3 +1,2 @@
Wen
REGIST
PullRequest
@@ -1,162 +0,0 @@
name: Nightly Branch Reset and PR Squash
env:
DEFAULT_SOURCE_BRANCH: "master-new"
DEFAULT_TARGET_BRANCH: "nightly"
PR_LABEL: "dev-c3"
on:
workflow_dispatch:
inputs:
source_branch:
description: 'Source branch to reset from'
required: true
default: 'master-new'
type: string
target_branch:
description: 'Target branch to reset and squash into'
required: true
default: 'master-dev-c3-new'
type: string
# schedule:
# - cron: '0 0 * * *' # Run at midnight UTC for nightly
jobs:
reset-and-squash:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all history for all branches
token: ${{ secrets.GITHUB_TOKEN }}
- name: Configure Git
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install PyGithub
- name: Check branches exist
run: |
# Check if source branch exists
if ! git ls-remote --heads origin ${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }} | grep -q "${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }}"; then
echo "Source branch ${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }} does not exist!"
exit 1
fi
# Make sure we have the latest source branch
git fetch origin ${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }}
# Check if target branch exists
if ! git ls-remote --heads origin ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} | grep -q "${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}"; then
echo "Target branch ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} does not exist, creating it from ${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }}"
git checkout -b ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} origin/${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }}
git push origin ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}
else
# Fetch target branch if it exists
git fetch origin ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}
fi
- name: Reset target branch
run: |
echo "Resetting ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} to match ${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }}"
# Delete if exists and recreate pointing to source
git branch -D ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} || true
git branch ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} origin/${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }}
- name: Get PRs to squash
id: get-prs
run: |
# Use GitHub API to get PRs with specific label, ordered by creation date
PR_LIST=$(gh api graphql -f query='
query($label:String!) {
search(query: $label, type:ISSUE, first:100) {
nodes {
... on PullRequest {
number
headRefName
title
createdAt
commits(last: 1) {
nodes {
commit {
statusCheckRollup {
state
}
}
}
}
}
}
}
}' -F label="is:pr is:open label:${PR_LABEL} sort:created-asc")
echo "PR_LIST=${PR_LIST}" >> $GITHUB_OUTPUT
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Process PRs
run: |
python3 ${{ github.workspace }}/release/ci/squash_and_merge_prs.py \
--pr-data '${{ steps.get-prs.outputs.PR_LIST }}' \
--target-branch ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} \
--squash-script-path '${{ github.workspace }}/release/ci/squash_and_merge.py'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Push changes if there are diffs
id: push-changes # Add an id so we can reference this step
run: |
TARGET_BRANCH="${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}"
# Fetch the latest from remote
git fetch origin $TARGET_BRANCH
# Check for diffs between local and remote
if git diff $TARGET_BRANCH origin/$TARGET_BRANCH --quiet; then
echo "No changes to push - local and remote branches are identical"
echo "has_changes=false" >> $GITHUB_OUTPUT
exit 0
fi
# If we get here, there are diffs, so push
if ! git push origin $TARGET_BRANCH --force; then
echo "Failed to push changes to $TARGET_BRANCH"
exit 1
fi
echo "Branch $TARGET_BRANCH has been reset and updated with squashed PRs"
echo "has_changes=true" >> $GITHUB_OUTPUT
- name: Trigger and wait for selfdrive tests
if: steps.push-changes.outputs.has_changes == 'true'
run: |
echo "Triggering selfdrive tests..."
gh workflow run selfdrive_tests.yaml --ref "${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}"
echo "Sleeping for 120s to give plenty of time for the action to start and then we wait"
sleep 120
echo "Getting latest run ID..."
RUN_ID=$(gh run list --workflow=selfdrive_tests.yaml --branch="${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}" --limit=1 --json databaseId --jq '.[0].databaseId')
echo "Watching run ID: $RUN_ID"
gh run watch "$RUN_ID"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Trigger prebuilt workflow
if: success() && steps.push-changes.outputs.has_changes == 'true'
run: |
gh workflow run sunnypilot-build-prebuilt.yaml --ref "${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+1 -1
View File
@@ -86,7 +86,7 @@ jobs:
run: >-
sudo apt-get install -y imagemagick
scenes="homescreen settings_device settings_network settings_network_advanced settings_software settings_sunnylink settings_toggles settings_sunnypilot settings_sunnypilot_mads settings_trips settings_vehicle settings_developer offroad_alert update_available prime onroad onroad_disengaged onroad_override onroad_sidebar onroad_wide onroad_wide_sidebar onroad_alert_small onroad_alert_mid onroad_alert_full driver_camera body keyboard keyboard_uppercase"
scenes="homescreen settings_device settings_software settings_sunnylink settings_toggles settings_sunnypilot settings_sunnypilot_mads settings_trips settings_developer offroad_alert update_available prime onroad onroad_disengaged onroad_override onroad_sidebar onroad_wide onroad_wide_sidebar onroad_alert_small onroad_alert_mid onroad_alert_full driver_camera body keyboard keyboard_uppercase"
A=($scenes)
DIFF=""
+18 -60
View File
@@ -10,23 +10,23 @@ $Cxx.namespace("cereal");
# DO rename the structs
# DON'T change the identifier (e.g. @0x81c2f05a394cf4af)
struct ModularAssistiveDrivingSystem {
state @0 :ModularAssistiveDrivingSystemState;
enabled @1 :Bool;
active @2 :Bool;
available @3 :Bool;
enum ModularAssistiveDrivingSystemState {
disabled @0;
paused @1;
enabled @2;
softDisabling @3;
overriding @4;
}
}
struct SelfdriveStateSP @0x81c2f05a394cf4af {
mads @0 :ModularAssistiveDrivingSystem;
struct ModularAssistiveDrivingSystem {
state @0 :ModularAssistiveDrivingSystemState;
enabled @1 :Bool;
active @2 :Bool;
available @3 :Bool;
enum ModularAssistiveDrivingSystemState {
disabled @0;
paused @1;
enabled @2;
softDisabling @3;
overriding @4;
}
}
}
struct ModelManagerSP @0xaedffd8f31e7b55d {
@@ -81,13 +81,11 @@ struct ModelManagerSP @0xaedffd8f31e7b55d {
generation @5 :UInt32;
environment @6 :Text;
runner @7 :Runner;
is20hz @8 :Bool;
}
}
struct LongitudinalPlanSP @0xf35cc4560bbf6ec2 {
dec @0 :DynamicExperimentalControl;
accelPersonality @1 :AccelerationPersonality;
struct DynamicExperimentalControl {
state @0 :DynamicExperimentalControlState;
@@ -99,55 +97,15 @@ struct LongitudinalPlanSP @0xf35cc4560bbf6ec2 {
blended @1;
}
}
enum AccelerationPersonality {
sport @0;
normal @1;
eco @2;
stock @3;
}
}
struct OnroadEventSP @0xda96579883444c35 {
name @0 :EventName;
# event types
enable @1 :Bool;
noEntry @2 :Bool;
warning @3 :Bool; # alerts presented only when enabled or soft disabling
userDisable @4 :Bool;
softDisable @5 :Bool;
immediateDisable @6 :Bool;
preEnable @7 :Bool;
permanent @8 :Bool; # alerts presented regardless of openpilot state
overrideLateral @10 :Bool;
overrideLongitudinal @9 :Bool;
enum EventName {
lkasEnable @0;
lkasDisable @1;
manualSteeringRequired @2;
manualLongitudinalRequired @3;
silentLkasEnable @4;
silentLkasDisable @5;
silentBrakeHold @6;
silentWrongGear @7;
silentReverseGear @8;
silentDoorOpen @9;
silentSeatbeltNotLatched @10;
silentParkBrake @11;
controlsMismatchLateral @12;
hyundaiRadarTracksConfirmed @13;
experimentalModeSwitched @14;
}
struct CustomReserved3 @0xda96579883444c35 {
}
struct CarParamsSP @0x80ae746ee2596b11 {
flags @0 :UInt32; # flags for car specific quirks in sunnypilot
struct CustomReserved4 @0x80ae746ee2596b11 {
}
struct CarControlSP @0xa5cd762cd951a455 {
mads @0 :ModularAssistiveDrivingSystem;
struct CustomReserved5 @0xa5cd762cd951a455 {
}
struct CustomReserved6 @0xf98d843bfd7004a3 {
+79 -4
View File
@@ -125,6 +125,81 @@ struct OnroadEvent @0xc4fa6047f024e718 {
espActive @90;
personalityChanged @91;
aeb @92;
eventReserved93 @93;
eventReserved94 @94;
eventReserved95 @95;
eventReserved96 @96;
eventReserved97 @97;
eventReserved98 @98;
eventReserved99 @99;
eventReserved100 @100;
eventReserved101 @101;
eventReserved102 @102;
eventReserved103 @103;
eventReserved104 @104;
eventReserved105 @105;
eventReserved106 @106;
eventReserved107 @107;
eventReserved108 @108;
eventReserved109 @109;
eventReserved110 @110;
eventReserved111 @111;
eventReserved112 @112;
eventReserved113 @113;
eventReserved114 @114;
eventReserved115 @115;
eventReserved116 @116;
eventReserved117 @117;
eventReserved118 @118;
eventReserved119 @119;
eventReserved120 @120;
eventReserved121 @121;
eventReserved122 @122;
eventReserved123 @123;
eventReserved124 @124;
eventReserved125 @125;
eventReserved126 @126;
eventReserved127 @127;
eventReserved128 @128;
eventReserved129 @129;
eventReserved130 @130;
eventReserved131 @131;
eventReserved132 @132;
eventReserved133 @133;
eventReserved134 @134;
eventReserved135 @135;
eventReserved136 @136;
eventReserved137 @137;
eventReserved138 @138;
eventReserved139 @139;
eventReserved140 @140;
eventReserved141 @141;
eventReserved142 @142;
eventReserved143 @143;
eventReserved144 @144;
eventReserved145 @145;
eventReserved146 @146;
eventReserved147 @147;
eventReserved148 @148;
eventReserved149 @149;
eventReserved150 @150;
# sunnypilot
lkasEnable @151;
lkasDisable @152;
manualSteeringRequired @153;
manualLongitudinalRequired @154;
silentLkasEnable @155;
silentLkasDisable @156;
silentBrakeHold @157;
silentWrongGear @158;
silentReverseGear @159;
silentDoorOpen @160;
silentSeatbeltNotLatched @161;
silentParkBrake @162;
controlsMismatchLateral @163;
hyundaiRadarTracksConfirmed @164;
experimentalModeSwitched @165;
soundsUnavailableDEPRECATED @47;
}
@@ -590,6 +665,7 @@ struct PandaState @0xa7649e2575e4591e {
# safety stuff
controlsAllowed @3 :Bool;
controlsAllowedLat @5 :Bool;
safetyRxInvalid @19 :UInt32;
safetyTxBlocked @24 :UInt32;
safetyModel @14 :Car.CarParams.SafetyModel;
@@ -697,7 +773,6 @@ struct PandaState @0xa7649e2575e4591e {
}
gasInterceptorDetectedDEPRECATED @4 :Bool;
startedSignalDetectedDEPRECATED @5 :Bool;
hasGpsDEPRECATED @6 :Bool;
gmlanSendErrsDEPRECATED @9 :UInt32;
fanSpeedRpmDEPRECATED @11 :UInt16;
@@ -2568,9 +2643,9 @@ struct Event {
selfdriveStateSP @107 :Custom.SelfdriveStateSP;
modelManagerSP @108 :Custom.ModelManagerSP;
longitudinalPlanSP @109 :Custom.LongitudinalPlanSP;
onroadEventsSP @110 :List(Custom.OnroadEventSP);
carParamsSP @111 :Custom.CarParamsSP;
carControlSP @112 :Custom.CarControlSP;
customReserved3 @110 :Custom.CustomReserved3;
customReserved4 @111 :Custom.CustomReserved4;
customReserved5 @112 :Custom.CustomReserved5;
customReserved6 @113 :Custom.CustomReserved6;
customReserved7 @114 :Custom.CustomReserved7;
customReserved8 @115 :Custom.CustomReserved8;
-3
View File
@@ -78,9 +78,6 @@ _services: dict[str, tuple] = {
"modelManagerSP": (False, 1., 1),
"selfdriveStateSP": (True, 100., 10),
"longitudinalPlanSP": (True, 20., 10),
"onroadEventsSP": (True, 1., 1),
"carParamsSP": (True, 0.02, 1),
"carControlSP": (True, 100., 10),
# debug
"uiDebug": (True, 0., 1),
-6
View File
@@ -202,13 +202,8 @@ std::unordered_map<std::string, uint32_t> keys = {
// --- sunnypilot params --- //
{"ApiCache_DriveStats", PERSISTENT},
{"CarParamsSP", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
{"CarParamsSPCache", CLEAR_ON_MANAGER_START},
{"CarParamsSPPersistent", PERSISTENT},
{"EnableGithubRunner", PERSISTENT | BACKUP},
{"ModelRunnerTypeCache", CLEAR_ON_ONROAD_TRANSITION},
{"OffroadMode", CLEAR_ON_MANAGER_START},
{"OffroadMode_Status", CLEAR_ON_MANAGER_START},
// MADS params
{"Mads", PERSISTENT | BACKUP},
@@ -236,7 +231,6 @@ std::unordered_map<std::string, uint32_t> keys = {
{"HyundaiRadarTracksToggle", PERSISTENT},
{"DynamicExperimentalControl", PERSISTENT},
{"AccelPersonality", PERSISTENT},
};
} // namespace
+1 -1
Submodule panda updated: 84836fd802...d3252abcc4
+6 -6
View File
@@ -117,7 +117,7 @@ def workspace_manager(original_branch: str):
if temp_branch:
run_command(f"git branch -D {temp_branch}")
print("\nOperation interrupted, but changes were already restored.")
sys.exit(3)
sys.exit(1)
# First, switch back to original branch
current = get_current_branch()
@@ -139,12 +139,12 @@ def workspace_manager(original_branch: str):
if signum:
print("\nOperation interrupted. Cleaned up and restored original state.")
sys.exit(4)
sys.exit(1)
except Exception as e:
print(f"Error during cleanup: {e}")
if signum:
sys.exit(5)
sys.exit(1)
try:
# Set up signal handlers
@@ -275,7 +275,7 @@ def squash_and_merge(source_branch: str, target_branch: str, manual_title: str |
return False
print(f"Attempting to merge changes from {temp_branch}...")
code, _, error = run_command(f"git rebase {temp_branch}")
code, _, error = run_command(f"git merge {temp_branch}")
if code != 0:
print(f"\nMerge failed with error: {error}")
@@ -344,7 +344,7 @@ def main():
parser.add_argument('--push', action='store_true',
help='Push changes to remote after squashing')
args, unknown = parser.parse_known_args()
args = parser.parse_args()
# Determine source branch early
source_branch = args.source
@@ -354,7 +354,7 @@ def main():
sys.exit(1)
if not squash_and_merge(source_branch, args.target, args.title, args.backup, args.push):
sys.exit(2)
sys.exit(1)
if __name__ == "__main__":
-163
View File
@@ -1,163 +0,0 @@
#!/usr/bin/env python3
import subprocess
import sys
import os
import argparse
import json
from datetime import datetime
def setup_argument_parser():
parser = argparse.ArgumentParser(description='Process and squash GitHub PRs')
parser.add_argument('--pr-data', type=str, help='PR data in JSON format')
parser.add_argument('--source-branch', type=str, default='master-new',
help='Source branch for merging')
parser.add_argument('--target-branch', type=str, default='master-dev-c3-new-test',
help='Target branch for merging')
parser.add_argument('--squash-script-path', type=str, required=True,
help='Path to the squash_and_merge.py script')
return parser
def validate_squash_script(script_path):
if not os.path.isfile(script_path):
raise FileNotFoundError(f"Squash script not found at: {script_path}")
if not os.access(script_path, os.X_OK):
raise PermissionError(f"Squash script is not executable: {script_path}")
def sort_prs_by_creation(pr_data):
"""Sort PRs by creation date"""
nodes = (pr_data.get('data', {}).get('search', {}).get('nodes', []))
return sorted(
nodes,
key=lambda x: datetime.fromisoformat(x.get('createdAt', '').replace('Z', '+00:00'))
)
def add_pr_comment(pr_number, comment):
"""Add a comment to a PR using gh cli"""
try:
subprocess.run(
['gh', 'pr', 'comment', str(pr_number), '--body', comment],
check=True,
capture_output=True,
text=True
)
except subprocess.CalledProcessError as e:
print(f"Failed to add comment to PR #{pr_number}: {e.stderr}")
def validate_pr(pr):
"""Validate a PR and return (is_valid, skip_reason)"""
pr_number = pr.get('number', 'UNKNOWN')
branch = pr.get('headRefName', '')
if not branch:
return False, f"Missing branch name for PR #{pr_number}"
# Check if checks have passed
commits = pr.get('commits', {}).get('nodes', [])
if not commits:
return False, "No commit data found"
status = commits[0].get('commit', {}).get('statusCheckRollup', {})
if not status or status.get('state') != 'SUCCESS':
return False, "Not all checks have passed"
# Check for merge conflicts
merge_status = subprocess.run(['gh', 'pr', 'view', str(pr_number), '--json', 'mergeable,mergeStateStatus'], capture_output=True, text=True)
merge_data = json.loads(merge_status.stdout)
if not merge_data.get('mergeable'):
return False, "Merge conflicts detected"
if (mergeStateStatus := merge_data.get('mergeStateStatus')) == "BEHIND":
return False, f"Branch is `{mergeStateStatus}`"
return True, None
def process_pr(pr_data, source_branch, target_branch, squash_script_path):
try:
nodes = sort_prs_by_creation(pr_data)
if not nodes:
print("No PRs to squash")
return 0
print(f"Deleting target branch {target_branch}")
subprocess.run(['git', 'branch', '-D', target_branch], check=False)
subprocess.run(['git', 'branch', target_branch, f'origin/{source_branch}'], check=True)
success_count = 0
for pr in nodes:
pr_number = pr.get('number', 'UNKNOWN')
branch = pr.get('headRefName', '')
title = pr.get('title', '')
is_valid, skip_reason = validate_pr(pr)
if not is_valid:
print(f"Warning: {skip_reason} for PR #{pr_number}, skipping")
add_pr_comment(pr_number, f"⚠️ This PR was skipped in the automated `{target_branch}` squash because {skip_reason}.")
continue
try:
# Fetch PR branch
subprocess.run(['git', 'fetch', 'origin', branch], check=True)
# Delete branch if it exists (ignore errors if it doesn't)
subprocess.run(['git', 'branch', '-D', branch], check=False)
# Create new branch pointing to origin's branch
subprocess.run(['git', 'branch', branch, f'origin/{branch}'], check=True)
# Run squash script
subprocess.run([
squash_script_path,
'--target', target_branch,
'--source', branch,
'--title', f"{title} (#{pr_number})",
], check=True)
print(f"Successfully processed PR #{pr_number}")
success_count += 1
except subprocess.CalledProcessError as e:
print(f"Error processing PR #{pr_number}:")
print(f"Command failed with exit code {e.returncode}")
error_output = getattr(e, 'stderr', 'No error output available')
print(f"Error output: {error_output}")
add_pr_comment(pr_number, f"⚠️ Error during automated {target_branch} squash:\n```\n{error_output}\n```")
continue
except Exception as e:
print(f"Unexpected error processing PR #{pr_number}: {str(e)}")
continue
return success_count
except Exception as e:
import traceback
print(f"Error in process_pr: {str(e)}")
print("Full traceback:")
print(traceback.format_exc())
return 0
def main():
parser = setup_argument_parser()
try:
args = parser.parse_args()
validate_squash_script(args.squash_script_path)
pr_data_json = json.loads(args.pr_data)
# Process the PRs
success_count = process_pr(pr_data_json, args.source_branch, args.target_branch, args.squash_script_path)
print(f"Successfully processed {success_count} PRs")
except Exception as e:
print(f"Fatal error: {str(e)}", file=sys.stderr)
return 1
return 0
if __name__ == "__main__":
sys.exit(main())
+12 -31
View File
@@ -5,7 +5,7 @@ import threading
import cereal.messaging as messaging
from cereal import car, log, custom
from cereal import car, log
from panda import ALTERNATIVE_EXPERIENCE
@@ -21,7 +21,6 @@ from opendbc.car.interfaces import CarInterfaceBase, RadarInterfaceBase
from openpilot.selfdrive.pandad import can_capnp_to_list, can_list_to_can_capnp
from openpilot.selfdrive.car.cruise import VCruiseHelper
from openpilot.selfdrive.car.car_specific import MockCarState
from openpilot.selfdrive.car.helpers import convert_carControlSP, convert_to_capnp
from openpilot.sunnypilot.mads.mads import MadsParams
from openpilot.sunnypilot.selfdrive.car import interfaces
@@ -67,13 +66,11 @@ class Car:
CI: CarInterfaceBase
RI: RadarInterfaceBase
CP: car.CarParams
CP_SP: structs.CarParamsSP
CP_SP_capnp: custom.CarParamsSP
def __init__(self, CI=None, RI=None) -> None:
self.can_sock = messaging.sub_sock('can', timeout=20)
self.sm = messaging.SubMaster(['pandaStates', 'carControl', 'onroadEvents'] + ['carControlSP'])
self.pm = messaging.PubMaster(['sendcan', 'carState', 'carParams', 'carOutput', 'liveTracks'] + ['carParamsSP'])
self.sm = messaging.SubMaster(['pandaStates', 'carControl', 'onroadEvents'])
self.pm = messaging.PubMaster(['sendcan', 'carState', 'carParams', 'carOutput', 'liveTracks'])
self.can_rcv_cum_timeout_counter = 0
@@ -105,15 +102,14 @@ class Car:
cached_params = _cached_params
self.CI = get_car(*self.can_callbacks, obd_callback(self.params), experimental_long_allowed, num_pandas, cached_params)
interfaces.setup_car_interface_sp(self.CI.CP, self.CI.CP_SP, self.params)
self.RI = get_radar_interface(self.CI.CP, self.CI.CP_SP)
interfaces.setup_car_interface_sp(self.CI.CP, self.params)
self.RI = get_radar_interface(self.CI.CP)
self.CP = self.CI.CP
self.CP_SP = self.CI.CP_SP
# continue onto next fingerprinting step in pandad
self.params.put_bool("FirmwareQueryDone", True)
else:
self.CI, self.CP, self.CP_SP = CI, CI.CP, CI.CP_SP
self.CI, self.CP = CI, CI.CP
self.RI = RI
# set alternative experiences from parameters
@@ -124,7 +120,7 @@ class Car:
# mads
MadsParams().set_alternative_experience(self.CP)
MadsParams().set_car_specific_params(self.CP, self.CP_SP)
MadsParams().set_car_specific_params(self.CP)
# Dynamic Experimental Control
self.dynamic_experimental_control = self.params.get_bool("DynamicExperimentalControl")
@@ -171,14 +167,6 @@ class Car:
self.params.put_nonblocking("CarParamsCache", cp_bytes)
self.params.put_nonblocking("CarParamsPersistent", cp_bytes)
# Write CarParamsSP for controls
# convert to pycapnp representation for caching and logging
self.CP_SP_capnp = convert_to_capnp(self.CP_SP)
cp_sp_bytes = self.CP_SP_capnp.to_bytes()
self.params.put("CarParamsSP", cp_sp_bytes)
self.params.put_nonblocking("CarParamsSPCache", cp_sp_bytes)
self.params.put_nonblocking("CarParamsSPPersistent", cp_sp_bytes)
self.mock_carstate = MockCarState()
self.v_cruise_helper = VCruiseHelper(self.CP)
@@ -257,28 +245,21 @@ class Car:
tracks_msg.liveTracks = RD
self.pm.send('liveTracks', tracks_msg)
# carParamsSP - logged every 50 seconds (> 1 per segment)
if self.sm.frame % int(50. / DT_CTRL) == 0:
cp_sp_send = messaging.new_message('carParamsSP')
cp_sp_send.valid = True
cp_sp_send.carParamsSP = self.CP_SP_capnp
self.pm.send('carParamsSP', cp_sp_send)
def controls_update(self, CS: car.CarState, CC: car.CarControl, CC_SP: custom.CarControlSP):
def controls_update(self, CS: car.CarState, CC: car.CarControl):
"""control update loop, driven by carControl"""
if not self.initialized_prev:
# Initialize CarInterface, once controls are ready
# TODO: this can make us miss at least a few cycles when doing an ECU knockout
self.CI.init(self.CP, self.CP_SP, *self.can_callbacks)
interfaces.initialize_car_interface_sp(self.CP, self.CP_SP, self.params, *self.can_callbacks)
self.CI.init(self.CP, *self.can_callbacks)
interfaces.initialize_car_interface_sp(self.CP, self.params, *self.can_callbacks)
# signal pandad to switch to car safety mode
self.params.put_bool_nonblocking("ControlsReady", True)
if self.sm.all_alive(['carControl']):
# send car controls over can
now_nanos = self.can_log_mono_time if REPLAY else int(time.monotonic() * 1e9)
self.last_actuators_output, can_sends = self.CI.apply(CC, convert_carControlSP(CC_SP), now_nanos)
self.last_actuators_output, 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.CC_prev = CC
@@ -291,7 +272,7 @@ class Car:
initialized = (not any(e.name == EventName.selfdriveInitializing for e in self.sm['onroadEvents']) and
self.sm.seen['onroadEvents'])
if not self.CP.passive and initialized:
self.controls_update(CS, self.sm['carControl'], self.sm['carControlSP'])
self.controls_update(CS, self.sm['carControl'])
self.initialized_prev = initialized
self.CS_prev = CS
-59
View File
@@ -1,59 +0,0 @@
import capnp
from typing import Any
from cereal import custom
from opendbc.car import structs
_FIELDS = '__dataclass_fields__' # copy of dataclasses._FIELDS
def is_dataclass(obj):
"""Similar to dataclasses.is_dataclass without instance type check checking"""
return hasattr(obj, _FIELDS)
def _asdictref_inner(obj) -> dict[str, Any] | Any:
if is_dataclass(obj):
ret = {}
for field in getattr(obj, _FIELDS): # similar to dataclasses.fields()
ret[field] = _asdictref_inner(getattr(obj, field))
return ret
elif isinstance(obj, (tuple, list)):
return type(obj)(_asdictref_inner(v) for v in obj)
else:
return obj
def asdictref(obj) -> dict[str, Any]:
"""
Similar to dataclasses.asdict without recursive type checking and copy.deepcopy
Note that the resulting dict will contain references to the original struct as a result
"""
if not is_dataclass(obj):
raise TypeError("asdictref() should be called on dataclass instances")
return _asdictref_inner(obj)
def convert_to_capnp(struct: structs.CarParamsSP) -> capnp.lib.capnp._DynamicStructBuilder:
struct_dict = asdictref(struct)
if isinstance(struct, structs.CarParamsSP):
struct_capnp = custom.CarParamsSP.new_message(**struct_dict)
else:
raise ValueError(f"Unsupported struct type: {type(struct)}")
return struct_capnp
def convert_carControlSP(struct: capnp.lib.capnp._DynamicStructReader) -> structs.CarControlSP:
# TODO: recursively handle any car struct as needed
def remove_deprecated(s: dict) -> dict:
return {k: v for k, v in s.items() if not k.endswith('DEPRECATED')}
struct_dict = struct.to_dict()
struct_dataclass = structs.CarControlSP(**remove_deprecated({k: v for k, v in struct_dict.items() if not isinstance(k, dict)}))
struct_dataclass.mads = structs.ModularAssistiveDrivingSystem(**remove_deprecated(struct_dict.get('mads', {})))
return struct_dataclass
+4 -11
View File
@@ -4,7 +4,7 @@ import hypothesis.strategies as st
from hypothesis import Phase, given, settings
from parameterized import parameterized
from cereal import car, custom
from cereal import car
from opendbc.car import DT_CTRL
from opendbc.car.car_helpers import interfaces
from opendbc.car.structs import CarParams
@@ -12,7 +12,6 @@ from opendbc.car.tests.test_car_interfaces import get_fuzzy_car_interface_args
from opendbc.car.fingerprints import all_known_cars
from opendbc.car.fw_versions import FW_VERSIONS, FW_QUERY_CONFIGS
from opendbc.car.mock.values import CAR as MOCK
from openpilot.selfdrive.car.helpers import convert_carControlSP
from openpilot.selfdrive.controls.lib.latcontrol_angle import LatControlAngle
from openpilot.selfdrive.controls.lib.latcontrol_pid import LatControlPID
from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque
@@ -41,12 +40,9 @@ class TestCarInterfaces:
car_params = CarInterface.get_params(car_name, args['fingerprints'], args['car_fw'],
experimental_long=args['experimental_long'], docs=False)
car_params_sp = CarInterface.get_params_sp(car_params, car_name, args['fingerprints'], args['car_fw'],
experimental_long=args['experimental_long'], docs=False)
car_params = car_params.as_reader()
car_interface = CarInterface(car_params, car_params_sp, CarController, CarState)
car_interface = CarInterface(car_params, CarController, CarState)
assert car_params
assert car_params_sp
assert car_interface
assert car_params.mass > 1
@@ -73,16 +69,13 @@ class TestCarInterfaces:
assert not math.isnan(tune.torque.friction) and tune.torque.friction > 0
cc_msg = FuzzyGenerator.get_random_msg(data.draw, car.CarControl, real_floats=True)
cc_sp_msg = FuzzyGenerator.get_random_msg(data.draw, custom.CarControlSP, real_floats=True)
# Run car interface
now_nanos = 0
CC = car.CarControl.new_message(**cc_msg)
CC = CC.as_reader()
CC_SP = custom.CarControlSP.new_message(**cc_sp_msg)
CC_SP = convert_carControlSP(CC_SP.as_reader())
for _ in range(10):
car_interface.update([])
car_interface.apply(CC, CC_SP, now_nanos)
car_interface.apply(CC, now_nanos)
now_nanos += DT_CTRL * 1e9 # 10 ms
CC = car.CarControl.new_message(**cc_msg)
@@ -90,7 +83,7 @@ class TestCarInterfaces:
CC = CC.as_reader()
for _ in range(10):
car_interface.update([])
car_interface.apply(CC, CC_SP, now_nanos)
car_interface.apply(CC, now_nanos)
now_nanos += DT_CTRL * 1e9 # 10ms
# Test controller initialization
+10 -15
View File
@@ -1,5 +1,4 @@
import capnp
import copy
import os
import pytest
import random
@@ -159,9 +158,7 @@ class TestCarModelBase(unittest.TestCase):
cls.CarInterface, cls.CarController, cls.CarState, cls.RadarInterface = interfaces[cls.platform]
cls.CP = cls.CarInterface.get_params(cls.platform, cls.fingerprint, car_fw, experimental_long, docs=False)
cls.CP_SP = cls.CarInterface.get_params_sp(cls.CP, cls.platform, cls.fingerprint, car_fw, experimental_long, docs=False)
assert cls.CP
assert cls.CP_SP
assert cls.CP.carFingerprint == cls.platform
os.environ["COMMA_CACHE"] = DEFAULT_DOWNLOAD_CACHE_ROOT
@@ -171,7 +168,7 @@ class TestCarModelBase(unittest.TestCase):
del cls.can_msgs
def setUp(self):
self.CI = self.CarInterface(self.CP.copy(), copy.deepcopy(self.CP_SP), self.CarController, self.CarState)
self.CI = self.CarInterface(self.CP.copy(), self.CarController, self.CarState)
assert self.CI
Params().put_bool("OpenpilotEnabledToggle", self.openpilot_enabled)
@@ -205,11 +202,10 @@ class TestCarModelBase(unittest.TestCase):
can_invalid_cnt = 0
can_valid = False
CC = structs.CarControl().as_reader()
CC_SP = structs.CarControlSP()
for i, msg in enumerate(self.can_msgs):
CS = self.CI.update(can_capnp_to_list((msg.as_builder().to_bytes(),)))
self.CI.apply(CC, CC_SP, msg.logMonoTime)
self.CI.apply(CC, msg.logMonoTime)
if CS.canValid:
can_valid = True
@@ -221,7 +217,7 @@ class TestCarModelBase(unittest.TestCase):
self.assertEqual(can_invalid_cnt, 0)
def test_radar_interface(self):
RI = self.RadarInterface(self.CP, self.CP_SP)
RI = self.RadarInterface(self.CP)
assert RI
# Since OBD port is multiplexed to bus 1 (commonly radar bus) while fingerprinting,
@@ -278,13 +274,13 @@ class TestCarModelBase(unittest.TestCase):
if self.CP.notCar:
self.skipTest("Skipping test for notCar")
def test_car_controller(car_control, car_control_sp):
def test_car_controller(car_control):
now_nanos = 0
msgs_sent = 0
CI = self.CarInterface(self.CP, self.CP_SP, self.CarController, self.CarState)
CI = self.CarInterface(self.CP, self.CarController, self.CarState)
for _ in range(round(10.0 / DT_CTRL)): # make sure we hit the slowest messages
CI.update([])
_, sendcan = CI.apply(car_control, car_control_sp, now_nanos)
_, sendcan = CI.apply(car_control, now_nanos)
now_nanos += DT_CTRL * 1e9
msgs_sent += len(sendcan)
@@ -297,18 +293,17 @@ class TestCarModelBase(unittest.TestCase):
# Make sure we can send all messages while inactive
CC = structs.CarControl()
CC_SP = structs.CarControlSP()
test_car_controller(CC.as_reader(), CC_SP)
test_car_controller(CC.as_reader())
# Test cancel + general messages (controls_allowed=False & cruise_engaged=True)
self.safety.set_cruise_engaged_prev(True)
CC = structs.CarControl(cruiseControl=structs.CarControl.CruiseControl(cancel=True))
test_car_controller(CC.as_reader(), CC_SP)
test_car_controller(CC.as_reader())
# Test resume + general messages (controls_allowed=True & cruise_engaged=True)
self.safety.set_controls_allowed(True)
CC = structs.CarControl(cruiseControl=structs.CarControl.CruiseControl(resume=True))
test_car_controller(CC.as_reader(), CC_SP)
test_car_controller(CC.as_reader())
# Skip stdout/stderr capture with pytest, causes elevated memory usage
@pytest.mark.nocapture
@@ -392,7 +387,7 @@ class TestCarModelBase(unittest.TestCase):
controls_allowed_prev = False
CS_prev = car.CarState.new_message()
checks = defaultdict(int)
selfdrived = SelfdriveD(CP=self.CP, CP_SP=self.CP_SP)
selfdrived = SelfdriveD(CP=self.CP)
selfdrived.initialized = True
for idx, can in enumerate(self.can_msgs):
CS = self.CI.update(can_capnp_to_list((can.as_builder().to_bytes(), ))).as_reader()
+15 -22
View File
@@ -2,7 +2,7 @@
import math
from typing import SupportsFloat
from cereal import car, log, custom
from cereal import car, log
import cereal.messaging as messaging
from openpilot.common.conversions import Conversions as CV
from openpilot.common.params import Params
@@ -19,6 +19,8 @@ from openpilot.selfdrive.controls.lib.longcontrol import LongControl
from openpilot.selfdrive.controls.lib.vehicle_model import VehicleModel
from openpilot.selfdrive.locationd.helpers import PoseCalibrator, Pose
from opendbc.sunnypilot import SunnypilotParamFlags
State = log.SelfdriveState.OpenpilotState
LaneChangeState = log.LaneChangeState
LaneChangeDirection = log.LaneChangeDirection
@@ -32,17 +34,12 @@ class Controls:
self.CP = messaging.log_from_bytes(self.params.get("CarParams", block=True), car.CarParams)
cloudlog.info("controlsd got CarParams")
cloudlog.info("controlsd is waiting for CarParamsSP")
self.CP_SP = messaging.log_from_bytes(self.params.get("CarParamsSP", block=True), custom.CarParamsSP)
cloudlog.info("controlsd got CarParamsSP")
self.CI = get_car_interface(self.CP, self.CP_SP)
self.CI = get_car_interface(self.CP)
self.sm = messaging.SubMaster(['liveParameters', 'liveTorqueParameters', 'modelV2', 'selfdriveState',
'liveCalibration', 'livePose', 'longitudinalPlan', 'carState', 'carOutput',
'driverMonitoringState', 'onroadEvents', 'driverAssistance'] + ['selfdriveStateSP'],
poll='selfdriveState')
self.pm = messaging.PubMaster(['carControl', 'controlsState'] + ['carControlSP'])
'driverMonitoringState', 'onroadEvents', 'driverAssistance'], poll='selfdriveState')
self.pm = messaging.PubMaster(['carControl', 'controlsState'])
self.steer_limited = False
self.desired_curvature = 0.0
@@ -60,6 +57,9 @@ class Controls:
elif self.CP.lateralTuning.which() == 'torque':
self.LaC = LatControlTorque(self.CP, self.CI)
data_services = list(self.sm.data.keys()) + ['selfdriveStateSP']
self.sm = messaging.SubMaster(data_services, poll='selfdriveState')
def update(self):
self.sm.update(15)
if self.sm.updated["liveCalibration"]:
@@ -94,7 +94,9 @@ class Controls:
standstill = abs(CS.vEgo) <= max(self.CP.minSteerSpeed, MIN_LATERAL_CONTROL_SPEED) or CS.standstill
ss_sp = self.sm['selfdriveStateSP']
CC.madsEnabled = ss_sp.mads.enabled
if ss_sp.mads.available:
CC.sunnypilotParams |= SunnypilotParamFlags.ENABLE_MADS.value
_lat_active = ss_sp.mads.active
else:
_lat_active = self.sm['selfdriveState'].active
@@ -137,12 +139,9 @@ class Controls:
cloudlog.error(f"actuators.{p} not finite {actuators.to_dict()}")
setattr(actuators, p, 0.0)
CC_SP = custom.CarControlSP.new_message()
CC_SP.mads = ss_sp.mads
return CC, lac_log
return CC, CC_SP, lac_log
def publish(self, CC, CC_SP, lac_log):
def publish(self, CC, lac_log):
CS = self.sm['carState']
# Orientation and angle rates can be useful for carcontroller
@@ -218,18 +217,12 @@ class Controls:
cc_send.carControl = CC
self.pm.send('carControl', cc_send)
# carControlSP
cc_sp_send = messaging.new_message('carControlSP')
cc_sp_send.valid = CS.canValid
cc_sp_send.carControlSP = CC_SP
self.pm.send('carControlSP', cc_sp_send)
def run(self):
rk = Ratekeeper(100, print_delay_threshold=None)
while True:
self.update()
CC, CC_SP, lac_log = self.state_control()
self.publish(CC, CC_SP, lac_log)
CC, lac_log = self.state_control()
self.publish(CC, lac_log)
rk.monitor_time()
def main():
@@ -141,9 +141,6 @@ class LongitudinalPlanner(LongitudinalPlannerSP):
accel_limits = [ACCEL_MIN, ACCEL_MAX]
accel_limits_turns = [ACCEL_MIN, ACCEL_MAX]
if (accel_control := self.compute_accel_limits(v_ego, sm, self.CP)):
accel_limits, accel_limits_turns = accel_control
if reset_state:
self.v_desired_filter.x = v_ego
# Clip aEgo to cruise limits to prevent large accelerations when becoming active
@@ -19,8 +19,7 @@ class TestLatControl:
def test_saturation(self, car_name, controller):
CarInterface, CarController, CarState, RadarInterface = interfaces[car_name]
CP = CarInterface.get_non_essential_params(car_name)
CP_SP = CarInterface.get_non_essential_params_sp(CP, car_name)
CI = CarInterface(CP, CP_SP, CarController, CarState)
CI = CarInterface(CP, CarController, CarState)
VM = VehicleModel(CP)
controller = controller(CP.as_reader(), CI)
-5
View File
@@ -1,5 +0,0 @@
from pathlib import Path
MODEL_PATH = Path(__file__).parent / 'models/supercombo.onnx'
MODEL_PKL_PATH = Path(__file__).parent / 'models/supercombo_tinygrad.pkl'
METADATA_PATH = Path(__file__).parent / 'models/supercombo_metadata.pkl'
+18 -42
View File
@@ -2,33 +2,13 @@ import os
import capnp
import numpy as np
from cereal import log
from openpilot.selfdrive.modeld.constants import ModelConstants, Plan
from openpilot.selfdrive.controls.lib.drive_helpers import MIN_SPEED
from openpilot.selfdrive.modeld.constants import ModelConstants, Plan, Meta
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
ConfidenceClass = log.ModelDataV2.ConfidenceClass
def curv_from_psis(psi_target, psi_rate, vego, delay):
vego = np.clip(vego, MIN_SPEED, np.inf)
curv_from_psi = psi_target / (vego * delay) # epsilon to prevent divide-by-zero
return 2 * curv_from_psi - psi_rate / vego
def get_curvature_from_plan(plan, vego, delay):
psi_target = np.interp(delay, ModelConstants.T_IDXS, plan[:, Plan.T_FROM_CURRENT_EULER][:, 2])
psi_rate = plan[:, Plan.ORIENTATION_RATE][0, 2]
return curv_from_psis(psi_target, psi_rate, vego, delay)
def get_curvature_from_output(output, vego, delay):
if desired_curv := output.get('desired_curvature'): # If the model outputs the desired curvature, use that directly
return float(desired_curv[0, 0])
return float(get_curvature_from_plan(output['plan'][0], vego, delay))
class PublishState:
def __init__(self):
self.disengage_buffer = np.zeros(ModelConstants.CONFIDENCE_BUFFER_LEN*ModelConstants.DISENGAGE_WIDTH, dtype=np.float32)
@@ -79,14 +59,12 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D
net_output_data: dict[str, np.ndarray], v_ego: float, delay: float,
publish_state: PublishState, vipc_frame_id: int, vipc_frame_id_extra: int,
frame_id: int, frame_drop: float, timestamp_eof: int, model_execution_time: float,
valid: bool, model_meta) -> None:
valid: bool) -> None:
frame_age = frame_id - vipc_frame_id if frame_id > vipc_frame_id else 0
frame_drop_perc = frame_drop * 100
extended_msg.valid = valid
base_msg.valid = valid
desired_curvature = float(get_curvature_from_output(net_output_data, v_ego, delay))
driving_model_data = base_msg.drivingModelData
driving_model_data.frameId = vipc_frame_id
@@ -95,7 +73,7 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D
driving_model_data.modelExecutionTime = model_execution_time
action = driving_model_data.action
action.desiredCurvature = desired_curvature
action.desiredCurvature = float(net_output_data['desired_curvature'][0,0])
modelV2 = extended_msg.modelV2
modelV2.frameId = vipc_frame_id
@@ -130,7 +108,7 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D
# lateral planning
action = modelV2.action
action.desiredCurvature = desired_curvature
action.desiredCurvature = float(net_output_data['desired_curvature'][0,0])
# times at X_IDXS according to model plan
PLAN_T_IDXS = [np.nan] * ModelConstants.IDX_N
@@ -181,25 +159,23 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D
meta = modelV2.meta
meta.desireState = net_output_data['desire_state'][0].reshape(-1).tolist()
meta.desirePrediction = net_output_data['desire_pred'][0].reshape(-1).tolist()
meta.engagedProb = net_output_data['meta'][0,model_meta.ENGAGED].item()
meta.engagedProb = net_output_data['meta'][0,Meta.ENGAGED].item()
meta.init('disengagePredictions')
disengage_predictions = meta.disengagePredictions
disengage_predictions.t = ModelConstants.META_T_IDXS
disengage_predictions.brakeDisengageProbs = net_output_data['meta'][0,model_meta.BRAKE_DISENGAGE].tolist()
disengage_predictions.gasDisengageProbs = net_output_data['meta'][0,model_meta.GAS_DISENGAGE].tolist()
disengage_predictions.steerOverrideProbs = net_output_data['meta'][0,model_meta.STEER_OVERRIDE].tolist()
disengage_predictions.brake3MetersPerSecondSquaredProbs = net_output_data['meta'][0,model_meta.HARD_BRAKE_3].tolist()
disengage_predictions.brake4MetersPerSecondSquaredProbs = net_output_data['meta'][0,model_meta.HARD_BRAKE_4].tolist()
disengage_predictions.brake5MetersPerSecondSquaredProbs = net_output_data['meta'][0,model_meta.HARD_BRAKE_5].tolist()
if hasattr(model_meta, 'GAS_PRESS') and hasattr(model_meta, 'BRAKE_PRESS'):
disengage_predictions.gasPressProbs = net_output_data['meta'][0,model_meta.GAS_PRESS].tolist()
disengage_predictions.brakePressProbs = net_output_data['meta'][0,model_meta.BRAKE_PRESS].tolist()
disengage_predictions.brakeDisengageProbs = net_output_data['meta'][0,Meta.BRAKE_DISENGAGE].tolist()
disengage_predictions.gasDisengageProbs = net_output_data['meta'][0,Meta.GAS_DISENGAGE].tolist()
disengage_predictions.steerOverrideProbs = net_output_data['meta'][0,Meta.STEER_OVERRIDE].tolist()
disengage_predictions.brake3MetersPerSecondSquaredProbs = net_output_data['meta'][0,Meta.HARD_BRAKE_3].tolist()
disengage_predictions.brake4MetersPerSecondSquaredProbs = net_output_data['meta'][0,Meta.HARD_BRAKE_4].tolist()
disengage_predictions.brake5MetersPerSecondSquaredProbs = net_output_data['meta'][0,Meta.HARD_BRAKE_5].tolist()
#disengage_predictions.gasPressProbs = net_output_data['meta'][0,Meta.GAS_PRESS].tolist()
#disengage_predictions.brakePressProbs = net_output_data['meta'][0,Meta.BRAKE_PRESS].tolist()
publish_state.prev_brake_5ms2_probs[:-1] = publish_state.prev_brake_5ms2_probs[1:]
publish_state.prev_brake_5ms2_probs[-1] = net_output_data['meta'][0,model_meta.HARD_BRAKE_5][0]
publish_state.prev_brake_5ms2_probs[-1] = net_output_data['meta'][0,Meta.HARD_BRAKE_5][0]
publish_state.prev_brake_3ms2_probs[:-1] = publish_state.prev_brake_3ms2_probs[1:]
publish_state.prev_brake_3ms2_probs[-1] = net_output_data['meta'][0,model_meta.HARD_BRAKE_3][0]
publish_state.prev_brake_3ms2_probs[-1] = net_output_data['meta'][0,Meta.HARD_BRAKE_3][0]
hard_brake_predicted = (publish_state.prev_brake_5ms2_probs > ModelConstants.FCW_THRESHOLDS_5MS2).all() and \
(publish_state.prev_brake_3ms2_probs > ModelConstants.FCW_THRESHOLDS_3MS2).all()
meta.hardBrakePredicted = hard_brake_predicted.item()
@@ -207,9 +183,9 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D
# confidence
if vipc_frame_id % (2*ModelConstants.MODEL_FREQ) == 0:
# any disengage prob
brake_disengage_probs = net_output_data['meta'][0,model_meta.BRAKE_DISENGAGE]
gas_disengage_probs = net_output_data['meta'][0,model_meta.GAS_DISENGAGE]
steer_override_probs = net_output_data['meta'][0,model_meta.STEER_OVERRIDE]
brake_disengage_probs = net_output_data['meta'][0,Meta.BRAKE_DISENGAGE]
gas_disengage_probs = net_output_data['meta'][0,Meta.GAS_DISENGAGE]
steer_override_probs = net_output_data['meta'][0,Meta.STEER_OVERRIDE]
any_disengage_probs = 1-((1-brake_disengage_probs)*(1-gas_disengage_probs)*(1-steer_override_probs))
# independent disengage prob for each 2s slice
ind_disengage_probs = np.r_[any_disengage_probs[0], np.diff(any_disengage_probs) / (1 - any_disengage_probs[:-1])]
+65 -63
View File
@@ -1,11 +1,21 @@
#!/usr/bin/env python3
import os
from openpilot.system.hardware import TICI
#
if TICI:
from tinygrad.tensor import Tensor
from tinygrad.dtype import dtypes
from openpilot.selfdrive.modeld.runners.tinygrad_helpers import qcom_tensor_from_opencl_address
os.environ['QCOM'] = '1'
else:
from openpilot.selfdrive.modeld.runners.ort_helpers import make_onnx_cpu_runner
import time
import pickle
import numpy as np
import cereal.messaging as messaging
from cereal import car, log
from pathlib import Path
from setproctitle import setproctitle
from cereal.messaging import PubMaster, SubMaster
from msgq.visionipc import VisionIpcClient, VisionStreamType, VisionBuf
@@ -23,11 +33,13 @@ from openpilot.selfdrive.modeld.fill_model_msg import fill_model_msg, fill_pose_
from openpilot.selfdrive.modeld.constants import ModelConstants
from openpilot.selfdrive.modeld.models.commonmodel_pyx import DrivingModelFrame, CLContext
from openpilot.sunnypilot.modeld_v2.meta_helper import load_meta_constants
from openpilot.sunnypilot.modeld_v2.model_runner import ONNXRunner, TinygradRunner
PROCESS_NAME = "selfdrive.modeld.modeld"
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
MODEL_PATH = Path(__file__).parent / 'models/supercombo.onnx'
MODEL_PKL_PATH = Path(__file__).parent / 'models/supercombo_tinygrad.pkl'
METADATA_PATH = Path(__file__).parent / 'models/supercombo_metadata.pkl'
class FrameMeta:
frame_id: int = 0
@@ -45,35 +57,39 @@ class ModelState:
prev_desire: np.ndarray # for tracking the rising edge of the pulse
def __init__(self, context: CLContext):
try:
self.model_runner = TinygradRunner() if TICI else ONNXRunner()
except Exception as e:
cloudlog.exception(f"Failed to initialize model runner: {str(e)}")
buffer_length = 5 if self.model_runner.is_20hz else 2
self.frames = {'input_imgs': DrivingModelFrame(context, buffer_length), 'big_input_imgs': DrivingModelFrame(context, buffer_length)}
self.frames = {'input_imgs': DrivingModelFrame(context), 'big_input_imgs': DrivingModelFrame(context)}
self.prev_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32)
if self.model_runner.is_20hz:
self.full_features_20Hz = np.zeros((ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32)
self.desire_20Hz = np.zeros((ModelConstants.FULL_HISTORY_BUFFER_LEN + 1, ModelConstants.DESIRE_LEN), dtype=np.float32)
# img buffers are managed in openCL transform code
self.numpy_inputs = {}
self.numpy_inputs = {
'desire': np.zeros((1, (ModelConstants.FULL_HISTORY_BUFFER_LEN+1), ModelConstants.DESIRE_LEN), dtype=np.float32),
'traffic_convention': np.zeros((1, ModelConstants.TRAFFIC_CONVENTION_LEN), dtype=np.float32),
'lateral_control_params': np.zeros((1, ModelConstants.LATERAL_CONTROL_PARAMS_LEN), dtype=np.float32),
'prev_desired_curv': np.zeros((1, (ModelConstants.FULL_HISTORY_BUFFER_LEN+1), ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32),
'features_buffer': np.zeros((1, ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32),
}
for key, shape in self.model_runner.input_shapes.items():
if key not in self.frames: # Managed by opencl
self.numpy_inputs[key] = np.zeros(shape, dtype=np.float32)
with open(METADATA_PATH, 'rb') as f:
model_metadata = pickle.load(f)
self.input_shapes = model_metadata['input_shapes']
self.output_slices = model_metadata['output_slices']
net_output_size = model_metadata['output_shapes']['outputs'][1]
self.output = np.zeros(net_output_size, dtype=np.float32)
self.parser = Parser()
if self.model_runner.is_20hz:
net_output_size = self.model_runner.model_metadata['output_shapes']['outputs'][1]
self.output = np.zeros(net_output_size, dtype=np.float32)
if TICI:
self.tensor_inputs = {k: Tensor(v, device='NPY').realize() for k,v in self.numpy_inputs.items()}
with open(MODEL_PKL_PATH, "rb") as f:
self.model_run = pickle.load(f)
else:
self.onnx_cpu_runner = make_onnx_cpu_runner(MODEL_PATH)
num_elements = self.numpy_inputs['features_buffer'].shape[1]
step_size = int(-100 / num_elements)
self.full_features_20Hz_idxs = np.arange(step_size, step_size * (num_elements + 1), step_size)[::-1]
self.desire_reshape_dims = (self.numpy_inputs['desire'].shape[0], self.numpy_inputs['desire'].shape[1], -1, self.numpy_inputs['desire'].shape[2])
def slice_outputs(self, model_outputs: np.ndarray) -> dict[str, np.ndarray]:
parsed_model_outputs = {k: model_outputs[np.newaxis, v] for k,v in self.output_slices.items()}
if SEND_RAW_PRED:
parsed_model_outputs['raw_pred'] = model_outputs.copy()
return parsed_model_outputs
def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_wide: np.ndarray,
inputs: dict[str, np.ndarray], prepare_only: bool) -> dict[str, np.ndarray] | None:
@@ -82,53 +98,40 @@ class ModelState:
new_desire = np.where(inputs['desire'] - self.prev_desire > .99, inputs['desire'], 0)
self.prev_desire[:] = inputs['desire']
if self.model_runner.is_20hz:
self.desire_20Hz[:-1] = self.desire_20Hz[1:]
self.desire_20Hz[-1] = new_desire
self.numpy_inputs['desire'][:] = self.desire_20Hz.reshape(self.desire_reshape_dims).max(axis=2)
else:
length = inputs['desire'].shape[0]
self.numpy_inputs['desire'][0, :-1] = self.numpy_inputs['desire'][0, 1:]
self.numpy_inputs['desire'][0, -1, :length] = new_desire[:length]
for key in self.numpy_inputs:
if key in inputs and key not in ['desire']:
self.numpy_inputs[key][:] = inputs[key]
self.numpy_inputs['desire'][0,:-1] = self.numpy_inputs['desire'][0,1:]
self.numpy_inputs['desire'][0,-1] = new_desire
self.numpy_inputs['traffic_convention'][:] = inputs['traffic_convention']
self.numpy_inputs['lateral_control_params'][:] = inputs['lateral_control_params']
imgs_cl = {'input_imgs': self.frames['input_imgs'].prepare(buf, transform.flatten()),
'big_input_imgs': self.frames['big_input_imgs'].prepare(wbuf, transform_wide.flatten())}
# Prepare inputs using the model runner
self.model_runner.prepare_inputs(imgs_cl, self.numpy_inputs, self.frames)
if TICI:
# The imgs tensors are backed by opencl memory, only need init once
for key in imgs_cl:
if key not in self.tensor_inputs:
self.tensor_inputs[key] = qcom_tensor_from_opencl_address(imgs_cl[key].mem_address, self.input_shapes[key], dtype=dtypes.uint8)
else:
for key in imgs_cl:
self.numpy_inputs[key] = self.frames[key].buffer_from_cl(imgs_cl[key]).reshape(self.input_shapes[key]).astype(dtype=np.float32)
if prepare_only:
return None
# Run model inference
self.output = self.model_runner.run_model()
outputs = self.parser.parse_outputs(self.model_runner.slice_outputs(self.output))
if self.model_runner.is_20hz:
self.full_features_20Hz[:-1] = self.full_features_20Hz[1:]
self.full_features_20Hz[-1] = outputs['hidden_state'][0, :]
self.numpy_inputs['features_buffer'][:] = self.full_features_20Hz[self.full_features_20Hz_idxs]
if TICI:
self.output = self.model_run(**self.tensor_inputs).numpy().flatten()
else:
feature_len = outputs['hidden_state'].shape[1]
self.numpy_inputs['features_buffer'][0, :-1] = self.numpy_inputs['features_buffer'][0, 1:]
self.numpy_inputs['features_buffer'][0, -1, :feature_len] = outputs['hidden_state'][0, :feature_len]
self.output = self.onnx_cpu_runner.run(None, self.numpy_inputs)[0].flatten()
if "desired_curvature" in outputs:
input_name_prev = None
outputs = self.parser.parse_outputs(self.slice_outputs(self.output))
if "prev_desired_curvs" in self.numpy_inputs.keys():
input_name_prev = 'prev_desired_curvs'
elif "prev_desired_curv" in self.numpy_inputs.keys():
input_name_prev = 'prev_desired_curv'
self.numpy_inputs['features_buffer'][0,:-1] = self.numpy_inputs['features_buffer'][0,1:]
self.numpy_inputs['features_buffer'][0,-1] = outputs['hidden_state'][0, :]
if input_name_prev is not None:
length = outputs['desired_curvature'][0].size
self.numpy_inputs[input_name_prev][0, :-length, 0] = self.numpy_inputs[input_name_prev][0, length:, 0]
self.numpy_inputs[input_name_prev][0, -length:, 0] = outputs['desired_curvature'][0]
# TODO model only uses last value now
self.numpy_inputs['prev_desired_curv'][0,:-1] = self.numpy_inputs['prev_desired_curv'][0,1:]
self.numpy_inputs['prev_desired_curv'][0,-1,:] = outputs['desired_curvature'][0, :]
return outputs
@@ -239,6 +242,7 @@ def main(demo=False):
is_rhd = sm["driverMonitoringState"].isRHD
frame_id = sm["roadCameraState"].frameId
v_ego = max(sm["carState"].vEgo, 0.)
lateral_control_params = np.array([v_ego, steer_delay], dtype=np.float32)
if sm.updated["liveCalibration"] and sm.seen['roadCameraState'] and sm.seen['deviceState']:
device_from_calib_euler = np.array(sm["liveCalibration"].rpyCalib, dtype=np.float32)
dc = DEVICE_CAMERAS[(str(sm['deviceState'].deviceType), str(sm['roadCameraState'].sensor))]
@@ -269,10 +273,8 @@ def main(demo=False):
inputs:dict[str, np.ndarray] = {
'desire': vec_desire,
'traffic_convention': traffic_convention,
}
if "lateral_control_params" in model.numpy_inputs.keys():
inputs['lateral_control_params'] = np.array([v_ego, steer_delay], dtype=np.float32)
'lateral_control_params': lateral_control_params,
}
mt1 = time.perf_counter()
model_output = model.run(buf_main, buf_extra, model_transform_main, model_transform_extra, inputs, prepare_only)
@@ -285,7 +287,7 @@ def main(demo=False):
posenet_send = messaging.new_message('cameraOdometry')
fill_model_msg(drivingdata_send, modelv2_send, model_output, v_ego, steer_delay,
publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id,
frame_drop_ratio, meta_main.timestamp_eof, model_execution_time, live_calib_seen, load_meta_constants())
frame_drop_ratio, meta_main.timestamp_eof, model_execution_time, live_calib_seen)
desire_state = modelv2_send.modelV2.meta.desireState
l_lane_change_prob = desire_state[log.Desire.laneChangeLeft]
+4 -5
View File
@@ -5,14 +5,13 @@
#include "common/clutil.h"
DrivingModelFrame::DrivingModelFrame(cl_device_id device_id, cl_context context, uint8_t buffer_length) : ModelFrame(device_id, context), buffer_length(buffer_length) {
DrivingModelFrame::DrivingModelFrame(cl_device_id device_id, cl_context context) : ModelFrame(device_id, context) {
input_frames = std::make_unique<uint8_t[]>(buf_size);
input_frames_cl = CL_CHECK_ERR(clCreateBuffer(context, CL_MEM_READ_WRITE, buf_size, NULL, &err));
img_buffer_20hz_cl = CL_CHECK_ERR(clCreateBuffer(context, CL_MEM_READ_WRITE, buffer_length*frame_size_bytes, NULL, &err));
region.origin = (buffer_length - 1) * frame_size_bytes;
img_buffer_20hz_cl = CL_CHECK_ERR(clCreateBuffer(context, CL_MEM_READ_WRITE, 2*frame_size_bytes, NULL, &err));
region.origin = 1 * frame_size_bytes;
region.size = frame_size_bytes;
last_img_cl = CL_CHECK_ERR(clCreateSubBuffer(img_buffer_20hz_cl, CL_MEM_READ_WRITE, CL_BUFFER_CREATE_TYPE_REGION, &region, &err));
// printf("Buffer length: %d, region origin: %lu, region size: %lu\n", buffer_length, region.origin, region.size);
loadyuv_init(&loadyuv, context, device_id, MODEL_WIDTH, MODEL_HEIGHT);
init_transform(device_id, context, MODEL_WIDTH, MODEL_HEIGHT);
@@ -21,7 +20,7 @@ DrivingModelFrame::DrivingModelFrame(cl_device_id device_id, cl_context context,
cl_mem* DrivingModelFrame::prepare(cl_mem yuv_cl, int frame_width, int frame_height, int frame_stride, int frame_uv_offset, const mat3& projection) {
run_transform(yuv_cl, MODEL_WIDTH, MODEL_HEIGHT, frame_width, frame_height, frame_stride, frame_uv_offset, projection);
for (int i = 0; i < (buffer_length - 1); i++) {
for (int i = 0; i < 1; i++) {
CL_CHECK(clEnqueueCopyBuffer(q, img_buffer_20hz_cl, img_buffer_20hz_cl, (i+1)*frame_size_bytes, i*frame_size_bytes, frame_size_bytes, 0, nullptr, nullptr));
}
loadyuv_queue(&loadyuv, q, y_cl, u_cl, v_cl, last_img_cl);
+1 -2
View File
@@ -64,7 +64,7 @@ protected:
class DrivingModelFrame : public ModelFrame {
public:
DrivingModelFrame(cl_device_id device_id, cl_context context, uint8_t buffer_length);
DrivingModelFrame(cl_device_id device_id, cl_context context);
~DrivingModelFrame();
cl_mem* prepare(cl_mem yuv_cl, int frame_width, int frame_height, int frame_stride, int frame_uv_offset, const mat3& projection);
@@ -73,7 +73,6 @@ public:
const int MODEL_FRAME_SIZE = MODEL_WIDTH * MODEL_HEIGHT * 3 / 2;
const int buf_size = MODEL_FRAME_SIZE * 2;
const size_t frame_size_bytes = MODEL_FRAME_SIZE * sizeof(uint8_t);
const uint8_t buffer_length;
private:
LoadYUVState loadyuv;
+1 -1
View File
@@ -20,7 +20,7 @@ cdef extern from "selfdrive/modeld/models/commonmodel.h":
cppclass DrivingModelFrame:
int buf_size
DrivingModelFrame(cl_device_id, cl_context, unsigned char)
DrivingModelFrame(cl_device_id, cl_context)
cppclass MonitoringModelFrame:
int buf_size
+3 -3
View File
@@ -4,7 +4,7 @@
import numpy as np
cimport numpy as cnp
from libc.string cimport memcpy
from libc.stdint cimport uintptr_t, uint8_t
from libc.stdint cimport uintptr_t
from msgq.visionipc.visionipc cimport cl_mem
from msgq.visionipc.visionipc_pyx cimport VisionBuf, CLContext as BaseCLContext
@@ -59,8 +59,8 @@ cdef class ModelFrame:
cdef class DrivingModelFrame(ModelFrame):
cdef cppDrivingModelFrame * _frame
def __cinit__(self, CLContext context, int buffer_length=2):
self._frame = new cppDrivingModelFrame(context.device_id, context.context, buffer_length)
def __cinit__(self, CLContext context):
self._frame = new cppDrivingModelFrame(context.device_id, context.context)
self.frame = <cppModelFrame*>(self._frame)
self.buf_size = self._frame.buf_size
-5
View File
@@ -81,8 +81,3 @@ void PandaSafety::setSafetyMode(const std::string &params_string) {
pandas_[i]->set_safety_model(safety_model, safety_param);
}
}
bool PandaSafety::getOffroadMode() {
auto offroad_mode = params_.getBool("OffroadMode");
return offroad_mode;
}
+6 -5
View File
@@ -158,6 +158,7 @@ void fill_panda_state(cereal::PandaState::Builder &ps, cereal::PandaState::Panda
ps.setIgnitionLine(health.ignition_line_pkt);
ps.setIgnitionCan(health.ignition_can_pkt);
ps.setControlsAllowed(health.controls_allowed_pkt);
ps.setControlsAllowedLat(health.controls_allowed_lat_pkt);
ps.setTxBufferOverflow(health.tx_buffer_overflow_pkt);
ps.setRxBufferOverflow(health.rx_buffer_overflow_pkt);
ps.setPandaType(hw_type);
@@ -205,7 +206,7 @@ void fill_panda_can_state(cereal::PandaState::PandaCanState::Builder &cs, const
cs.setCanCoreResetCnt(can_health.can_core_reset_cnt);
}
std::optional<bool> send_panda_states(PubMaster *pm, const std::vector<Panda *> &pandas, bool spoofing_started, PandaSafety *panda_safety) {
std::optional<bool> send_panda_states(PubMaster *pm, const std::vector<Panda *> &pandas, bool spoofing_started) {
bool ignition_local = false;
const uint32_t pandas_cnt = pandas.size();
@@ -253,7 +254,7 @@ std::optional<bool> send_panda_states(PubMaster *pm, const std::vector<Panda *>
health.ignition_line_pkt = 0;
}
ignition_local |= ((health.ignition_line_pkt != 0) || (health.ignition_can_pkt != 0)) && !panda_safety->getOffroadMode();
ignition_local |= ((health.ignition_line_pkt != 0) || (health.ignition_can_pkt != 0));
pandaStates.push_back(health);
}
@@ -340,7 +341,7 @@ void send_peripheral_state(Panda *panda, PubMaster *pm) {
pm->send("peripheralState", msg);
}
void process_panda_state(std::vector<Panda *> &pandas, PubMaster *pm, bool spoofing_started, PandaSafety *panda_safety) {
void process_panda_state(std::vector<Panda *> &pandas, PubMaster *pm, bool spoofing_started) {
static SubMaster sm({"selfdriveState", "selfdriveStateSP", "carParams"});
std::vector<std::string> connected_serials;
@@ -349,7 +350,7 @@ void process_panda_state(std::vector<Panda *> &pandas, PubMaster *pm, bool spoof
}
{
auto ignition_opt = send_panda_states(pm, pandas, spoofing_started, panda_safety);
auto ignition_opt = send_panda_states(pm, pandas, spoofing_started);
if (!ignition_opt) {
LOGE("Failed to get ignition_opt");
return;
@@ -461,7 +462,7 @@ void pandad_run(std::vector<Panda *> &pandas) {
// Process panda state at 10 Hz
if (rk.frame() % 10 == 0) {
process_panda_state(pandas, &pm, spoofing_started, &panda_safety);
process_panda_state(pandas, &pm, spoofing_started);
panda_safety.configureSafetyMode();
}
-1
View File
@@ -12,7 +12,6 @@ class PandaSafety {
public:
PandaSafety(const std::vector<Panda *> &pandas) : pandas_(pandas) {}
void configureSafetyMode();
bool getOffroadMode();
private:
void updateMultiplexingMode();
+1 -2
View File
@@ -6,8 +6,7 @@ from dataclasses import dataclass
from openpilot.common.basedir import BASEDIR
from openpilot.common.params import Params
from openpilot.selfdrive.selfdrived.events import Alert
from openpilot.sunnypilot.selfdrive.selfdrived.events_base import EmptyAlert
from openpilot.selfdrive.selfdrived.events import Alert, EmptyAlert
with open(os.path.join(BASEDIR, "selfdrive/selfdrived/alerts_offroad.json")) as f:
-4
View File
@@ -44,9 +44,5 @@
"Offroad_Recalibration": {
"text": "openpilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield.",
"severity": 0
},
"OffroadMode_Status": {
"text": "sunnypilot is now in Always Offroad mode. sunnypilot won't start until Always Offroad mode is disabled. Go to \"Settings\" -> \"Device\" to exit Always Offroad mode.",
"severity": 1
}
}
+291 -13
View File
@@ -1,6 +1,9 @@
#!/usr/bin/env python3
import bisect
import math
import os
from enum import IntEnum
from collections.abc import Callable
from cereal import log, car
import cereal.messaging as messaging
@@ -9,11 +12,6 @@ from openpilot.common.git import get_short_branch
from openpilot.common.realtime import DT_CTRL
from openpilot.selfdrive.locationd.calibrationd import MIN_SPEED_FILTER
from openpilot.sunnypilot.selfdrive.selfdrived.events_base import EventsBase, Priority, ET, Alert, \
NoEntryAlert, SoftDisableAlert, UserSoftDisableAlert, ImmediateDisableAlert, EngagementAlert, NormalPermanentAlert, \
StartupAlert, AlertCallbackType
AlertSize = log.SelfdriveState.AlertSize
AlertStatus = log.SelfdriveState.AlertStatus
VisualAlert = car.CarControl.HUDControl.VisualAlert
@@ -21,23 +19,201 @@ AudibleAlert = car.CarControl.HUDControl.AudibleAlert
EventName = log.OnroadEvent.EventName
# Alert priorities
class Priority(IntEnum):
LOWEST = 0
LOWER = 1
LOW = 2
MID = 3
HIGH = 4
HIGHEST = 5
# Event types
class ET:
ENABLE = 'enable'
PRE_ENABLE = 'preEnable'
OVERRIDE_LATERAL = 'overrideLateral'
OVERRIDE_LONGITUDINAL = 'overrideLongitudinal'
NO_ENTRY = 'noEntry'
WARNING = 'warning'
USER_DISABLE = 'userDisable'
SOFT_DISABLE = 'softDisable'
IMMEDIATE_DISABLE = 'immediateDisable'
PERMANENT = 'permanent'
# get event name from enum
EVENT_NAME = {v: k for k, v in EventName.schema.enumerants.items()}
class Events(EventsBase):
class Events:
def __init__(self):
super().__init__()
self.events: list[int] = []
self.static_events: list[int] = []
self.event_counters = dict.fromkeys(EVENTS.keys(), 0)
def get_events_mapping(self) -> dict[int, dict[str, Alert | AlertCallbackType]]:
return EVENTS
@property
def names(self) -> list[int]:
return self.events
def get_event_name(self, event: int):
return EVENT_NAME[event]
def __len__(self) -> int:
return len(self.events)
def get_event_msg_type(self):
return log.OnroadEvent
def add(self, event_name: int, static: bool=False) -> None:
if static:
bisect.insort(self.static_events, event_name)
bisect.insort(self.events, event_name)
def clear(self) -> None:
self.event_counters = {k: (v + 1 if k in self.events else 0) for k, v in self.event_counters.items()}
self.events = self.static_events.copy()
def contains(self, event_type: str) -> bool:
return any(event_type in EVENTS.get(e, {}) for e in self.events)
def create_alerts(self, event_types: list[str], callback_args=None):
if callback_args is None:
callback_args = []
ret = []
for e in self.events:
types = EVENTS[e].keys()
for et in event_types:
if et in types:
alert = EVENTS[e][et]
if not isinstance(alert, Alert):
alert = alert(*callback_args)
if DT_CTRL * (self.event_counters[e] + 1) >= alert.creation_delay:
alert.alert_type = f"{EVENT_NAME[e]}/{et}"
alert.event_type = et
ret.append(alert)
return ret
def add_from_msg(self, events):
for e in events:
bisect.insort(self.events, e.name.raw)
def to_msg(self):
ret = []
for event_name in self.events:
event = log.OnroadEvent.new_message()
event.name = event_name
for event_type in EVENTS.get(event_name, {}):
setattr(event, event_type, True)
ret.append(event)
return ret
def has(self, event_name: int) -> bool:
return event_name in self.events
def contains_in_list(self, events_list: list[int]) -> bool:
return any(event_name in self.events for event_name in events_list)
def remove(self, event_name: int, static: bool = False) -> None:
if static and event_name in self.static_events:
self.static_events.remove(event_name)
if event_name in self.events:
self.event_counters[event_name] = self.event_counters[event_name] + 1
self.events.remove(event_name)
def replace(self, prev_event_name: int, cur_event_name: int, static: bool = False) -> None:
self.remove(prev_event_name, static)
self.add(cur_event_name, static)
class Alert:
def __init__(self,
alert_text_1: str,
alert_text_2: str,
alert_status: log.SelfdriveState.AlertStatus,
alert_size: log.SelfdriveState.AlertSize,
priority: Priority,
visual_alert: car.CarControl.HUDControl.VisualAlert,
audible_alert: car.CarControl.HUDControl.AudibleAlert,
duration: float,
creation_delay: float = 0.):
self.alert_text_1 = alert_text_1
self.alert_text_2 = alert_text_2
self.alert_status = alert_status
self.alert_size = alert_size
self.priority = priority
self.visual_alert = visual_alert
self.audible_alert = audible_alert
self.duration = int(duration / DT_CTRL)
self.creation_delay = creation_delay
self.alert_type = ""
self.event_type: str | None = None
def __str__(self) -> str:
return f"{self.alert_text_1}/{self.alert_text_2} {self.priority} {self.visual_alert} {self.audible_alert}"
def __gt__(self, alert2) -> bool:
if not isinstance(alert2, Alert):
return False
return self.priority > alert2.priority
EmptyAlert = Alert("" , "", AlertStatus.normal, AlertSize.none, Priority.LOWEST,
VisualAlert.none, AudibleAlert.none, 0)
class NoEntryAlert(Alert):
def __init__(self, alert_text_2: str,
alert_text_1: str = "openpilot Unavailable",
visual_alert: car.CarControl.HUDControl.VisualAlert=VisualAlert.none):
super().__init__(alert_text_1, alert_text_2, AlertStatus.normal,
AlertSize.mid, Priority.LOW, visual_alert,
AudibleAlert.refuse, 3.)
class SoftDisableAlert(Alert):
def __init__(self, alert_text_2: str):
super().__init__("TAKE CONTROL IMMEDIATELY", alert_text_2,
AlertStatus.userPrompt, AlertSize.full,
Priority.MID, VisualAlert.steerRequired,
AudibleAlert.warningSoft, 2.),
# less harsh version of SoftDisable, where the condition is user-triggered
class UserSoftDisableAlert(SoftDisableAlert):
def __init__(self, alert_text_2: str):
super().__init__(alert_text_2),
self.alert_text_1 = "openpilot will disengage"
class ImmediateDisableAlert(Alert):
def __init__(self, alert_text_2: str):
super().__init__("TAKE CONTROL IMMEDIATELY", alert_text_2,
AlertStatus.critical, AlertSize.full,
Priority.HIGHEST, VisualAlert.steerRequired,
AudibleAlert.warningImmediate, 4.),
class EngagementAlert(Alert):
def __init__(self, audible_alert: car.CarControl.HUDControl.AudibleAlert):
super().__init__("", "",
AlertStatus.normal, AlertSize.none,
Priority.MID, VisualAlert.none,
audible_alert, .2),
class NormalPermanentAlert(Alert):
def __init__(self, alert_text_1: str, alert_text_2: str = "", duration: float = 0.2, priority: Priority = Priority.LOWER, creation_delay: float = 0.):
super().__init__(alert_text_1, alert_text_2,
AlertStatus.normal, AlertSize.mid if len(alert_text_2) else AlertSize.small,
priority, VisualAlert.none, AudibleAlert.none, duration, creation_delay=creation_delay),
class StartupAlert(Alert):
def __init__(self, alert_text_1: str, alert_text_2: str = "Always keep hands on wheel and eyes on road", alert_status=AlertStatus.normal):
super().__init__(alert_text_1, alert_text_2,
alert_status, AlertSize.mid,
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 5.),
# ********** helper functions **********
@@ -49,6 +225,8 @@ def get_display_speed(speed_ms: float, metric: bool) -> str:
# ********** alert callback functions **********
AlertCallbackType = Callable[[car.CarParams, car.CarState, messaging.SubMaster, bool, int, log.ControlsState], Alert]
def soft_disable_alert(alert_text_2: str) -> AlertCallbackType:
def func(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert:
@@ -794,6 +972,106 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
ET.WARNING: personality_changed_alert,
},
# sunnypilot
EventName.lkasEnable: {
ET.ENABLE: EngagementAlert(AudibleAlert.engage),
},
EventName.lkasDisable: {
ET.USER_DISABLE: EngagementAlert(AudibleAlert.disengage),
},
EventName.manualSteeringRequired: {
ET.USER_DISABLE: Alert(
"Automatic Lane Centering is OFF",
"Manual Steering Required",
AlertStatus.normal, AlertSize.mid,
Priority.LOW, VisualAlert.none, AudibleAlert.disengage, 1.),
},
EventName.manualLongitudinalRequired: {
ET.WARNING: Alert(
"Smart/Adaptive Cruise Control: OFF",
"Manual Speed Control Required",
AlertStatus.normal, AlertSize.mid,
Priority.LOW, VisualAlert.none, AudibleAlert.none, 1.),
},
EventName.silentLkasEnable: {
ET.ENABLE: EngagementAlert(AudibleAlert.none),
},
EventName.silentLkasDisable: {
ET.USER_DISABLE: EngagementAlert(AudibleAlert.none),
},
EventName.silentBrakeHold: {
ET.USER_DISABLE: EngagementAlert(AudibleAlert.none),
ET.NO_ENTRY: NoEntryAlert("Brake Hold Active"),
},
EventName.silentWrongGear: {
ET.WARNING: Alert(
"",
"",
AlertStatus.normal, AlertSize.none,
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, 0.),
ET.NO_ENTRY: Alert(
"Gear not D",
"openpilot Unavailable",
AlertStatus.normal, AlertSize.mid,
Priority.LOW, VisualAlert.none, AudibleAlert.none, 0.),
},
EventName.silentReverseGear: {
ET.PERMANENT: Alert(
"Reverse\nGear",
"",
AlertStatus.normal, AlertSize.full,
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .2, creation_delay=0.5),
ET.NO_ENTRY: NoEntryAlert("Reverse Gear"),
},
EventName.silentDoorOpen: {
ET.WARNING: Alert(
"",
"",
AlertStatus.normal, AlertSize.none,
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, 0.),
ET.NO_ENTRY: NoEntryAlert("Door Open"),
},
EventName.silentSeatbeltNotLatched: {
ET.WARNING: Alert(
"",
"",
AlertStatus.normal, AlertSize.none,
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, 0.),
ET.NO_ENTRY: NoEntryAlert("Seatbelt Unlatched"),
},
EventName.silentParkBrake: {
ET.WARNING: Alert(
"",
"",
AlertStatus.normal, AlertSize.none,
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, 0.),
ET.NO_ENTRY: NoEntryAlert("Parking Brake Engaged"),
},
EventName.controlsMismatchLateral: {
ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Controls Mismatch: Lateral"),
ET.NO_ENTRY: NoEntryAlert("Controls Mismatch: Lateral"),
},
EventName.hyundaiRadarTracksConfirmed: {
ET.PERMANENT: NormalPermanentAlert("Radar tracks available. Restart the car to initialize")
},
EventName.experimentalModeSwitched: {
ET.WARNING: NormalPermanentAlert("Experimental Mode Switched", duration=1.5)
}
}
+16 -36
View File
@@ -5,7 +5,7 @@ import threading
import cereal.messaging as messaging
from cereal import car, log, custom
from cereal import car, log
from msgq.visionipc import VisionIpcClient, VisionStreamType
from panda import ALTERNATIVE_EXPERIENCE
@@ -25,8 +25,7 @@ from openpilot.system.version import get_build_metadata
from openpilot.sunnypilot.mads.mads import ModularAssistiveDrivingSystem
from openpilot.sunnypilot.selfdrive.car.car_specific import CarSpecificEventsSP
from openpilot.sunnypilot.selfdrive.car.cruise_helpers import CruiseHelper
from openpilot.sunnypilot.selfdrive.selfdrived.events import EventsSP
from openpilot.sunnypilot.selfdrive.car.experimental_switcher import ExperimentalSwitcher
REPLAY = "REPLAY" in os.environ
SIMULATION = "SIMULATION" in os.environ
@@ -46,8 +45,8 @@ SafetyModel = car.CarParams.SafetyModel
IGNORED_SAFETY_MODES = (SafetyModel.silent, SafetyModel.noOutput)
class SelfdriveD(CruiseHelper):
def __init__(self, CP=None, CP_SP=None):
class SelfdriveD(ExperimentalSwitcher):
def __init__(self, CP=None):
self.params = Params()
# Ensure the current branch is cached, otherwise the first cycle lags
@@ -60,18 +59,11 @@ class SelfdriveD(CruiseHelper):
else:
self.CP = CP
if CP_SP is None:
cloudlog.info("selfdrived is waiting for CarParamsSP")
self.CP_SP = messaging.log_from_bytes(self.params.get("CarParamsSP", block=True), custom.CarParamsSP)
cloudlog.info("selfdrived got CarParamsSP")
else:
self.CP_SP = CP_SP
self.car_events = CarSpecificEvents(self.CP)
self.disengage_on_accelerator = not (self.CP.alternativeExperience & ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS)
# Setup sockets
self.pm = messaging.PubMaster(['selfdriveState', 'onroadEvents'] + ['selfdriveStateSP', 'onroadEventsSP'])
self.pm = messaging.PubMaster(['selfdriveState', 'onroadEvents'])
self.gps_location_service = get_gps_location_service(self.params)
self.gps_packets = [self.gps_location_service]
@@ -143,20 +135,18 @@ class SelfdriveD(CruiseHelper):
elif self.CP.passive:
self.events.add(EventName.dashcamMode, static=True)
self.events_sp = EventsSP()
self.events_sp_prev = []
self.mads = ModularAssistiveDrivingSystem(self)
sock_services = list(self.pm.sock.keys()) + ['selfdriveStateSP']
self.pm = messaging.PubMaster(sock_services)
self.car_events_sp = CarSpecificEventsSP(self.CP, self.params)
CruiseHelper.__init__(self, self.CP)
ExperimentalSwitcher.__init__(self, self.params)
def update_events(self, CS):
"""Compute onroadEvents from carState"""
self.events.clear()
self.events_sp.clear()
if self.sm['controlsState'].lateralControlState.which() == 'debugState':
self.events.add(EventName.joystickDebug)
@@ -194,7 +184,7 @@ class SelfdriveD(CruiseHelper):
self.events.add_from_msg(car_events)
car_events_sp = self.car_events_sp.update().to_msg()
self.events_sp.add_from_msg(car_events_sp)
self.events.add_from_msg(car_events_sp)
if self.CP.notCar:
# wait for everything to init first
@@ -380,7 +370,9 @@ class SelfdriveD(CruiseHelper):
if self.sm['modelV2'].frameDropPerc > 20:
self.events.add(EventName.modeldLagging)
CruiseHelper.update(self, CS, self.events_sp, self.experimental_mode)
# toggle experimental mode once on distance button hold
if self.CP.openpilotLongitudinalControl:
ExperimentalSwitcher.update(self, CS, self.events, self.experimental_mode)
# decrement personality on distance button press
if self.CP.openpilotLongitudinalControl:
@@ -446,13 +438,9 @@ class SelfdriveD(CruiseHelper):
clear_event_types.add(ET.NO_ENTRY)
pers = LONGITUDINAL_PERSONALITY_MAP[self.personality]
callback_args = [self.CP, CS, self.sm, self.is_metric,
self.state_machine.soft_disable_timer, pers]
alerts = self.events.create_alerts(self.state_machine.current_alert_types, callback_args)
alerts_sp = self.events_sp.create_alerts(self.state_machine.current_alert_types, callback_args)
self.AM.add_many(self.sm.frame, alerts + alerts_sp)
alerts = self.events.create_alerts(self.state_machine.current_alert_types, [self.CP, CS, self.sm, self.is_metric,
self.state_machine.soft_disable_timer, pers])
self.AM.add_many(self.sm.frame, alerts)
self.AM.process_alerts(self.sm.frame, clear_event_types)
def publish_selfdriveState(self, CS):
@@ -497,21 +485,13 @@ class SelfdriveD(CruiseHelper):
self.pm.send('selfdriveStateSP', ss_sp_msg)
# onroadEventsSP - logged every second or on change
if (self.sm.frame % int(1. / DT_CTRL) == 0) or (self.events_sp.names != self.events_sp_prev):
ce_send_sp = messaging.new_message('onroadEventsSP', len(self.events_sp))
ce_send_sp.valid = True
ce_send_sp.onroadEventsSP = self.events_sp.to_msg()
self.pm.send('onroadEventsSP', ce_send_sp)
self.events_sp_prev = self.events_sp.names.copy()
def step(self):
CS = self.data_sample()
self.update_events(CS)
if not self.CP.passive and self.initialized:
self.enabled, self.active = self.state_machine.update(self.events)
if not self.CP.notCar:
self.mads.update(CS)
self.mads.update(CS, self.sm)
self.update_alerts(CS)
self.publish_selfdriveState(CS)
@@ -1,10 +1,8 @@
import random
from openpilot.selfdrive.selfdrived.events import Alert, EVENTS
from openpilot.selfdrive.selfdrived.events import Alert, EmptyAlert, EVENTS
from openpilot.selfdrive.selfdrived.alertmanager import AlertManager
from openpilot.sunnypilot.selfdrive.selfdrived.events_base import EmptyAlert
class TestAlertManager:
@@ -23,7 +23,7 @@ from openpilot.common.prefix import OpenpilotPrefix
from openpilot.common.timeout import Timeout
from openpilot.common.realtime import DT_CTRL
from panda.python import ALTERNATIVE_EXPERIENCE
from openpilot.selfdrive.car.card import can_comm_callbacks, convert_to_capnp
from openpilot.selfdrive.car.card import can_comm_callbacks
from openpilot.system.manager.process_config import managed_processes
from openpilot.selfdrive.test.process_replay.vision_meta import meta_from_camera_state, available_streams
from openpilot.selfdrive.test.process_replay.migration import migrate_all
@@ -344,7 +344,6 @@ def get_car_params_callback(rc, pm, msgs, fingerprint):
if fingerprint:
CarInterface, _, _, _ = interfaces[fingerprint]
CP = CarInterface.get_non_essential_params(fingerprint)
CP_SP = CarInterface.get_non_essential_params_sp(CP, fingerprint)
else:
can = DummySocket()
sendcan = DummySocket()
@@ -365,14 +364,12 @@ def get_car_params_callback(rc, pm, msgs, fingerprint):
with car.CarParams.from_bytes(cached_params_raw) as _cached_params:
cached_params = _cached_params
_CI = get_car(*can_callbacks, lambda obd: None, Params().get_bool("ExperimentalLongitudinalEnabled"), cached_params=cached_params)
CP, CP_SP = _CI.CP, _CI.CP_SP
CP = get_car(*can_callbacks, lambda obd: None, Params().get_bool("ExperimentalLongitudinalEnabled"), cached_params=cached_params).CP
if not params.get_bool("DisengageOnAccelerator"):
CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS
params.put("CarParams", CP.to_bytes())
params.put("CarParamsSP", convert_to_capnp(CP_SP).to_bytes())
def selfdrived_rcv_callback(msg, cfg, frame):
+1 -1
View File
@@ -97,7 +97,7 @@ public:
void setPrimeType(PrimeState::Type type);
WifiManager* wifi = nullptr;
protected:
private:
QStackedLayout* main_layout = nullptr;
QWidget* wifiScreen = nullptr;
AdvancedNetworking* an = nullptr;
-20
View File
@@ -87,16 +87,6 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
"../assets/offroad/icon_speed_limit.png",
longi_button_texts);
// accel controller
std::vector<QString> accel_personality_texts{tr("Sport"), tr("Normal"), tr("Eco"), tr("Stock")};
accel_personality_setting = new ButtonParamControlSP("AccelPersonality", tr("Acceleration Personality"),
tr("Normal is recommended. In sport mode, sunnypilot will provide aggressive acceleration for a dynamic driving experience. "
"In eco mode, sunnypilot will apply smoother and more relaxed acceleration. On supported cars, you can cycle through these "
"acceleration personality within Onroad Settings on the driving screen."),
"",
accel_personality_texts);
accel_personality_setting->showDescription();
// set up uiState update for personality setting
QObject::connect(uiState(), &UIState::uiUpdate, this, &TogglesPanel::updateState);
@@ -112,7 +102,6 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
// insert longitudinal personality after NDOG toggle
if (param == "DisengageOnAccelerator") {
addItem(long_personality_setting);
addItem(accel_personality_setting);
}
}
@@ -138,13 +127,6 @@ void TogglesPanel::updateState(const UIState &s) {
}
uiState()->scene.personality = personality;
}
if (sm.updated("longitudinalPlanSP")) {
auto accel_personality = sm["longitudinalPlanSP"].getLongitudinalPlanSP().getAccelPersonality();
if (accel_personality != s.scene.accel_personality && s.scene.started && isVisible()) {
accel_personality_setting->setCheckedButton(static_cast<int>(accel_personality));
}
uiState()->scene.accel_personality = accel_personality;
}
}
void TogglesPanel::expandToggleDescription(const QString &param) {
@@ -187,12 +169,10 @@ void TogglesPanel::updateToggles() {
experimental_mode_toggle->setEnabled(true);
experimental_mode_toggle->setDescription(e2e_description);
long_personality_setting->setEnabled(true);
accel_personality_setting->setEnabled(true);
} else {
// no long for now
experimental_mode_toggle->setEnabled(false);
long_personality_setting->setEnabled(false);
accel_personality_setting->setEnabled(true);
params.remove("ExperimentalMode");
const QString unavailable = tr("Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control.");
-1
View File
@@ -83,7 +83,6 @@ protected:
Params params;
std::map<std::string, ParamControl*> toggles;
ButtonParamControl *long_personality_setting;
ButtonParamControl *accel_personality_setting;
virtual void updateToggles();
};
-2
View File
@@ -4,7 +4,6 @@ widgets_src = [
"sunnypilot/qt/widgets/drive_stats.cc",
"sunnypilot/qt/widgets/prime.cc",
"sunnypilot/qt/widgets/scrollview.cc",
"sunnypilot/qt/network/networking.cc",
]
qt_util = [
@@ -23,7 +22,6 @@ qt_src = [
"sunnypilot/qt/offroad/settings/sunnylink_panel.cc",
"sunnypilot/qt/offroad/settings/sunnypilot_panel.cc",
"sunnypilot/qt/offroad/settings/trips_panel.cc",
"sunnypilot/qt/offroad/settings/vehicle_panel.cc",
"sunnypilot/qt/onroad/annotated_camera.cc",
"sunnypilot/qt/onroad/buttons.cc",
"sunnypilot/qt/onroad/hud.cc",
@@ -1,45 +0,0 @@
#include "selfdrive/ui/sunnypilot/qt/network/networking.h"
#include <QVBoxLayout>
#include <QPushButton>
#include <QStackedLayout>
NetworkingSP::NetworkingSP(QWidget *parent) : Networking(parent) {
auto vlayout = wifiScreen->findChild<QVBoxLayout*>();
auto hlayout = new QHBoxLayout();
// Create and setup scan button
auto scanButton = new QPushButton(tr("Scan"));
scanButton->setObjectName("scan_btn");
scanButton->setFixedSize(400, 100);
connect(wifi, &WifiManager::refreshSignal, this, [=]() { scanButton->setText(tr("Scan")); scanButton->setEnabled(true); });
connect(scanButton, &QPushButton::clicked, [=]() { scanButton->setText(tr("Scanning...")); scanButton->setEnabled(false); wifi->requestScan(); });
hlayout->addWidget(scanButton);
hlayout->addStretch(1);
// Look for an existing Advanced button
QPushButton* existingAdvanced = wifiScreen->findChild<QPushButton*>("advanced_btn");
if (existingAdvanced) {
hlayout->addWidget(existingAdvanced);
}
// Insert our new layout at the top of vlayout
vlayout->setMargin(40);
vlayout->insertLayout(0, hlayout);
// Add our scan button to the existing style selectors
auto newStyleSheet = styleSheet().replace(
", #advanced_btn ",
", #advanced_btn, #scan_btn "
).replace(
", #advanced_btn:pressed",
", #advanced_btn:pressed, #scan_btn:pressed"
).append(R"(
#scan_btn:disabled {
background-color: #121212;
color: #33FFFFFF;
}
)");
setStyleSheet(newStyleSheet);
}
@@ -1,17 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/qt/network/networking.h"
class NetworkingSP : public Networking {
Q_OBJECT
public:
explicit NetworkingSP(QWidget *parent = nullptr);
};
@@ -74,17 +74,23 @@ DevicePanelSP::DevicePanelSP(SettingsWindowSP *parent) : DevicePanel(parent) {
addItem(device_grid_layout);
QObject::connect(uiState(), &UIState::offroadTransition, [=](bool offroad) {
for (auto btn : findChildren<PushButtonSP*>()) {
btn->setEnabled(offroad);
}
});
// offroad mode and power buttons
QHBoxLayout *power_layout = new QHBoxLayout();
power_layout->setSpacing(5);
PushButtonSP *rebootBtn = new PushButtonSP(tr("Reboot"), 720, this);
QPushButton *rebootBtn = new PushButtonSP(tr("Reboot"), 720, this);
rebootBtn->setStyleSheet(rebootButtonStyle);
power_layout->addWidget(rebootBtn);
QObject::connect(rebootBtn, &PushButtonSP::clicked, this, &DevicePanelSP::reboot);
PushButtonSP *poweroffBtn = new PushButtonSP(tr("Power Off"), 720, this);
QPushButton *poweroffBtn = new PushButtonSP(tr("Power Off"), 720, this);
poweroffBtn->setStyleSheet(powerOffButtonStyle);
power_layout->addWidget(poweroffBtn);
QObject::connect(poweroffBtn, &PushButtonSP::clicked, this, &DevicePanelSP::poweroff);
@@ -93,60 +99,5 @@ DevicePanelSP::DevicePanelSP(SettingsWindowSP *parent) : DevicePanel(parent) {
connect(uiState(), &UIState::offroadTransition, poweroffBtn, &PushButtonSP::setVisible);
}
offroadBtn = new PushButtonSP(tr("Offroad Mode"));
offroadBtn->setFixedWidth(power_layout->sizeHint().width());
QObject::connect(offroadBtn, &PushButtonSP::clicked, this, &DevicePanelSP::setOffroadMode);
QVBoxLayout *power_group_layout = new QVBoxLayout();
power_group_layout->setSpacing(30);
power_group_layout->addWidget(offroadBtn, 0, Qt::AlignHCenter);
power_group_layout->addLayout(power_layout);
addItem(power_group_layout);
QObject::connect(uiState(), &UIState::offroadTransition, [=](bool offroad) {
for (auto btn : findChildren<PushButtonSP*>()) {
if (btn != rebootBtn && btn != poweroffBtn && btn != offroadBtn) {
btn->setEnabled(offroad);
}
}
});
}
void DevicePanelSP::setOffroadMode() {
if (!uiState()->engaged()) {
if (params.getBool("OffroadMode")) {
if (ConfirmationDialog::confirm(tr("Are you sure you want to exit Always Offroad mode?"), tr("Confirm"), this)) {
// Check engaged again in case it changed while the dialog was open
if (!uiState()->engaged()) {
params.remove("OffroadMode");
}
}
} else {
if (ConfirmationDialog::confirm(tr("Are you sure you want to enter Always Offroad mode?"), tr("Confirm"), this)) {
// Check engaged again in case it changed while the dialog was open
if (!uiState()->engaged()) {
params.putBool("OffroadMode", true);
}
}
}
} else {
ConfirmationDialog::alert(tr("Disengage to Enter Always Offroad Mode"), this);
}
updateState();
}
void DevicePanelSP::showEvent(QShowEvent *event) {
updateState();
}
void DevicePanelSP::updateState() {
if (!isVisible()) {
return;
}
bool offroad_mode_param = params.getBool("OffroadMode");
offroadBtn->setText(offroad_mode_param ? tr("Exit Always Offroad") : tr("Always Offroad"));
offroadBtn->setStyleSheet(offroad_mode_param ? alwaysOffroadStyle : autoOffroadStyle);
addItem(power_layout);
}
@@ -15,43 +15,9 @@ class DevicePanelSP : public DevicePanel {
public:
explicit DevicePanelSP(SettingsWindowSP *parent = 0);
void showEvent(QShowEvent *event) override;
void setOffroadMode();
void updateState();
private:
std::map<QString, PushButtonSP*> buttons;
PushButtonSP *offroadBtn;
const QString alwaysOffroadStyle = R"(
PushButtonSP {
border-radius: 20px;
font-size: 50px;
font-weight: 450;
height: 150px;
padding: 0 25px 0 25px;
color: #FFFFFF;
background-color: #393939;
}
PushButtonSP:pressed {
background-color: #4A4A4A;
}
)";
const QString autoOffroadStyle = R"(
PushButtonSP {
border-radius: 20px;
font-size: 50px;
font-weight: 450;
height: 150px;
padding: 0 25px 0 25px;
color: #FFFFFF;
background-color: #E22C2C;
}
PushButtonSP:pressed {
background-color: #FF2424;
}
)";
const QString rebootButtonStyle = R"(
PushButtonSP {
@@ -7,16 +7,15 @@
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/qt/network/networking.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h"
#include "selfdrive/ui/qt/offroad/developer_panel.h"
#include "selfdrive/ui/sunnypilot/qt/network/networking.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink_panel.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot_panel.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/trips_panel.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h"
TogglesPanelSP::TogglesPanelSP(SettingsWindowSP *parent) : TogglesPanel(parent) {
QObject::connect(uiStateSP(), &UIStateSP::uiUpdate, this, &TogglesPanelSP::updateState);
@@ -67,8 +66,8 @@ SettingsWindowSP::SettingsWindowSP(QWidget *parent) : SettingsWindow(parent) {
TogglesPanelSP *toggles = new TogglesPanelSP(this);
QObject::connect(this, &SettingsWindowSP::expandToggleDescription, toggles, &TogglesPanel::expandToggleDescription);
auto networking = new NetworkingSP(this);
QObject::connect(uiState()->prime_state, &PrimeState::changed, networking, &NetworkingSP::setPrimeType);
auto networking = new Networking(this);
QObject::connect(uiState()->prime_state, &PrimeState::changed, networking, &Networking::setPrimeType);
QList<PanelInfo> panels = {
PanelInfo(" " + tr("Device"), device, "../../sunnypilot/selfdrive/assets/offroad/icon_home.svg"),
@@ -78,7 +77,6 @@ SettingsWindowSP::SettingsWindowSP(QWidget *parent) : SettingsWindow(parent) {
PanelInfo(" " + tr("Software"), new SoftwarePanelSP(this), "../../sunnypilot/selfdrive/assets/offroad/icon_software.png"),
PanelInfo(" " + tr("sunnypilot"), new SunnypilotPanel(this), "../assets/images/button_home.png"),
PanelInfo(" " + tr("Trips"), new TripsPanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_trips.png"),
PanelInfo(" " + tr("Vehicle"), new VehiclePanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_vehicle.png"),
PanelInfo(" " + tr("Developer"), new DeveloperPanel(this), "../assets/offroad/icon_shell.png"),
};
@@ -1,11 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h"
VehiclePanel::VehiclePanel(QWidget *parent) : QWidget(parent) {
}
@@ -1,17 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
class VehiclePanel : public QWidget {
Q_OBJECT
public:
explicit VehiclePanel(QWidget *parent = nullptr);
};
+1 -18
View File
@@ -40,14 +40,6 @@ def setup_homescreen(click, pm: PubMaster, scroll=None):
def setup_settings_device(click, pm: PubMaster, scroll=None):
click(100, 100)
def setup_settings_network(click, pm: PubMaster, scroll=None):
setup_settings_device(click, pm)
click(278, 405)
def setup_settings_network_advanced(click, pm: PubMaster, scroll=None):
setup_settings_network(click, pm)
click(1913, 90)
def setup_settings_toggles(click, pm: PubMaster, scroll=None):
setup_settings_device(click, pm)
click(278, 632)
@@ -60,7 +52,7 @@ def setup_settings_software(click, pm: PubMaster, scroll=None):
def setup_settings_developer(click, pm: PubMaster, scroll=None):
setup_settings_device(click, pm)
scroll(-400, 278, 962)
scroll(-100, 278, 962)
click(278, 970)
time.sleep(UI_DELAY)
@@ -215,19 +207,11 @@ def setup_settings_trips(click, pm: PubMaster, scroll=None):
click(278, 962)
time.sleep(UI_DELAY)
def setup_settings_vehicle(click, pm: PubMaster, scroll=None):
setup_settings_device(click, pm)
scroll(-400, 278, 862)
click(278, 862)
time.sleep(UI_DELAY)
CASES = {
"homescreen": setup_homescreen,
"prime": setup_homescreen,
"pair_device": setup_pair_device,
"settings_device": setup_settings_device,
"settings_network": setup_settings_network,
"settings_network_advanced": setup_settings_network_advanced,
"settings_toggles": setup_settings_toggles,
"settings_software": setup_settings_software,
"settings_developer": setup_settings_developer,
@@ -253,7 +237,6 @@ CASES.update({
"settings_sunnypilot": setup_settings_sunnypilot,
"settings_sunnypilot_mads": setup_settings_sunnypilot_mads,
"settings_trips": setup_settings_trips,
"settings_vehicle": setup_settings_vehicle,
})
TEST_DIR = pathlib.Path(__file__).parent
-75
View File
@@ -321,34 +321,6 @@
<source>Power Off</source>
<translation type="unfinished">إيقاف التشغيل</translation>
</message>
<message>
<source>Offroad Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to exit Always Offroad mode?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Confirm</source>
<translation type="unfinished">تأكيد</translation>
</message>
<message>
<source>Are you sure you want to enter Always Offroad mode?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage to Enter Always Offroad Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Exit Always Offroad</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Always Offroad</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DriveStats</name>
@@ -507,21 +479,6 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<translation>كلمة مرور خاطئة</translation>
</message>
</context>
<context>
<name>NetworkingSP</name>
<message>
<source>Advanced</source>
<translation type="obsolete">متقدم</translation>
</message>
<message>
<source>Scan</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Scanning...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OffroadAlert</name>
<message>
@@ -570,10 +527,6 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<source>openpilot detected a change in the device&apos;s mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield.</source>
<translation>لقد اكتشف openpilot تغييراً في موقع تركيب الجهاز. تأكد من تثبيت الجهاز بشكل كامل في موقعه وتثبيته بإحكام على الزجاج الأمامي.</translation>
</message>
<message>
<source>sunnypilot is now in Always Offroad mode. sunnypilot won&apos;t start until Always Offroad mode is disabled. Go to &quot;Settings&quot; -&gt; &quot;Device&quot; to exit Always Offroad mode.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OffroadHome</name>
@@ -858,10 +811,6 @@ This may take up to a minute.</source>
<source>Trips</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vehicle</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Setup</name>
@@ -1494,30 +1443,6 @@ This may take up to a minute.</source>
<source>Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Sport</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Normal</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Eco</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stock</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Acceleration Personality</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Normal is recommended. In sport mode, sunnypilot will provide aggressive acceleration for a dynamic driving experience. In eco mode, sunnypilot will apply smoother and more relaxed acceleration. On supported cars, you can cycle through these acceleration personality within Onroad Settings on the driving screen.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Updater</name>
-75
View File
@@ -321,34 +321,6 @@
<source>Power Off</source>
<translation type="unfinished">Ausschalten</translation>
</message>
<message>
<source>Offroad Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to exit Always Offroad mode?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Confirm</source>
<translation type="unfinished">Bestätigen</translation>
</message>
<message>
<source>Are you sure you want to enter Always Offroad mode?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage to Enter Always Offroad Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Exit Always Offroad</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Always Offroad</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DriveStats</name>
@@ -503,21 +475,6 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<translation>Falsches Passwort</translation>
</message>
</context>
<context>
<name>NetworkingSP</name>
<message>
<source>Advanced</source>
<translation type="obsolete">Erweitert</translation>
</message>
<message>
<source>Scan</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Scanning...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OffroadAlert</name>
<message>
@@ -565,10 +522,6 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<source>Device temperature too high. System cooling down before starting. Current internal component temperature: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>sunnypilot is now in Always Offroad mode. sunnypilot won&apos;t start until Always Offroad mode is disabled. Go to &quot;Settings&quot; -&gt; &quot;Device&quot; to exit Always Offroad mode.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OffroadHome</name>
@@ -840,10 +793,6 @@ This may take up to a minute.</source>
<source>Trips</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vehicle</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Setup</name>
@@ -1478,30 +1427,6 @@ This may take up to a minute.</source>
<source>Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Sport</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Normal</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Eco</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stock</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Acceleration Personality</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Normal is recommended. In sport mode, sunnypilot will provide aggressive acceleration for a dynamic driving experience. In eco mode, sunnypilot will apply smoother and more relaxed acceleration. On supported cars, you can cycle through these acceleration personality within Onroad Settings on the driving screen.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Updater</name>
-71
View File
@@ -321,34 +321,6 @@
<source>Power Off</source>
<translation type="unfinished">Apagar</translation>
</message>
<message>
<source>Offroad Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to exit Always Offroad mode?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Confirm</source>
<translation type="unfinished">Confirmar</translation>
</message>
<message>
<source>Are you sure you want to enter Always Offroad mode?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage to Enter Always Offroad Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Exit Always Offroad</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Always Offroad</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DriveStats</name>
@@ -503,17 +475,6 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<translation>Contraseña incorrecta</translation>
</message>
</context>
<context>
<name>NetworkingSP</name>
<message>
<source>Scan</source>
<translation>Escanear</translation>
</message>
<message>
<source>Scanning...</source>
<translation>Escaneando...</translation>
</message>
</context>
<context>
<name>OffroadAlert</name>
<message>
@@ -562,10 +523,6 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<source>openpilot detected a change in the device&apos;s mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield.</source>
<translation>openpilot detectó un cambio en la posición de montaje del dispositivo. Asegúrese de que el dispositivo esté completamente asentado en el soporte y que el soporte esté firmemente asegurado al parabrisas.</translation>
</message>
<message>
<source>sunnypilot is now in Always Offroad mode. sunnypilot won&apos;t start until Always Offroad mode is disabled. Go to &quot;Settings&quot; -&gt; &quot;Device&quot; to exit Always Offroad mode.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OffroadHome</name>
@@ -838,10 +795,6 @@ Esto puede tardar un minuto.</translation>
<source>Trips</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vehicle</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Setup</name>
@@ -1474,30 +1427,6 @@ Esto puede tardar un minuto.</translation>
<source>Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Sport</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Normal</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Eco</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stock</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Acceleration Personality</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Normal is recommended. In sport mode, sunnypilot will provide aggressive acceleration for a dynamic driving experience. In eco mode, sunnypilot will apply smoother and more relaxed acceleration. On supported cars, you can cycle through these acceleration personality within Onroad Settings on the driving screen.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Updater</name>
-75
View File
@@ -321,34 +321,6 @@
<source>Power Off</source>
<translation type="unfinished">Éteindre</translation>
</message>
<message>
<source>Offroad Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to exit Always Offroad mode?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Confirm</source>
<translation type="unfinished">Confirmer</translation>
</message>
<message>
<source>Are you sure you want to enter Always Offroad mode?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage to Enter Always Offroad Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Exit Always Offroad</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Always Offroad</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DriveStats</name>
@@ -503,21 +475,6 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<translation>Mot de passe incorrect</translation>
</message>
</context>
<context>
<name>NetworkingSP</name>
<message>
<source>Advanced</source>
<translation type="obsolete">Avancé</translation>
</message>
<message>
<source>Scan</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Scanning...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OffroadAlert</name>
<message>
@@ -566,10 +523,6 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<source>openpilot detected a change in the device&apos;s mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield.</source>
<translation>openpilot a détecté un changement dans la position de montage de l&apos;appareil. Assurez-vous que l&apos;appareil est totalement inséré dans le support et que le support est fermement fixé au pare-brise.</translation>
</message>
<message>
<source>sunnypilot is now in Always Offroad mode. sunnypilot won&apos;t start until Always Offroad mode is disabled. Go to &quot;Settings&quot; -&gt; &quot;Device&quot; to exit Always Offroad mode.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OffroadHome</name>
@@ -842,10 +795,6 @@ Cela peut prendre jusqu&apos;à une minute.</translation>
<source>Trips</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vehicle</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Setup</name>
@@ -1478,30 +1427,6 @@ Cela peut prendre jusqu&apos;à une minute.</translation>
<source>Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Sport</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Normal</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Eco</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stock</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Acceleration Personality</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Normal is recommended. In sport mode, sunnypilot will provide aggressive acceleration for a dynamic driving experience. In eco mode, sunnypilot will apply smoother and more relaxed acceleration. On supported cars, you can cycle through these acceleration personality within Onroad Settings on the driving screen.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Updater</name>
-75
View File
@@ -321,34 +321,6 @@
<source>Power Off</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Offroad Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to exit Always Offroad mode?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to enter Always Offroad mode?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage to Enter Always Offroad Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Exit Always Offroad</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Always Offroad</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DriveStats</name>
@@ -502,21 +474,6 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<translation></translation>
</message>
</context>
<context>
<name>NetworkingSP</name>
<message>
<source>Advanced</source>
<translation type="obsolete"></translation>
</message>
<message>
<source>Scan</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Scanning...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OffroadAlert</name>
<message>
@@ -564,10 +521,6 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<source>Device temperature too high. System cooling down before starting. Current internal component temperature: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>sunnypilot is now in Always Offroad mode. sunnypilot won&apos;t start until Always Offroad mode is disabled. Go to &quot;Settings&quot; -&gt; &quot;Device&quot; to exit Always Offroad mode.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OffroadHome</name>
@@ -836,10 +789,6 @@ This may take up to a minute.</source>
<source>Trips</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vehicle</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Setup</name>
@@ -1472,30 +1421,6 @@ This may take up to a minute.</source>
<source>Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Sport</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Normal</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Eco</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stock</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Acceleration Personality</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Normal is recommended. In sport mode, sunnypilot will provide aggressive acceleration for a dynamic driving experience. In eco mode, sunnypilot will apply smoother and more relaxed acceleration. On supported cars, you can cycle through these acceleration personality within Onroad Settings on the driving screen.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Updater</name>
-75
View File
@@ -321,34 +321,6 @@
<source>Power Off</source>
<translation type="unfinished"> </translation>
</message>
<message>
<source>Offroad Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to exit Always Offroad mode?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to enter Always Offroad mode?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage to Enter Always Offroad Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Exit Always Offroad</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Always Offroad</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DriveStats</name>
@@ -502,21 +474,6 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<translation> </translation>
</message>
</context>
<context>
<name>NetworkingSP</name>
<message>
<source>Advanced</source>
<translation type="obsolete"> </translation>
</message>
<message>
<source>Scan</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Scanning...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OffroadAlert</name>
<message>
@@ -565,10 +522,6 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<source>Device temperature too high. System cooling down before starting. Current internal component temperature: %1</source>
<translation> . . : %1</translation>
</message>
<message>
<source>sunnypilot is now in Always Offroad mode. sunnypilot won&apos;t start until Always Offroad mode is disabled. Go to &quot;Settings&quot; -&gt; &quot;Device&quot; to exit Always Offroad mode.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OffroadHome</name>
@@ -838,10 +791,6 @@ This may take up to a minute.</source>
<source>Trips</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vehicle</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Setup</name>
@@ -1474,30 +1423,6 @@ This may take up to a minute.</source>
<source>Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Sport</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Normal</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Eco</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stock</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Acceleration Personality</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Normal is recommended. In sport mode, sunnypilot will provide aggressive acceleration for a dynamic driving experience. In eco mode, sunnypilot will apply smoother and more relaxed acceleration. On supported cars, you can cycle through these acceleration personality within Onroad Settings on the driving screen.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Updater</name>
-75
View File
@@ -321,34 +321,6 @@
<source>Power Off</source>
<translation type="unfinished">Desligar</translation>
</message>
<message>
<source>Offroad Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to exit Always Offroad mode?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Confirm</source>
<translation type="unfinished">Confirmar</translation>
</message>
<message>
<source>Are you sure you want to enter Always Offroad mode?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage to Enter Always Offroad Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Exit Always Offroad</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Always Offroad</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DriveStats</name>
@@ -503,21 +475,6 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<translation>Senha incorreta</translation>
</message>
</context>
<context>
<name>NetworkingSP</name>
<message>
<source>Advanced</source>
<translation type="obsolete">Avançado</translation>
</message>
<message>
<source>Scan</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Scanning...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OffroadAlert</name>
<message>
@@ -566,10 +523,6 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<source>Device temperature too high. System cooling down before starting. Current internal component temperature: %1</source>
<translation>Temperatura do dispositivo muito alta. O sistema está sendo resfriado antes de iniciar. A temperatura atual do componente interno é: %1</translation>
</message>
<message>
<source>sunnypilot is now in Always Offroad mode. sunnypilot won&apos;t start until Always Offroad mode is disabled. Go to &quot;Settings&quot; -&gt; &quot;Device&quot; to exit Always Offroad mode.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OffroadHome</name>
@@ -842,10 +795,6 @@ Isso pode levar até um minuto.</translation>
<source>Trips</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vehicle</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Setup</name>
@@ -1478,30 +1427,6 @@ Isso pode levar até um minuto.</translation>
<source>Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Sport</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Normal</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Eco</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stock</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Acceleration Personality</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Normal is recommended. In sport mode, sunnypilot will provide aggressive acceleration for a dynamic driving experience. In eco mode, sunnypilot will apply smoother and more relaxed acceleration. On supported cars, you can cycle through these acceleration personality within Onroad Settings on the driving screen.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Updater</name>
-75
View File
@@ -321,34 +321,6 @@
<source>Power Off</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Offroad Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to exit Always Offroad mode?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to enter Always Offroad mode?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage to Enter Always Offroad Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Exit Always Offroad</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Always Offroad</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DriveStats</name>
@@ -502,21 +474,6 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<translation></translation>
</message>
</context>
<context>
<name>NetworkingSP</name>
<message>
<source>Advanced</source>
<translation type="obsolete"></translation>
</message>
<message>
<source>Scan</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Scanning...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OffroadAlert</name>
<message>
@@ -565,10 +522,6 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<source>openpilot detected a change in the device&apos;s mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield.</source>
<translation>openpilot </translation>
</message>
<message>
<source>sunnypilot is now in Always Offroad mode. sunnypilot won&apos;t start until Always Offroad mode is disabled. Go to &quot;Settings&quot; -&gt; &quot;Device&quot; to exit Always Offroad mode.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OffroadHome</name>
@@ -838,10 +791,6 @@ This may take up to a minute.</source>
<source>Trips</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vehicle</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Setup</name>
@@ -1474,30 +1423,6 @@ This may take up to a minute.</source>
<source>Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Sport</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Normal</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Eco</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stock</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Acceleration Personality</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Normal is recommended. In sport mode, sunnypilot will provide aggressive acceleration for a dynamic driving experience. In eco mode, sunnypilot will apply smoother and more relaxed acceleration. On supported cars, you can cycle through these acceleration personality within Onroad Settings on the driving screen.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Updater</name>
-75
View File
@@ -321,34 +321,6 @@
<source>Power Off</source>
<translation type="unfinished">Sistemi kapat</translation>
</message>
<message>
<source>Offroad Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to exit Always Offroad mode?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Confirm</source>
<translation type="unfinished">Onayla</translation>
</message>
<message>
<source>Are you sure you want to enter Always Offroad mode?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage to Enter Always Offroad Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Exit Always Offroad</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Always Offroad</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DriveStats</name>
@@ -502,21 +474,6 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<translation>Yalnış parola</translation>
</message>
</context>
<context>
<name>NetworkingSP</name>
<message>
<source>Advanced</source>
<translation type="obsolete">Gelişmiş Seçenekler</translation>
</message>
<message>
<source>Scan</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Scanning...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OffroadAlert</name>
<message>
@@ -564,10 +521,6 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<source>openpilot detected a change in the device&apos;s mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>sunnypilot is now in Always Offroad mode. sunnypilot won&apos;t start until Always Offroad mode is disabled. Go to &quot;Settings&quot; -&gt; &quot;Device&quot; to exit Always Offroad mode.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OffroadHome</name>
@@ -836,10 +789,6 @@ This may take up to a minute.</source>
<source>Trips</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vehicle</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Setup</name>
@@ -1472,30 +1421,6 @@ This may take up to a minute.</source>
<source>Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Sport</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Normal</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Eco</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stock</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Acceleration Personality</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Normal is recommended. In sport mode, sunnypilot will provide aggressive acceleration for a dynamic driving experience. In eco mode, sunnypilot will apply smoother and more relaxed acceleration. On supported cars, you can cycle through these acceleration personality within Onroad Settings on the driving screen.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Updater</name>
-75
View File
@@ -321,34 +321,6 @@
<source>Power Off</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Offroad Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to exit Always Offroad mode?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to enter Always Offroad mode?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage to Enter Always Offroad Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Exit Always Offroad</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Always Offroad</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DriveStats</name>
@@ -502,21 +474,6 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<translation></translation>
</message>
</context>
<context>
<name>NetworkingSP</name>
<message>
<source>Advanced</source>
<translation type="obsolete"></translation>
</message>
<message>
<source>Scan</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Scanning...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OffroadAlert</name>
<message>
@@ -565,10 +522,6 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<source>Device temperature too high. System cooling down before starting. Current internal component temperature: %1</source>
<translation>%1</translation>
</message>
<message>
<source>sunnypilot is now in Always Offroad mode. sunnypilot won&apos;t start until Always Offroad mode is disabled. Go to &quot;Settings&quot; -&gt; &quot;Device&quot; to exit Always Offroad mode.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OffroadHome</name>
@@ -838,10 +791,6 @@ This may take up to a minute.</source>
<source>Trips</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vehicle</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Setup</name>
@@ -1474,30 +1423,6 @@ This may take up to a minute.</source>
<source>Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Sport</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Normal</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Eco</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stock</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Acceleration Personality</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Normal is recommended. In sport mode, sunnypilot will provide aggressive acceleration for a dynamic driving experience. In eco mode, sunnypilot will apply smoother and more relaxed acceleration. On supported cars, you can cycle through these acceleration personality within Onroad Settings on the driving screen.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Updater</name>
-75
View File
@@ -321,34 +321,6 @@
<source>Power Off</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Offroad Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to exit Always Offroad mode?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to enter Always Offroad mode?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage to Enter Always Offroad Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Exit Always Offroad</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Always Offroad</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DriveStats</name>
@@ -502,21 +474,6 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<translation></translation>
</message>
</context>
<context>
<name>NetworkingSP</name>
<message>
<source>Advanced</source>
<translation type="obsolete"></translation>
</message>
<message>
<source>Scan</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Scanning...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OffroadAlert</name>
<message>
@@ -565,10 +522,6 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<source>Device temperature too high. System cooling down before starting. Current internal component temperature: %1</source>
<translation>%1</translation>
</message>
<message>
<source>sunnypilot is now in Always Offroad mode. sunnypilot won&apos;t start until Always Offroad mode is disabled. Go to &quot;Settings&quot; -&gt; &quot;Device&quot; to exit Always Offroad mode.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OffroadHome</name>
@@ -838,10 +791,6 @@ This may take up to a minute.</source>
<source>Trips</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vehicle</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Setup</name>
@@ -1474,30 +1423,6 @@ This may take up to a minute.</source>
<source>Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Sport</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Normal</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Eco</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stock</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Acceleration Personality</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Normal is recommended. In sport mode, sunnypilot will provide aggressive acceleration for a dynamic driving experience. In eco mode, sunnypilot will apply smoother and more relaxed acceleration. On supported cars, you can cycle through these acceleration personality within Onroad Settings on the driving screen.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Updater</name>
+2 -2
View File
@@ -75,8 +75,8 @@ void UIState::updateStatus() {
auto state = ss.getState();
auto state_mads = mads.getState();
if (state == cereal::SelfdriveState::OpenpilotState::PRE_ENABLED || state == cereal::SelfdriveState::OpenpilotState::OVERRIDING ||
state_mads == cereal::ModularAssistiveDrivingSystem::ModularAssistiveDrivingSystemState::PAUSED ||
state_mads == cereal::ModularAssistiveDrivingSystem::ModularAssistiveDrivingSystemState::OVERRIDING) {
state_mads == cereal::SelfdriveStateSP::ModularAssistiveDrivingSystem::ModularAssistiveDrivingSystemState::PAUSED ||
state_mads == cereal::SelfdriveStateSP::ModularAssistiveDrivingSystem::ModularAssistiveDrivingSystemState::OVERRIDING) {
status = STATUS_OVERRIDE;
} else {
if (mads.getAvailable()) {
-1
View File
@@ -60,7 +60,6 @@ typedef struct UIScene {
cereal::PandaState::PandaType pandaType;
cereal::LongitudinalPersonality personality;
cereal::LongitudinalPlanSP::AccelerationPersonality accel_personality;
float light_sensor = -1;
bool started, ignition, is_metric;
+2 -2
View File
@@ -48,13 +48,13 @@ class MadsParams:
if pause_lateral_on_brake:
CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISENGAGE_LATERAL_ON_BRAKE
def set_car_specific_params(self, CP, CP_SP):
def set_car_specific_params(self, CP):
if CP.carName == "hyundai":
# TODO-SP: This should be separated from MADS module for future implementations
# Use "HyundaiLongitudinalMainCruiseToggleable" param
hyundai_cruise_main_toggleable = True
if hyundai_cruise_main_toggleable:
CP_SP.flags |= HyundaiFlagsSP.LONGITUDINAL_MAIN_CRUISE_TOGGLEABLE.value
CP.sunnypilotFlags |= HyundaiFlagsSP.LONGITUDINAL_MAIN_CRUISE_TOGGLEABLE.value
CP.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_LONG_MAIN_CRUISE_TOGGLEABLE
# MADS is currently not supported in Tesla due to lack of consistent states to engage controls
+37 -27
View File
@@ -24,7 +24,7 @@ THE SOFTWARE.
Last updated: July 29, 2024
"""
from cereal import car, log, custom
from cereal import messaging, car, log, custom
from opendbc.car.hyundai.values import HyundaiFlags
from opendbc.sunnypilot.car.hyundai.values import HyundaiFlagsSP
@@ -32,10 +32,9 @@ from opendbc.sunnypilot.car.hyundai.values import HyundaiFlagsSP
from openpilot.sunnypilot.mads.helpers import MadsParams
from openpilot.sunnypilot.mads.state import StateMachine, GEARS_ALLOW_PAUSED_SILENT
State = custom.ModularAssistiveDrivingSystem.ModularAssistiveDrivingSystemState
State = custom.SelfdriveStateSP.ModularAssistiveDrivingSystem.ModularAssistiveDrivingSystemState
ButtonType = car.CarState.ButtonEvent.Type
EventName = log.OnroadEvent.EventName
EventNameSP = custom.OnroadEventSP.EventName
SafetyModel = car.CarParams.SafetyModel
SET_SPEED_BUTTONS = (ButtonType.accelCruise, ButtonType.resumeCruise, ButtonType.decelCruise, ButtonType.setCruise)
@@ -49,15 +48,15 @@ class ModularAssistiveDrivingSystem:
self.enabled = False
self.active = False
self.available = False
self.mismatch_counter = 0
self.allow_always = False
self.selfdrive = selfdrive
self.selfdrive.enabled_prev = False
self.state_machine = StateMachine(self)
self.events = self.selfdrive.events
self.events_sp = self.selfdrive.events_sp
if self.selfdrive.CP.carName == "hyundai":
if (self.selfdrive.CP_SP.flags & HyundaiFlagsSP.HAS_LFA_BUTTON) or \
if (self.selfdrive.CP.sunnypilotFlags & HyundaiFlagsSP.HAS_LFA_BUTTON) or \
(self.selfdrive.CP.flags & HyundaiFlags.CANFD):
self.allow_always = True
@@ -71,7 +70,20 @@ class ModularAssistiveDrivingSystem:
self.main_enabled_toggle = self.mads_params.read_param("MadsMainCruiseAllowed")
self.unified_engagement_mode = self.mads_params.read_param("MadsUnifiedEngagementMode")
def update_events(self, CS: car.CarState):
def update_controls_mismatch(self, sm: messaging.SubMaster):
heartbeat_engaged = self.active if self.pause_lateral_on_brake_toggle else self.enabled
if not heartbeat_engaged:
self.mismatch_counter = 0
if heartbeat_engaged and any(not ps.controlsAllowedLat for ps in sm['pandaStates']
if ps.safetyModel not in IGNORED_SAFETY_MODES):
self.mismatch_counter += 1
if self.mismatch_counter >= 200:
self.events.add(EventName.controlsMismatchLateral)
def update_events(self, CS: car.CarState, sm: messaging.SubMaster):
def update_unified_engagement_mode():
uem_blocked = self.enabled or (self.selfdrive.enabled and self.selfdrive.enabled_prev)
if (self.unified_engagement_mode and uem_blocked) or not self.unified_engagement_mode:
@@ -80,30 +92,26 @@ class ModularAssistiveDrivingSystem:
def transition_paused_state():
if self.state_machine.state != State.paused:
self.events_sp.add(EventNameSP.silentLkasDisable)
def replace_event(old_event: int, new_event: int):
self.events.remove(old_event)
self.events_sp.add(new_event)
self.events.add(EventName.silentLkasDisable)
if not self.selfdrive.enabled and self.enabled:
if self.events.has(EventName.doorOpen):
replace_event(EventName.doorOpen, EventNameSP.silentDoorOpen)
self.events.replace(EventName.doorOpen, EventName.silentDoorOpen)
transition_paused_state()
if self.events.has(EventName.seatbeltNotLatched):
replace_event(EventName.seatbeltNotLatched, EventNameSP.silentSeatbeltNotLatched)
self.events.replace(EventName.seatbeltNotLatched, EventName.silentSeatbeltNotLatched)
transition_paused_state()
if self.events.has(EventName.wrongGear):
replace_event(EventName.wrongGear, EventNameSP.silentWrongGear)
self.events.replace(EventName.wrongGear, EventName.silentWrongGear)
transition_paused_state()
if self.events.has(EventName.reverseGear):
replace_event(EventName.reverseGear, EventNameSP.silentReverseGear)
self.events.replace(EventName.reverseGear, EventName.silentReverseGear)
transition_paused_state()
if self.events.has(EventName.brakeHold):
replace_event(EventName.brakeHold, EventNameSP.silentBrakeHold)
self.events.replace(EventName.brakeHold, EventName.silentBrakeHold)
transition_paused_state()
if self.events.has(EventName.parkBrake):
replace_event(EventName.parkBrake, EventNameSP.silentParkBrake)
self.events.replace(EventName.parkBrake, EventName.silentParkBrake)
transition_paused_state()
if self.pause_lateral_on_brake_toggle:
@@ -113,7 +121,7 @@ class ModularAssistiveDrivingSystem:
if not (self.pause_lateral_on_brake_toggle and CS.brakePressed) and \
not self.events.contains_in_list(GEARS_ALLOW_PAUSED_SILENT):
if self.state_machine.state == State.paused:
self.events_sp.add(EventNameSP.silentLkasEnable)
self.events.add(EventName.silentLkasEnable)
self.events.remove(EventName.preEnableStandstill)
self.events.remove(EventName.belowEngageSpeed)
@@ -126,25 +134,25 @@ class ModularAssistiveDrivingSystem:
else:
if self.main_enabled_toggle:
if CS.cruiseState.available and not self.selfdrive.CS_prev.cruiseState.available:
self.events_sp.add(EventNameSP.lkasEnable)
self.events.add(EventName.lkasEnable)
for be in CS.buttonEvents:
if be.type == ButtonType.cancel:
if not self.selfdrive.enabled and self.selfdrive.enabled_prev:
self.events_sp.add(EventNameSP.manualLongitudinalRequired)
self.events.add(EventName.manualLongitudinalRequired)
if be.type == ButtonType.lkas and be.pressed and (CS.cruiseState.available or self.allow_always):
if self.enabled:
if self.selfdrive.enabled:
self.events_sp.add(EventNameSP.manualSteeringRequired)
self.events.add(EventName.manualSteeringRequired)
else:
self.events_sp.add(EventNameSP.lkasDisable)
self.events.add(EventName.lkasDisable)
else:
self.events_sp.add(EventNameSP.lkasEnable)
self.events.add(EventName.lkasEnable)
if not CS.cruiseState.available:
self.events.remove(EventName.buttonEnable)
if self.selfdrive.CS_prev.cruiseState.available:
self.events_sp.add(EventNameSP.lkasDisable)
self.events.add(EventName.lkasDisable)
self.events.remove(EventName.pcmDisable)
self.events.remove(EventName.buttonCancel)
@@ -153,14 +161,16 @@ class ModularAssistiveDrivingSystem:
if not any(be.type in SET_SPEED_BUTTONS for be in CS.buttonEvents):
self.events.remove(EventName.wrongCarMode)
def update(self, CS: car.CarState):
self.update_controls_mismatch(sm)
def update(self, CS: car.CarState, sm: messaging.SubMaster):
if not self.enabled_toggle:
return
self.update_events(CS)
self.update_events(CS, sm)
if not self.selfdrive.CP.passive and self.selfdrive.initialized:
self.enabled, self.active = self.state_machine.update(self.events, self.events_sp)
self.enabled, self.active = self.state_machine.update(self.events)
# Copy of previous SelfdriveD states for MADS events handling
self.selfdrive.enabled_prev = self.selfdrive.enabled
+19 -34
View File
@@ -29,17 +29,14 @@ from openpilot.selfdrive.selfdrived.events import ET, Events
from openpilot.selfdrive.selfdrived.state import SOFT_DISABLE_TIME
from openpilot.common.realtime import DT_CTRL
from openpilot.sunnypilot.selfdrive.selfdrived.events import EventsSP
State = custom.ModularAssistiveDrivingSystem.ModularAssistiveDrivingSystemState
State = custom.SelfdriveStateSP.ModularAssistiveDrivingSystem.ModularAssistiveDrivingSystemState
EventName = log.OnroadEvent.EventName
EventNameSP = custom.OnroadEventSP.EventName
ACTIVE_STATES = (State.enabled, State.softDisabling, State.overriding)
ENABLED_STATES = (State.paused, *ACTIVE_STATES)
GEARS_ALLOW_PAUSED_SILENT = [EventNameSP.silentWrongGear, EventNameSP.silentReverseGear, EventNameSP.silentBrakeHold,
EventNameSP.silentDoorOpen, EventNameSP.silentSeatbeltNotLatched, EventNameSP.silentParkBrake]
GEARS_ALLOW_PAUSED_SILENT = [EventName.silentWrongGear, EventName.silentReverseGear, EventName.silentBrakeHold,
EventName.silentDoorOpen, EventName.silentSeatbeltNotLatched, EventName.silentParkBrake]
GEARS_ALLOW_PAUSED = [EventName.wrongGear, EventName.reverseGear, EventName.brakeHold,
EventName.doorOpen, EventName.seatbeltNotLatched, EventName.parkBrake,
*GEARS_ALLOW_PAUSED_SILENT]
@@ -52,57 +49,45 @@ class StateMachine:
self.state = State.disabled
self._events = Events()
self._events_sp = EventsSP()
def add_current_alert_types(self, alert_type):
if not self.selfdrive.enabled:
self.ss_state_machine.current_alert_types.append(alert_type)
def check_contains(self, event_type: str) -> bool:
return bool(self._events.contains(event_type) or self._events_sp.contains(event_type))
def check_contains_in_list(self, events_list: list[int]) -> bool:
return bool(self._events.contains_in_list(events_list) or self._events_sp.contains_in_list(events_list))
def update(self, events: Events, events_sp: EventsSP):
def update(self, events: Events):
# soft disable timer and current alert types are from the state machine of openpilot
# decrement the soft disable timer at every step, as it's reset on
# entrance in SOFT_DISABLING state
self._events = events
self._events_sp = events_sp
# ENABLED, SOFT DISABLING, PAUSED, OVERRIDING
if self.state != State.disabled:
# user and immediate disable always have priority in a non-disabled state
if self.check_contains(ET.USER_DISABLE):
if events_sp.has(EventNameSP.silentLkasDisable) or events_sp.has(EventNameSP.silentBrakeHold):
if events.contains(ET.USER_DISABLE):
if events.has(EventName.silentLkasDisable) or events.has(EventName.silentBrakeHold):
self.state = State.paused
else:
self.state = State.disabled
self.ss_state_machine.current_alert_types.append(ET.USER_DISABLE)
elif self.check_contains(ET.IMMEDIATE_DISABLE):
elif events.contains(ET.IMMEDIATE_DISABLE):
self.state = State.disabled
self.add_current_alert_types(ET.IMMEDIATE_DISABLE)
else:
# ENABLED
if self.state == State.enabled:
if self.check_contains(ET.SOFT_DISABLE):
if events.contains(ET.SOFT_DISABLE):
self.state = State.softDisabling
if not self.selfdrive.enabled:
self.ss_state_machine.soft_disable_timer = int(SOFT_DISABLE_TIME / DT_CTRL)
self.ss_state_machine.current_alert_types.append(ET.SOFT_DISABLE)
elif self.check_contains(ET.OVERRIDE_LATERAL):
elif events.contains(ET.OVERRIDE_LATERAL):
self.state = State.overriding
self.add_current_alert_types(ET.OVERRIDE_LATERAL)
# SOFT DISABLING
elif self.state == State.softDisabling:
if not self.check_contains(ET.SOFT_DISABLE):
if not events.contains(ET.SOFT_DISABLE):
# no more soft disabling condition, so go back to ENABLED
self.state = State.enabled
@@ -114,12 +99,12 @@ class StateMachine:
# PAUSED
elif self.state == State.paused:
if self.check_contains(ET.ENABLE):
if self.check_contains(ET.NO_ENTRY):
if events.contains(ET.ENABLE):
if events.contains(ET.NO_ENTRY):
self.add_current_alert_types(ET.NO_ENTRY)
else:
if self.check_contains(ET.OVERRIDE_LATERAL):
if events.contains(ET.OVERRIDE_LATERAL):
self.state = State.overriding
else:
self.state = State.enabled
@@ -127,26 +112,26 @@ class StateMachine:
# OVERRIDING
elif self.state == State.overriding:
if self.check_contains(ET.SOFT_DISABLE):
if events.contains(ET.SOFT_DISABLE):
self.state = State.softDisabling
if not self.selfdrive.enabled:
self.ss_state_machine.soft_disable_timer = int(SOFT_DISABLE_TIME / DT_CTRL)
self.ss_state_machine.current_alert_types.append(ET.SOFT_DISABLE)
elif not self.check_contains(ET.OVERRIDE_LATERAL):
elif not events.contains(ET.OVERRIDE_LATERAL):
self.state = State.enabled
else:
self.ss_state_machine.current_alert_types += [ET.OVERRIDE_LATERAL]
# DISABLED
elif self.state == State.disabled:
if self.check_contains(ET.ENABLE):
if self.check_contains(ET.NO_ENTRY):
if self.check_contains_in_list(GEARS_ALLOW_PAUSED):
if events.contains(ET.ENABLE):
if events.contains(ET.NO_ENTRY):
if events.contains_in_list(GEARS_ALLOW_PAUSED):
self.state = State.paused
self.add_current_alert_types(ET.NO_ENTRY)
else:
if self.check_contains(ET.OVERRIDE_LATERAL):
if events.contains(ET.OVERRIDE_LATERAL):
self.state = State.overriding
else:
self.state = State.enabled
@@ -27,14 +27,13 @@ Last updated: July 29, 2024
import pytest
from pytest_mock import MockerFixture
from cereal import custom
from cereal import log, custom
from openpilot.common.realtime import DT_CTRL
from openpilot.sunnypilot.mads.state import StateMachine, SOFT_DISABLE_TIME, GEARS_ALLOW_PAUSED
from openpilot.selfdrive.selfdrived.events import ET, NormalPermanentAlert
from openpilot.sunnypilot.selfdrive.selfdrived.events import EVENTS_SP
from openpilot.selfdrive.selfdrived.events import Events, ET, EVENTS, NormalPermanentAlert
State = custom.ModularAssistiveDrivingSystem.ModularAssistiveDrivingSystemState
EventNameSP = custom.OnroadEventSP.EventName
State = custom.SelfdriveStateSP.ModularAssistiveDrivingSystem.ModularAssistiveDrivingSystemState
EventName = log.OnroadEvent.EventName
# The event types that maintain the current state
MAINTAIN_STATES = {State.enabled: (None,), State.disabled: (None,), State.softDisabling: (ET.SOFT_DISABLE,),
@@ -48,7 +47,7 @@ def make_event(event_types):
event = {}
for ev in event_types:
event[ev] = NormalPermanentAlert("alert")
EVENTS_SP[0] = event
EVENTS[0] = event
return 0
@@ -63,93 +62,87 @@ class TestMADSStateMachine:
@pytest.fixture(autouse=True)
def setup_method(self, mocker: MockerFixture):
self.mads = MockMADS(mocker)
self.events = Events()
self.state_machine = StateMachine(self.mads)
self.events = self.state_machine._events
self.events_sp = self.state_machine._events_sp
self.mads.selfdrive.state_machine.soft_disable_timer = int(SOFT_DISABLE_TIME / DT_CTRL)
def reset(self):
self.events.clear()
self.events_sp.clear()
self.state_machine.state = State.disabled
def test_immediate_disable(self):
for state in ALL_STATES:
for et in MAINTAIN_STATES[state]:
self.events_sp.add(make_event([et, ET.IMMEDIATE_DISABLE]))
self.events.add(make_event([et, ET.IMMEDIATE_DISABLE]))
self.state_machine.state = state
self.state_machine.update(self.events, self.events_sp)
self.state_machine.update(self.events)
assert State.disabled == self.state_machine.state
self.reset()
self.events.clear()
def test_user_disable(self):
for state in ALL_STATES:
for et in MAINTAIN_STATES[state]:
self.events_sp.add(make_event([et, ET.USER_DISABLE]))
self.events.add(make_event([et, ET.USER_DISABLE]))
self.state_machine.state = state
self.state_machine.update(self.events, self.events_sp)
self.state_machine.update(self.events)
assert State.disabled == self.state_machine.state
self.reset()
self.events.clear()
def test_user_disable_to_paused(self):
paused_events = (EventNameSP.silentLkasDisable, EventNameSP.silentBrakeHold)
paused_events = (EventName.silentLkasDisable, EventName.silentBrakeHold)
for state in ALL_STATES:
for et in MAINTAIN_STATES[state]:
self.events_sp.add(make_event([et, ET.USER_DISABLE]))
self.events.add(make_event([et, ET.USER_DISABLE]))
for en in paused_events:
self.events_sp.add(en)
self.events.add(en)
self.state_machine.state = state
self.state_machine.update(self.events, self.events_sp)
final_state = State.paused if self.events_sp.has(en) and state != State.disabled else State.disabled
self.state_machine.update(self.events)
final_state = State.paused if self.events.has(en) and state != State.disabled else State.disabled
assert self.state_machine.state == final_state
self.reset()
self.events.clear()
def test_soft_disable(self):
for state in ALL_STATES:
if state == State.paused: # paused considers USER_DISABLE instead
continue
for et in MAINTAIN_STATES[state]:
self.events_sp.add(make_event([et, ET.SOFT_DISABLE]))
self.events.add(make_event([et, ET.SOFT_DISABLE]))
self.state_machine.state = state
self.state_machine.update(self.events, self.events_sp)
self.state_machine.update(self.events)
assert self.state_machine.state == State.disabled if state == State.disabled else State.softDisabling
self.reset()
self.events.clear()
def test_soft_disable_timer(self):
self.state_machine.state = State.enabled
self.events_sp.add(make_event([ET.SOFT_DISABLE]))
self.state_machine.update(self.events, self.events_sp)
self.events.add(make_event([ET.SOFT_DISABLE]))
self.state_machine.update(self.events)
for _ in range(int(SOFT_DISABLE_TIME / DT_CTRL)):
assert self.state_machine.state == State.softDisabling
self.mads.selfdrive.state_machine.soft_disable_timer -= 1
self.state_machine.update(self.events, self.events_sp)
self.state_machine.update(self.events)
assert self.state_machine.state == State.disabled
def test_no_entry(self):
for et in ENABLE_EVENT_TYPES:
self.events_sp.add(make_event([ET.NO_ENTRY, et]))
if not self.state_machine.check_contains_in_list(GEARS_ALLOW_PAUSED):
self.state_machine.update(self.events, self.events_sp)
self.events.add(make_event([ET.NO_ENTRY, et]))
if not self.events.contains_in_list(GEARS_ALLOW_PAUSED):
self.state_machine.update(self.events)
assert self.state_machine.state == State.disabled
self.reset()
self.events.clear()
def test_no_entry_paused(self):
self.state_machine.state = State.paused
self.events_sp.add(make_event([ET.NO_ENTRY]))
self.state_machine.update(self.events, self.events_sp)
self.events.add(make_event([ET.NO_ENTRY]))
self.state_machine.update(self.events)
assert self.state_machine.state == State.paused
def test_override_lateral(self):
self.state_machine.state = State.enabled
self.events_sp.add(make_event([ET.OVERRIDE_LATERAL]))
self.state_machine.update(self.events, self.events_sp)
self.events.add(make_event([ET.OVERRIDE_LATERAL]))
self.state_machine.update(self.events)
assert self.state_machine.state == State.overriding
def test_paused_to_enabled(self):
self.state_machine.state = State.paused
self.events_sp.add(make_event([ET.ENABLE]))
self.state_machine.update(self.events, self.events_sp)
self.events.add(make_event([ET.ENABLE]))
self.state_machine.update(self.events)
assert self.state_machine.state == State.enabled
def test_maintain_states(self):
@@ -157,7 +150,7 @@ class TestMADSStateMachine:
for et in MAINTAIN_STATES[state]:
self.state_machine.state = state
if et is not None:
self.events_sp.add(make_event([et]))
self.state_machine.update(self.events, self.events_sp)
self.events.add(make_event([et]))
self.state_machine.update(self.events)
assert self.state_machine.state == state
self.reset()
self.events.clear()
View File
-17
View File
@@ -1,17 +0,0 @@
from openpilot.selfdrive.modeld.constants import Meta
class Meta20hz(Meta):
ENGAGED = slice(0, 1)
# next 2, 4, 6, 8, 10 seconds
GAS_DISENGAGE = slice(1, 31, 6)
BRAKE_DISENGAGE = slice(2, 31, 6)
STEER_OVERRIDE = slice(3, 31, 6)
HARD_BRAKE_3 = slice(4, 31, 6)
HARD_BRAKE_4 = slice(5, 31, 6)
HARD_BRAKE_5 = slice(6, 31, 6)
# next 0, 2, 4, 6, 8, 10 seconds
GAS_PRESS = slice(31, 55, 4)
BRAKE_PRESS = slice(32, 55, 4)
LEFT_BLINKER = slice(33, 55, 4)
RIGHT_BLINKER = slice(34, 55, 4)
-26
View File
@@ -1,26 +0,0 @@
from openpilot.selfdrive.modeld.constants import Meta
from cereal import custom
from openpilot.sunnypilot.modeld_v2.meta_20hz import Meta20hz
from openpilot.sunnypilot.models.helpers import get_active_bundle
ModelBundle = custom.ModelManagerSP.ModelBundle
def load_meta_constants():
"""
Determines and loads the appropriate meta model class based on the metadata provided. The function checks
specific keys and conditions within the provided metadata dictionary to identify the corresponding meta
model class to return.
:param model_metadata: Dictionary containing metadata about the model. It includes
details such as input shapes, output slices, and other configurations for identifying
metadata-dependent meta model classes.
:type model_metadata: dict
:return: The appropriate meta model class (Meta, MetaSimPose, or MetaTombRaider)
based on the conditions and metadata provided.
:rtype: type
"""
if (bundle := get_active_bundle()) and bundle.is20hz:
return Meta20hz
return Meta # Default
-134
View File
@@ -1,134 +0,0 @@
import os
import pickle
from abc import ABC, abstractmethod
import numpy as np
from cereal import custom
from openpilot.selfdrive.modeld import MODEL_PATH, MODEL_PKL_PATH, METADATA_PATH
from openpilot.selfdrive.modeld.models.commonmodel_pyx import DrivingModelFrame, CLMem
from openpilot.selfdrive.modeld.runners.ort_helpers import make_onnx_cpu_runner, ORT_TYPES_TO_NP_TYPES
from openpilot.selfdrive.modeld.runners.tinygrad_helpers import qcom_tensor_from_opencl_address
from openpilot.system.hardware import TICI
from openpilot.system.hardware.hw import Paths
from openpilot.sunnypilot.models.helpers import get_active_bundle
from tinygrad.tensor import Tensor
if TICI:
os.environ['QCOM'] = '1'
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
CUSTOM_MODEL_PATH = Paths.model_root()
ModelManager = custom.ModelManagerSP
class ModelRunner(ABC):
"""Abstract base class for model runners that defines the interface for running ML models."""
def __init__(self):
"""Initialize the model runner with paths to model and metadata files."""
metadata_path = METADATA_PATH
self.is_20hz = None
self._drive_model = None
self._metadata_model = None
if bundle := get_active_bundle():
bundle_models = {model.type.raw: model for model in bundle.models}
self._drive_model = bundle_models.get(ModelManager.Type.drive)
self._metadata_model = bundle_models.get(ModelManager.Type.metadata)
self.is_20hz = bundle.is20hz
# Override the metadata path if a metadata model is found in the active bundle
if self._metadata_model:
metadata_path = f"{CUSTOM_MODEL_PATH}/{self._metadata_model.fileName}"
with open(metadata_path, 'rb') as f:
self.model_metadata = pickle.load(f)
self.input_shapes = self.model_metadata['input_shapes']
self.output_slices = self.model_metadata['output_slices']
self.inputs: dict = {}
@abstractmethod
def prepare_inputs(self, imgs_cl: dict[str, CLMem], numpy_inputs: dict[str, np.ndarray], frames: dict[str, DrivingModelFrame]) -> dict:
"""Prepare inputs for model inference."""
raise NotImplementedError
@abstractmethod
def run_model(self):
"""Run model inference with prepared inputs."""
def slice_outputs(self, model_outputs: np.ndarray) -> dict:
"""Slice model outputs according to metadata configuration."""
parsed_outputs = {k: model_outputs[np.newaxis, v] for k, v in self.output_slices.items()}
if SEND_RAW_PRED:
parsed_outputs['raw_pred'] = model_outputs.copy()
return parsed_outputs
class TinygradRunner(ModelRunner):
"""Tinygrad implementation of model runner for TICI hardware."""
def __init__(self):
super().__init__()
model_pkl_path = MODEL_PKL_PATH
if self._drive_model:
model_pkl_path = f"{CUSTOM_MODEL_PATH}/{self._drive_model.fileName}"
assert model_pkl_path.endswith('_tinygrad.pkl'), f"Invalid model file: {model_pkl_path} for TinygradRunner"
# Load Tinygrad model
with open(model_pkl_path, "rb") as f:
try:
self.model_run = pickle.load(f)
except FileNotFoundError as e:
assert "/dev/kgsl-3d0" not in str(e), "Model was built on C3 or C3X, but is being loaded on PC"
raise
self.input_to_dtype = {}
self.input_to_device = {}
for idx, name in enumerate(self.model_run.captured.expected_names):
self.input_to_dtype[name] = self.model_run.captured.expected_st_vars_dtype_device[idx][2] # 2 is the dtype
self.input_to_device[name] = self.model_run.captured.expected_st_vars_dtype_device[idx][3] # 3 is the device
def prepare_inputs(self, imgs_cl: dict[str, CLMem], numpy_inputs: dict[str, np.ndarray], frames: dict[str, DrivingModelFrame]) -> dict:
# Initialize image tensors if not already done
for key in imgs_cl:
if TICI and key not in self.inputs:
self.inputs[key] = qcom_tensor_from_opencl_address(imgs_cl[key].mem_address, self.input_shapes[key], dtype=self.input_to_dtype[key])
elif not TICI:
shape = frames[key].buffer_from_cl(imgs_cl[key]).reshape(self.input_shapes[key])
self.inputs[key] = Tensor(shape, device=self.input_to_device[key], dtype=self.input_to_dtype[key]).realize()
# Update numpy inputs
for key, value in numpy_inputs.items():
if key not in imgs_cl:
self.inputs[key] = Tensor(value, device=self.input_to_device[key], dtype=self.input_to_dtype[key]).realize()
return self.inputs
def run_model(self):
return self.model_run(**self.inputs).numpy().flatten()
class ONNXRunner(ModelRunner):
"""ONNX implementation of model runner for non-TICI hardware."""
def __init__(self):
super().__init__()
self.runner = make_onnx_cpu_runner(MODEL_PATH)
self.input_to_nptype = {
model_input.name: ORT_TYPES_TO_NP_TYPES[model_input.type]
for model_input in self.runner.get_inputs()
}
def prepare_inputs(self, imgs_cl: dict[str, CLMem], numpy_inputs: dict[str, np.ndarray], frames: dict[str, DrivingModelFrame]) -> dict:
self.inputs = numpy_inputs
for key in imgs_cl:
self.inputs[key] = frames[key].buffer_from_cl(imgs_cl[key]).reshape(self.input_shapes[key]).astype(dtype=self.input_to_nptype[key])
return self.inputs
def run_model(self):
return self.runner.run(None, self.inputs)[0].flatten()
+2 -1
View File
@@ -72,12 +72,13 @@ class ModelParser:
model_bundle.generation = int(value["generation"])
model_bundle.environment = value["environment"]
model_bundle.runner = value.get("runner", custom.ModelManagerSP.Runner.snpe)
model_bundle.is20hz = value.get("is_20hz", False)
return model_bundle
@staticmethod
def parse_models(json_data: dict) -> list[custom.ModelManagerSP.ModelBundle]:
# TODO-SP: Remove the following filter once we add support for tinygrad model switcher
json_data = {k: v for k, v in json_data.items() if v.get("runner", -1) == custom.ModelManagerSP.Runner.snpe}
return [ModelParser._parse_bundle(key, value) for key, value in json_data.items()]
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3d7726f7c6a8106c767ae974ce7395a399dbf0a934779a7710b748917c29cf6e
size 11365
@@ -0,0 +1,32 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
from cereal import car, log
ButtonType = car.CarState.ButtonEvent.Type
EventName = log.OnroadEvent.EventName
class ButtonHoldTracker:
def __init__(self):
self.button_frame_counts = {}
def update(self, CS) -> None:
if CS.cruiseState.available:
self.update_button_timers(CS)
def update_button_timers(self, CS) -> None:
for button_type, held_frames in self.button_frame_counts.items():
if held_frames > 0:
self.button_frame_counts[button_type] += 1
for event in CS.buttonEvents:
event_type = event.type.raw
if not (event.pressed and self.button_frame_counts.get(event_type, 0) > 0): # No need to reset if the button is "pulsing" always "pressed"
self.button_frame_counts[event_type] = int(event.pressed)
def get_hold_duration(self, button_type):
return self.button_frame_counts.get(button_type, 0)
+5 -5
View File
@@ -5,12 +5,12 @@ This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
from cereal import custom
from cereal import log
from opendbc.car import structs
from openpilot.sunnypilot.selfdrive.selfdrived.events import EventsSP
from openpilot.selfdrive.selfdrived.events import Events
EventNameSP = custom.OnroadEventSP.EventName
EventName = log.OnroadEvent.EventName
class CarSpecificEventsSP:
@@ -26,9 +26,9 @@ class CarSpecificEventsSP:
self.hyundai_radar_tracks_confirmed = self.params.get_bool("HyundaiRadarTracksConfirmed")
def update(self):
events = EventsSP()
events = Events()
if self.CP.carName == 'hyundai':
if self.hyundai_radar_tracks and not self.hyundai_radar_tracks_confirmed:
events.add(EventNameSP.hyundaiRadarTracksConfirmed)
events.add(EventName.hyundaiRadarTracksConfirmed)
return events
@@ -1,50 +0,0 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
from cereal import car, custom
from opendbc.car import structs
from openpilot.common.params import Params
ButtonType = car.CarState.ButtonEvent.Type
EventNameSP = custom.OnroadEventSP.EventName
DISTANCE_LONG_PRESS = 50
class CruiseHelper:
def __init__(self, CP: structs.CarParams):
self.CP = CP
self.params = Params()
self.button_frame_counts = {ButtonType.gapAdjustCruise: 0}
self._experimental_mode = False
self.experimental_mode_switched = False
def update(self, CS, events, experimental_mode) -> None:
if self.CP.openpilotLongitudinalControl:
if CS.cruiseState.available:
self.update_button_frame_counts(CS)
# toggle experimental mode once on distance button hold
self.update_experimental_mode(events, experimental_mode)
def update_button_frame_counts(self, CS) -> None:
for button in self.button_frame_counts:
if self.button_frame_counts[button] > 0:
self.button_frame_counts[button] += 1
for button_event in CS.buttonEvents:
button = button_event.type.raw
if button in self.button_frame_counts:
self.button_frame_counts[button] = int(button_event.pressed)
def update_experimental_mode(self, events, experimental_mode) -> None:
if self.button_frame_counts[ButtonType.gapAdjustCruise] >= DISTANCE_LONG_PRESS and not self.experimental_mode_switched:
self._experimental_mode = not experimental_mode
self.params.put_bool_nonblocking("ExperimentalMode", self._experimental_mode)
events.add(EventNameSP.experimentalModeSwitched)
self.experimental_mode_switched = True
@@ -0,0 +1,30 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
from openpilot.sunnypilot.selfdrive.car.button_hold_tracker import ButtonHoldTracker, ButtonType, EventName
DISTANCE_LONG_PRESS = 50
class ExperimentalSwitcher:
def __init__(self, params, button_type=ButtonType.gapAdjustCruise):
self.params = params
self.button_type = button_type
self._button_tracker = ButtonHoldTracker()
self.experimental_mode_switched = False # Meant to be toggled off by this class's children.
def update(self, CS, events, experimental_mode):
self._button_tracker.update(CS)
if CS.cruiseState.available:
self.update_mode(events, experimental_mode)
def update_mode(self, events, experimental_mode) -> None:
if self._button_tracker.get_hold_duration(self.button_type) >= DISTANCE_LONG_PRESS and not self.experimental_mode_switched:
experimental_mode = not experimental_mode
self.params.put_bool_nonblocking("ExperimentalMode", experimental_mode)
events.add(EventName.experimentalModeSwitched)
self.experimental_mode_switched = True
+4 -5
View File
@@ -22,21 +22,20 @@ def log_fingerprint(CP: structs.CarParams) -> None:
sentry.capture_fingerprint(CP.carFingerprint, CP.carName)
def setup_car_interface_sp(CP: structs.CarParams, CP_SP: structs.CarParamsSP, params):
def setup_car_interface_sp(CP: structs.CarParams, params):
if CP.carName == 'hyundai':
if CP.flags & HyundaiFlags.MANDO_RADAR and CP.radarUnavailable:
# Having this automatic without a toggle causes a weird process replay diff because
# somehow it sees fewer logs than intended
if params.get_bool("HyundaiRadarTracksToggle"):
CP_SP.flags |= HyundaiFlagsSP.ENABLE_RADAR_TRACKS.value
CP.sunnypilotFlags |= HyundaiFlagsSP.ENABLE_RADAR_TRACKS.value
if params.get_bool("HyundaiRadarTracks"):
CP.radarUnavailable = False
def initialize_car_interface_sp(CP: structs.CarParams, CP_SP: structs.CarParamsSP, params, can_recv: CanRecvCallable,
can_send: CanSendCallable):
def initialize_car_interface_sp(CP: structs.CarParams, params, can_recv: CanRecvCallable, can_send: CanSendCallable):
if CP.carName == 'hyundai':
if CP_SP.flags & HyundaiFlagsSP.ENABLE_RADAR_TRACKS:
if CP.sunnypilotFlags & HyundaiFlagsSP.ENABLE_RADAR_TRACKS:
can_recv()
_, fingerprint = can_fingerprint(can_recv)
radar_unavailable = RADAR_START_ADDR not in fingerprint[1] or Bus.radar not in HYUNDAI_DBC[CP.carFingerprint]
@@ -0,0 +1,81 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import pytest
from cereal import car
from sunnypilot.selfdrive.car.button_hold_tracker import ButtonHoldTracker, ButtonType
@pytest.fixture
def setup_button_tracker():
return ButtonHoldTracker()
class TestButtonHoldTracker:
@pytest.mark.parametrize(
"button_type, button_events, initial_timer, expected_timer",
[
# Basic button press scenarios
(ButtonType.gapAdjustCruise, [car.CarState.ButtonEvent(type=ButtonType.gapAdjustCruise, pressed=True)], 0, 1),
(ButtonType.gapAdjustCruise, [car.CarState.ButtonEvent(type=ButtonType.gapAdjustCruise, pressed=False)], 1, 0),
# Auto-increment when already held
(ButtonType.gapAdjustCruise, [], 5, 6),
(ButtonType.cancel, [], 10, 11),
(ButtonType.leftBlinker, [], 1, 2),
# Multiple button events in same frame
(ButtonType.gapAdjustCruise,
[
car.CarState.ButtonEvent(type=ButtonType.cancel, pressed=True),
car.CarState.ButtonEvent(type=ButtonType.gapAdjustCruise, pressed=False)
], 0, 0),
# Events for different buttons shouldn't affect each other
(ButtonType.gapAdjustCruise, [car.CarState.ButtonEvent(type=ButtonType.cancel, pressed=True)], 5, 6),
# Press while already held (like if it was pulsing the event for some reason)
(ButtonType.cancel, [car.CarState.ButtonEvent(type=ButtonType.cancel, pressed=True)], 5, 6),
# Edge cases
(ButtonType.leftBlinker, [], 0, 0),
(ButtonType.cancel, [car.CarState.ButtonEvent(type=ButtonType.cancel, pressed=False)], 0, 0),
],
ids=[
"initial_button_press",
"button_release",
"increment_held_gap_adjust",
"increment_held_cancel",
"increment_held_blinker",
"multiple_events_same_frame",
"different_button_no_effect",
"press_while_held",
"zero_state_no_events",
"release_when_not_held"
]
)
def test_update_button_timers(self, mocker, setup_button_tracker, button_type, button_events, initial_timer, expected_timer):
tracker = setup_button_tracker
mock_CS = mocker.Mock(buttonEvents=button_events, cruiseState=mocker.Mock(available=True))
if initial_timer > 0:
tracker.button_frame_counts[button_type] = initial_timer
tracker.update(mock_CS)
timer_value = tracker.get_hold_duration(button_type)
assert timer_value == expected_timer, f"Expected timer for {button_type} to be {expected_timer}, but got {timer_value}"
def test_cruise_state_unavailable(self, mocker, setup_button_tracker):
tracker = setup_button_tracker
mock_CS = mocker.Mock(
buttonEvents=[car.CarState.ButtonEvent(type=ButtonType.gapAdjustCruise, pressed=True)],
cruiseState=mocker.Mock(available=False)
)
tracker.update(mock_CS)
assert tracker.get_hold_duration(ButtonType.gapAdjustCruise) == 0
@@ -1,66 +0,0 @@
from parameterized import parameterized_class
from cereal import car
from openpilot.selfdrive.selfdrived.events import Events
from openpilot.sunnypilot.selfdrive.car.cruise_helpers import CruiseHelper, DISTANCE_LONG_PRESS
ButtonEvent = car.CarState.ButtonEvent
ButtonType = car.CarState.ButtonEvent.Type
@parameterized_class(('openpilot_longitudinal',), [(True,)])
class TestCruiseHelper:
def setup_method(self):
self.CP = car.CarParams(openpilotLongitudinalControl=self.openpilot_longitudinal)
self.cruise_helper = CruiseHelper(self.CP)
self.cruise_helper.experimental_mode_switched = False
self.events = Events()
def reset(self):
for _ in range(2):
CS = car.CarState(cruiseState={"available": False})
CS.buttonEvents = [ButtonEvent(type=ButtonType.gapAdjustCruise, pressed=False)]
self.cruise_helper._experimental_mode = False
self.cruise_helper.experimental_mode_switched = False
self.cruise_helper.update(CS, self.events, False)
def test_gap_adjust_cruise_long_press_toggle_mode(self) -> None:
for pressed in (True, False):
for experimental_mode in (True, False):
self.reset()
self.cruise_helper._experimental_mode = experimental_mode
toggled_mode = not experimental_mode if pressed else experimental_mode
for i in range(DISTANCE_LONG_PRESS):
CS = car.CarState(cruiseState={"available": True})
CS.buttonEvents = [ButtonEvent(type=ButtonType.gapAdjustCruise, pressed=pressed)] if i == 0 else []
self.cruise_helper.update(CS, self.events, experimental_mode)
# mode should be toggled
assert self.cruise_helper._experimental_mode == toggled_mode
assert self.cruise_helper.experimental_mode_switched is pressed
# keep holding button after switching mode
for _ in range(DISTANCE_LONG_PRESS):
CS = car.CarState(cruiseState={"available": True})
CS.buttonEvents = [ButtonEvent(type=ButtonType.gapAdjustCruise, pressed=pressed)]
self.cruise_helper.update(CS, self.events, toggled_mode)
# mode should not be toggled
assert self.cruise_helper._experimental_mode == toggled_mode
assert self.cruise_helper.experimental_mode_switched is pressed
def test_gap_adjust_cruise_short_press_toggle_mode(self) -> None:
for pressed in (True, False):
for experimental_mode in (True, False):
self.reset()
self.cruise_helper._experimental_mode = experimental_mode
for i in range(DISTANCE_LONG_PRESS - 1):
CS = car.CarState(cruiseState={"available": True})
CS.buttonEvents = [ButtonEvent(type=ButtonType.gapAdjustCruise, pressed=pressed)] if i == 0 else []
self.cruise_helper.update(CS, self.events, experimental_mode)
# mode should not be toggled
assert self.cruise_helper._experimental_mode == experimental_mode
assert self.cruise_helper.experimental_mode_switched is False
@@ -0,0 +1,55 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import pytest
from cereal import car
from sunnypilot.selfdrive.car.experimental_switcher import ExperimentalSwitcher, ButtonType, EventName, DISTANCE_LONG_PRESS
@pytest.fixture
def setup_switcher(mocker):
mock_params = mocker.Mock()
return ExperimentalSwitcher(mock_params), mock_params
class TestExperimentalSwitcher:
@pytest.mark.parametrize(
"use_experimental_mode, long_press, expected_mode, expected_experimental_mode",
[
(False, True, True, True), # Normal -> Experimental via long press
(True, True, False, True), # Experimental -> Normal via long press
(False, False, False, False) # No change without long press
],
ids=[
"enable_experimental_mode",
"disable_experimental_mode",
"no_mode_change_without_long_press"
]
)
def test_update_mode_switches(self, mocker, setup_switcher, use_experimental_mode, long_press, expected_mode, expected_experimental_mode):
switcher, params = setup_switcher
mock_events = mocker.Mock()
mock_CS = mocker.Mock(
buttonEvents=[car.CarState.ButtonEvent(type=ButtonType.gapAdjustCruise, pressed=True)],
cruiseState=mocker.Mock(available=True)
)
if long_press:
for _ in range(DISTANCE_LONG_PRESS + 1):
switcher.update(mock_CS, mock_events, use_experimental_mode)
switcher.update_mode(mock_events, use_experimental_mode)
if expected_experimental_mode:
params.put_bool_nonblocking.assert_called_once_with("ExperimentalMode", expected_mode)
mock_events.add.assert_called_once_with(EventName.experimentalModeSwitched)
assert switcher.experimental_mode_switched is True
else:
params.put_bool_nonblocking.assert_not_called()
mock_events.add.assert_not_called()
assert switcher.experimental_mode_switched is False
@@ -1,106 +0,0 @@
# The MIT License
#
# Copyright (c) 2019-, Rick Lan, dragonpilot community, and a number of other of contributors.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
# Last updated: August 12, 2024
from cereal import custom
from openpilot.common.numpy_fast import interp
from openpilot.common.realtime import DT_MDL
from openpilot.common.params import Params
#from openpilot.selfdrive.controls.lib.longitudinal_planner import limit_accel_in_turns
from opendbc.car.interfaces import ACCEL_MIN
AccelPersonality = custom.LongitudinalPlanSP.AccelerationPersonality
# Acceleration Profiles
_DP_CRUISE_MIN_V = {
AccelPersonality.eco: [-0.015, -0.015, -1.2, -1.2, -1.2, -1.2, -1.2, -1.2],
AccelPersonality.normal: [-0.018, -0.018, -1.2, -1.2, -1.2, -1.2, -1.2, -1.2],
AccelPersonality.sport: [-0.020, -0.020, -1.2, -1.2, -1.2, -1.2, -1.2, -1.2],
}
_DP_CRUISE_MAX_V = {
AccelPersonality.eco: [1.60, 1.60, 1.58, 1.58, 0.86, .532, .432, .32, .28, .085],
AccelPersonality.normal: [1.80, 1.80, 1.75, 1.75, 0.92, .65, .56, .36, .30, .12],
AccelPersonality.sport: [2.00, 2.00, 2.00, 2.00, 1.05, .79, .61, .42, .333, .2],
}
_DP_CRUISE_MIN_BP = [0., 2.0, 2.01, 15., 15.01, 20., 20.01, 40.]
_DP_CRUISE_MAX_BP = [0., 1., 6., 8., 11., 16, 20., 25., 30., 55.]
class AccelController:
def __init__(self):
self._params = Params()
self._personality = AccelPersonality.stock
self._frame = 0
def _read_params(self):
""" Reads the acceleration personality setting from persistent storage. """
if self._frame % int(1. / DT_MDL) == 0:
personality_str = self._params.get("AccelPersonality", encoding='utf-8')
if personality_str is not None:
personality_int = int(personality_str)
if personality_int in _DP_CRUISE_MIN_V:
self._personality = personality_int
# Print the current mode being used
print(f"Current Acceleration Personality Mode: {self._personality}")
def compute_accel_limits(self, v_ego: float, sm, CP) -> tuple[list[float], list[float]]:
""" Computes acceleration limits based on personality, turns, and DEC mode. """
self._read_params()
# Print the current mode being used
print(f"Current Acceleration Personality Mode: {self._personality}")
if self._personality == AccelPersonality.eco:
min_v = _DP_CRUISE_MIN_V[AccelPersonality.eco]
max_v = _DP_CRUISE_MAX_V[AccelPersonality.eco]
print("eco")
elif self._personality == AccelPersonality.sport:
min_v = _DP_CRUISE_MIN_V[AccelPersonality.sport]
max_v = _DP_CRUISE_MAX_V[AccelPersonality.sport]
print("sport")
else:
min_v = _DP_CRUISE_MIN_V[AccelPersonality.normal]
max_v = _DP_CRUISE_MAX_V[AccelPersonality.normal]
print("normal")
a_cruise_min = interp(v_ego, _DP_CRUISE_MIN_BP, min_v)
a_cruise_max = interp(v_ego, _DP_CRUISE_MAX_BP, max_v)
# Determine acceleration mode
accel_limits = [ACCEL_MIN, a_cruise_max] if CP.radarUnavailable else [a_cruise_min, a_cruise_max]
# Adjust acceleration for turns
#steer_angle = sm['carState'].steeringAngleDeg - sm['liveParameters'].angleOffsetDeg
# Ensure accel_limits_turns is a valid list/tuple here
accel_limits_turns = accel_limits # This is temporary if limit_accel_in_turns is not defined
return accel_limits, accel_limits_turns
def is_enabled(self) -> bool:
""" Returns True if AccelController is actively modifying acceleration limits. """
return bool(self._personality != AccelPersonality.stock)
def update(self):
""" Updates the internal frame counter. """
self._frame += 1
@@ -8,7 +8,6 @@ See the LICENSE.md file in the root directory for more details.
from cereal import messaging, custom
from opendbc.car import structs
from openpilot.sunnypilot.selfdrive.controls.lib.dec.dec import DynamicExperimentalController
from openpilot.sunnypilot.selfdrive.controls.lib.accel_personality.accel_controller import AccelController
DecState = custom.LongitudinalPlanSP.DynamicExperimentalControl.DynamicExperimentalControlState
@@ -16,24 +15,19 @@ DecState = custom.LongitudinalPlanSP.DynamicExperimentalControl.DynamicExperimen
class LongitudinalPlannerSP:
def __init__(self, CP: structs.CarParams, mpc):
self.dec = DynamicExperimentalController(CP, mpc)
self.accel_controller = AccelController()
def get_mpc_mode(self) -> str | None:
""" Returns the current MPC mode if DEC is active. """
return self.dec.mode() if self.dec.active() else None
if not self.dec.active():
return None
def compute_accel_limits(self, v_ego: float, sm, CP) -> tuple[list[float], list[float]]:
""" Delegates acceleration limit computation to AccelController. """
return self.accel_controller.compute_accel_limits(v_ego, sm, CP)
return self.dec.mode()
def update(self, sm: messaging.SubMaster) -> None:
""" Updates DEC and AccelController states. """
self.dec.update(sm)
self.accel_controller.update()
def publish_longitudinal_plan_sp(self, sm: messaging.SubMaster, pm: messaging.PubMaster) -> None:
""" Publishes the longitudinal plan state to messaging. """
plan_sp_send = messaging.new_message('longitudinalPlanSP')
plan_sp_send.valid = sm.all_checks(service_list=['carState', 'controlsState'])
longitudinalPlanSP = plan_sp_send.longitudinalPlanSP
-133
View File
@@ -1,133 +0,0 @@
from cereal import log, car, custom
from openpilot.sunnypilot.selfdrive.selfdrived.events_base import EventsBase, Priority, ET, Alert, \
NoEntryAlert, ImmediateDisableAlert, EngagementAlert, NormalPermanentAlert, AlertCallbackType
AlertSize = log.SelfdriveState.AlertSize
AlertStatus = log.SelfdriveState.AlertStatus
VisualAlert = car.CarControl.HUDControl.VisualAlert
AudibleAlert = car.CarControl.HUDControl.AudibleAlert
EventNameSP = custom.OnroadEventSP.EventName
# get event name from enum
EVENT_NAME_SP = {v: k for k, v in EventNameSP.schema.enumerants.items()}
class EventsSP(EventsBase):
def __init__(self):
super().__init__()
self.event_counters = dict.fromkeys(EVENTS_SP.keys(), 0)
def get_events_mapping(self) -> dict[int, dict[str, Alert | AlertCallbackType]]:
return EVENTS_SP
def get_event_name(self, event: int):
return EVENT_NAME_SP[event]
def get_event_msg_type(self):
return custom.OnroadEventSP
EVENTS_SP: dict[int, dict[str, Alert | AlertCallbackType]] = {
# sunnypilot
EventNameSP.lkasEnable: {
ET.ENABLE: EngagementAlert(AudibleAlert.engage),
},
EventNameSP.lkasDisable: {
ET.USER_DISABLE: EngagementAlert(AudibleAlert.disengage),
},
EventNameSP.manualSteeringRequired: {
ET.USER_DISABLE: Alert(
"Automatic Lane Centering is OFF",
"Manual Steering Required",
AlertStatus.normal, AlertSize.mid,
Priority.LOW, VisualAlert.none, AudibleAlert.disengage, 1.),
},
EventNameSP.manualLongitudinalRequired: {
ET.WARNING: Alert(
"Smart/Adaptive Cruise Control: OFF",
"Manual Speed Control Required",
AlertStatus.normal, AlertSize.mid,
Priority.LOW, VisualAlert.none, AudibleAlert.none, 1.),
},
EventNameSP.silentLkasEnable: {
ET.ENABLE: EngagementAlert(AudibleAlert.none),
},
EventNameSP.silentLkasDisable: {
ET.USER_DISABLE: EngagementAlert(AudibleAlert.none),
},
EventNameSP.silentBrakeHold: {
ET.USER_DISABLE: EngagementAlert(AudibleAlert.none),
ET.NO_ENTRY: NoEntryAlert("Brake Hold Active"),
},
EventNameSP.silentWrongGear: {
ET.WARNING: Alert(
"",
"",
AlertStatus.normal, AlertSize.none,
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, 0.),
ET.NO_ENTRY: Alert(
"Gear not D",
"openpilot Unavailable",
AlertStatus.normal, AlertSize.mid,
Priority.LOW, VisualAlert.none, AudibleAlert.none, 0.),
},
EventNameSP.silentReverseGear: {
ET.PERMANENT: Alert(
"Reverse\nGear",
"",
AlertStatus.normal, AlertSize.full,
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .2, creation_delay=0.5),
ET.NO_ENTRY: NoEntryAlert("Reverse Gear"),
},
EventNameSP.silentDoorOpen: {
ET.WARNING: Alert(
"",
"",
AlertStatus.normal, AlertSize.none,
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, 0.),
ET.NO_ENTRY: NoEntryAlert("Door Open"),
},
EventNameSP.silentSeatbeltNotLatched: {
ET.WARNING: Alert(
"",
"",
AlertStatus.normal, AlertSize.none,
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, 0.),
ET.NO_ENTRY: NoEntryAlert("Seatbelt Unlatched"),
},
EventNameSP.silentParkBrake: {
ET.WARNING: Alert(
"",
"",
AlertStatus.normal, AlertSize.none,
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, 0.),
ET.NO_ENTRY: NoEntryAlert("Parking Brake Engaged"),
},
EventNameSP.controlsMismatchLateral: {
ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Controls Mismatch: Lateral"),
ET.NO_ENTRY: NoEntryAlert("Controls Mismatch: Lateral"),
},
EventNameSP.hyundaiRadarTracksConfirmed: {
ET.PERMANENT: NormalPermanentAlert("Radar tracks available. Restart the car to initialize")
},
EventNameSP.experimentalModeSwitched: {
ET.WARNING: NormalPermanentAlert("Experimental Mode Switched", duration=1.5)
}
}
@@ -1,225 +0,0 @@
import bisect
from enum import IntEnum
from abc import abstractmethod
from collections.abc import Callable
from cereal import log, car
import cereal.messaging as messaging
from openpilot.common.realtime import DT_CTRL
AlertSize = log.SelfdriveState.AlertSize
AlertStatus = log.SelfdriveState.AlertStatus
VisualAlert = car.CarControl.HUDControl.VisualAlert
AudibleAlert = car.CarControl.HUDControl.AudibleAlert
# Alert priorities
class Priority(IntEnum):
LOWEST = 0
LOWER = 1
LOW = 2
MID = 3
HIGH = 4
HIGHEST = 5
# Event types
class ET:
ENABLE = 'enable'
PRE_ENABLE = 'preEnable'
OVERRIDE_LATERAL = 'overrideLateral'
OVERRIDE_LONGITUDINAL = 'overrideLongitudinal'
NO_ENTRY = 'noEntry'
WARNING = 'warning'
USER_DISABLE = 'userDisable'
SOFT_DISABLE = 'softDisable'
IMMEDIATE_DISABLE = 'immediateDisable'
PERMANENT = 'permanent'
class Alert:
def __init__(self,
alert_text_1: str,
alert_text_2: str,
alert_status: log.SelfdriveState.AlertStatus,
alert_size: log.SelfdriveState.AlertSize,
priority: Priority,
visual_alert: car.CarControl.HUDControl.VisualAlert,
audible_alert: car.CarControl.HUDControl.AudibleAlert,
duration: float,
creation_delay: float = 0.):
self.alert_text_1 = alert_text_1
self.alert_text_2 = alert_text_2
self.alert_status = alert_status
self.alert_size = alert_size
self.priority = priority
self.visual_alert = visual_alert
self.audible_alert = audible_alert
self.duration = int(duration / DT_CTRL)
self.creation_delay = creation_delay
self.alert_type = ""
self.event_type: str | None = None
def __str__(self) -> str:
return f"{self.alert_text_1}/{self.alert_text_2} {self.priority} {self.visual_alert} {self.audible_alert}"
def __gt__(self, alert2) -> bool:
if not isinstance(alert2, Alert):
return False
return self.priority > alert2.priority
class AlertBase(Alert):
def __init__(self, alert_text_1: str, alert_text_2: str, alert_status: log.SelfdriveState.AlertStatus,
alert_size: log.SelfdriveState.AlertSize, priority: Priority,
visual_alert: car.CarControl.HUDControl.VisualAlert,
audible_alert: car.CarControl.HUDControl.AudibleAlert, duration: float):
super().__init__(alert_text_1, alert_text_2, alert_status, alert_size, priority, visual_alert, audible_alert, duration)
AlertCallbackType = Callable[[car.CarParams, car.CarState, messaging.SubMaster, bool, int, log.ControlsState], Alert]
class EventsBase:
def __init__(self):
self.events: list[int] = []
self.static_events: list[int] = []
self.event_counters = {}
@property
def names(self) -> list[int]:
return self.events
def __len__(self) -> int:
return len(self.events)
def add(self, event_name: int, static: bool = False) -> None:
if static:
bisect.insort(self.static_events, event_name)
bisect.insort(self.events, event_name)
def clear(self) -> None:
self.event_counters = {k: (v + 1 if k in self.events else 0) for k, v in self.event_counters.items()}
self.events = self.static_events.copy()
def contains(self, event_type: str) -> bool:
return any(event_type in self.get_events_mapping().get(e, {}) for e in self.events)
def create_alerts(self, event_types: list[str], callback_args=None):
if callback_args is None:
callback_args = []
ret = []
for e in self.events:
types = self.get_events_mapping()[e].keys()
for et in event_types:
if et in types:
alert = self.get_events_mapping()[e][et]
if not isinstance(alert, Alert):
alert = alert(*callback_args)
if DT_CTRL * (self.event_counters[e] + 1) >= alert.creation_delay:
alert.alert_type = f"{self.get_event_name(e)}/{et}"
alert.event_type = et
ret.append(alert)
return ret
def add_from_msg(self, events):
for e in events:
bisect.insort(self.events, e.name.raw)
def to_msg(self):
ret = []
for event_name in self.events:
event = self.get_event_msg_type().new_message()
event.name = event_name
for event_type in self.get_events_mapping().get(event_name, {}):
setattr(event, event_type, True)
ret.append(event)
return ret
def has(self, event_name: int) -> bool:
return event_name in self.events
def contains_in_list(self, events_list: list[int]) -> bool:
return any(event_name in self.events for event_name in events_list)
def remove(self, event_name: int, static: bool = False) -> None:
if static and event_name in self.static_events:
self.static_events.remove(event_name)
if event_name in self.events:
self.event_counters[event_name] = self.event_counters[event_name] + 1
self.events.remove(event_name)
@abstractmethod
def get_events_mapping(self) -> dict[int, dict[str, Alert | AlertCallbackType]]:
raise NotImplementedError
@abstractmethod
def get_event_name(self, event: int) -> str:
raise NotImplementedError
@abstractmethod
def get_event_msg_type(self):
raise NotImplementedError
EmptyAlert = Alert("" , "", AlertStatus.normal, AlertSize.none, Priority.LOWEST,
VisualAlert.none, AudibleAlert.none, 0)
class NoEntryAlert(Alert):
def __init__(self, alert_text_2: str,
alert_text_1: str = "openpilot Unavailable",
visual_alert: car.CarControl.HUDControl.VisualAlert=VisualAlert.none):
super().__init__(alert_text_1, alert_text_2, AlertStatus.normal,
AlertSize.mid, Priority.LOW, visual_alert,
AudibleAlert.refuse, 3.)
class SoftDisableAlert(Alert):
def __init__(self, alert_text_2: str):
super().__init__("TAKE CONTROL IMMEDIATELY", alert_text_2,
AlertStatus.userPrompt, AlertSize.full,
Priority.MID, VisualAlert.steerRequired,
AudibleAlert.warningSoft, 2.),
# less harsh version of SoftDisable, where the condition is user-triggered
class UserSoftDisableAlert(SoftDisableAlert):
def __init__(self, alert_text_2: str):
super().__init__(alert_text_2),
self.alert_text_1 = "openpilot will disengage"
class ImmediateDisableAlert(Alert):
def __init__(self, alert_text_2: str):
super().__init__("TAKE CONTROL IMMEDIATELY", alert_text_2,
AlertStatus.critical, AlertSize.full,
Priority.HIGHEST, VisualAlert.steerRequired,
AudibleAlert.warningImmediate, 4.),
class EngagementAlert(Alert):
def __init__(self, audible_alert: car.CarControl.HUDControl.AudibleAlert):
super().__init__("", "",
AlertStatus.normal, AlertSize.none,
Priority.MID, VisualAlert.none,
audible_alert, .2),
class NormalPermanentAlert(Alert):
def __init__(self, alert_text_1: str, alert_text_2: str = "", duration: float = 0.2, priority: Priority = Priority.LOWER, creation_delay: float = 0.):
super().__init__(alert_text_1, alert_text_2,
AlertStatus.normal, AlertSize.mid if len(alert_text_2) else AlertSize.small,
priority, VisualAlert.none, AudibleAlert.none, duration, creation_delay=creation_delay),
class StartupAlert(Alert):
def __init__(self, alert_text_1: str, alert_text_2: str = "Always keep hands on wheel and eyes on road", alert_status=AlertStatus.normal):
super().__init__(alert_text_1, alert_text_2,
alert_status, AlertSize.mid,
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 5.),
+1 -7
View File
@@ -310,12 +310,6 @@ def hardware_thread(end_event, hw_queue) -> None:
# ensure device is fully booted
startup_conditions["device_booted"] = startup_conditions.get("device_booted", False) or HARDWARE.booted()
# user-forced status
offroad_mode = params.get_bool("OffroadMode")
startup_conditions["not_always_offroad"] = not offroad_mode
onroad_conditions["not_always_offroad"] = not offroad_mode
set_offroad_alert("OffroadMode_Status", offroad_mode)
# if the temperature enters the danger zone, go offroad to cool down
onroad_conditions["device_temp_good"] = thermal_status < ThermalStatus.danger
extra_text = f"{offroad_comp_temp:.1f}C"
@@ -398,7 +392,7 @@ def hardware_thread(end_event, hw_queue) -> None:
cloudlog.warning(f"shutting device down, offroad since {off_ts}")
params.put_bool("DoShutdown", True)
msg.deviceState.started = started_ts is not None and not offroad_mode
msg.deviceState.started = started_ts is not None
msg.deviceState.startedMonoTime = int(1e9*(started_ts or 0))
last_ping = params.get("LastAthenaPingTime")
+2 -3
View File
@@ -5,7 +5,7 @@ import signal
import sys
import traceback
from cereal import log, custom
from cereal import log
import cereal.messaging as messaging
import openpilot.system.sentry as sentry
from openpilot.common.params import Params, ParamKeyType
@@ -49,8 +49,7 @@ def manager_init() -> None:
("MadsPauseLateralOnBrake", "0"),
("MadsUnifiedEngagementMode", "1"),
("ModelManager_LastSyncTime", "0"),
("ModelManager_ModelsCache", ""),
("AccelPersonality", str(custom.LongitudinalPlanSP.AccelerationPersonality.stock)),
("ModelManager_ModelsCache", "")
]
if params.get_bool("RecordFrontLock"):
+5 -1
View File
@@ -116,8 +116,12 @@ def get_properties() -> tuple[str, str, str]:
def init(project: SentryProject) -> bool:
build_metadata = get_build_metadata()
# forks like to mess with this, so double check
# comma_remote = build_metadata.openpilot.comma_remote and "commaai" in build_metadata.openpilot.git_origin
# if not comma_remote or not is_registered_device() or PC:
# return False
env = build_metadata.channel_type
env = "release" if build_metadata.tested_channel else "master"
dongle_id, git_username, sunnylink_dongle_id = get_properties()
integrations = []
+2 -22
View File
@@ -10,11 +10,8 @@ from openpilot.common.basedir import BASEDIR
from openpilot.common.swaglog import cloudlog
from openpilot.common.git import get_commit, get_origin, get_branch, get_short_branch, get_commit_date
RELEASE_SP_BRANCHES = ['release-c3']
TESTED_SP_BRANCHES = ['staging-c3', 'staging-c3-new']
MASTER_SP_BRANCHES = ['master', 'master-new']
RELEASE_BRANCHES = ['release3-staging', 'release3', 'nightly'] + RELEASE_SP_BRANCHES
TESTED_BRANCHES = RELEASE_BRANCHES + ['devel', 'devel-staging', 'nightly-dev'] + TESTED_SP_BRANCHES
RELEASE_BRANCHES = ['release3-staging', 'release3', 'nightly']
TESTED_BRANCHES = RELEASE_BRANCHES + ['devel', 'devel-staging', 'nightly-dev']
BUILD_METADATA_FILENAME = "build.json"
@@ -113,23 +110,6 @@ class BuildMetadata:
def ui_description(self) -> str:
return f"{self.openpilot.version} / {self.openpilot.git_commit[:6]} / {self.channel}"
@property
def master_channel(self) -> bool:
return self.channel in MASTER_SP_BRANCHES
@property
def channel_type(self) -> str:
if self.channel.startswith("dev-"):
return "development"
elif self.channel.startswith("staging-"):
return "staging"
elif self.master_channel:
return "master"
elif self.tested_channel:
return "release"
else:
return "feature"
def build_metadata_from_dict(build_metadata: dict) -> BuildMetadata:
channel = build_metadata.get("channel", "unknown")