Compare commits

..

1 Commits

Author SHA1 Message Date
Jason Wen 783e3dad39 phase 1 2026-03-03 12:26:11 -05:00
2044 changed files with 128845 additions and 53685 deletions
+18
View File
@@ -0,0 +1,18 @@
**/.git
.DS_Store
*.dylib
*.DSYM
*.d
*.pyc
*.pyo
.*.swp
.*.swo
.*.un~
*.tmp
*.o
*.o-*
*.os
*.os-*
venv/
.venv/
+11 -2
View File
@@ -10,5 +10,14 @@
*.otf filter=lfs diff=lfs merge=lfs -text *.otf filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text *.wav filter=lfs diff=lfs merge=lfs -text
openpilot/selfdrive/car/tests/test_models_segs.txt filter=lfs diff=lfs merge=lfs -text selfdrive/car/tests/test_models_segs.txt filter=lfs diff=lfs merge=lfs -text
openpilot/common/hardware/tici/updater filter=lfs diff=lfs merge=lfs -text system/hardware/tici/updater_weston filter=lfs diff=lfs merge=lfs -text
system/hardware/tici/updater_magic filter=lfs diff=lfs merge=lfs -text
third_party/**/*.a filter=lfs diff=lfs merge=lfs -text
third_party/**/*.so filter=lfs diff=lfs merge=lfs -text
third_party/**/*.so.* filter=lfs diff=lfs merge=lfs -text
third_party/**/*.dylib filter=lfs diff=lfs merge=lfs -text
third_party/acados/*/t_renderer filter=lfs diff=lfs merge=lfs -text
third_party/qt5/larch64/bin/lrelease filter=lfs diff=lfs merge=lfs -text
third_party/qt5/larch64/bin/lupdate filter=lfs diff=lfs merge=lfs -text
third_party/catch2/include/catch2/catch.hpp filter=lfs diff=lfs merge=lfs -text
+8
View File
@@ -0,0 +1,8 @@
---
name: Enhancement
about: For openpilot enhancement suggestions
title: ''
labels: 'enhancement'
assignees: ''
---
+6 -6
View File
@@ -8,24 +8,24 @@ chore:
car: car:
- changed-files: - changed-files:
- any-glob-to-all-files: '{openpilot/selfdrive/car/**,opendbc_repo}' - any-glob-to-all-files: '{selfdrive/car/**,opendbc_repo}'
simulation: simulation:
- changed-files: - changed-files:
- any-glob-to-all-files: 'openpilot/tools/sim/**' - any-glob-to-all-files: 'tools/sim/**'
ui: ui:
- changed-files: - changed-files:
- any-glob-to-all-files: '{openpilot/selfdrive/assets/**,openpilot/selfdrive/ui/**,openpilot/system/ui/**}' - any-glob-to-all-files: '{selfdrive/assets/**,selfdrive/ui/**,system/ui/**}'
tools: tools:
- changed-files: - changed-files:
- any-glob-to-all-files: 'openpilot/tools/**' - any-glob-to-all-files: 'tools/**'
multilanguage: multilanguage:
- changed-files: - changed-files:
- any-glob-to-all-files: 'openpilot/selfdrive/ui/translations/**' - any-glob-to-all-files: 'selfdrive/ui/translations/**'
autonomy: autonomy:
- changed-files: - changed-files:
- any-glob-to-all-files: "{openpilot/selfdrive/modeld/models/**,openpilot/selfdrive/test/process_replay/model_replay_ref_commit,openpilot/sunnypilot/modeld*/models/**}" - any-glob-to-all-files: "{selfdrive/modeld/models/**,selfdrive/test/process_replay/model_replay_ref_commit,sunnypilot/modeld*/models/**}"
+2 -2
View File
@@ -44,8 +44,8 @@ Explain how you tested this bug fix.
**Checklist** **Checklist**
- [ ] added entry to CAR in openpilot/selfdrive/car/*/values.py and ran `openpilot/selfdrive/car/docs.py` to generate new docs - [ ] added entry to CAR in selfdrive/car/*/values.py and ran `selfdrive/car/docs.py` to generate new docs
- [ ] test route added to [routes.py](https://github.com/commaai/openpilot/blob/master/openpilot/selfdrive/car/tests/routes.py) - [ ] test route added to [routes.py](https://github.com/commaai/openpilot/blob/master/selfdrive/car/tests/routes.py)
- [ ] route with openpilot: - [ ] route with openpilot:
- [ ] route with stock system: - [ ] route with stock system:
- [ ] car harness used (if comma doesn't sell it, put N/A): - [ ] car harness used (if comma doesn't sell it, put N/A):
+2 -2
View File
@@ -12,7 +12,7 @@ jobs:
issues: write issues: write
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v6
with: with:
submodules: false submodules: false
@@ -24,7 +24,7 @@ jobs:
# Check PR target branch # Check PR target branch
- name: check branch - name: check branch
uses: Vankka/pr-target-branch-action@5da68a42bcb7b43d39104295a876a6f3f8d7908b uses: Vankka/pr-target-branch-action@def32ec9d93514138d6ac0132ee62e120a72aed5
if: github.repository == 'sunnypilot/sunnypilot' if: github.repository == 'sunnypilot/sunnypilot'
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -34,10 +34,10 @@ jobs:
echo "tinygrad_ref=$ref" >> $GITHUB_OUTPUT echo "tinygrad_ref=$ref" >> $GITHUB_OUTPUT
echo "tinygrad_ref is $ref" echo "tinygrad_ref is $ref"
- name: Checkout docs repo (sunnypilot-models, gh-pages) - name: Checkout docs repo (sunnypilot-docs, gh-pages)
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
repository: sunnypilot/sunnypilot-models repository: sunnypilot/sunnypilot-docs
ref: gh-pages ref: gh-pages
path: docs path: docs
ssh-key: ${{ secrets.CI_SUNNYPILOT_DOCS_PRIVATE_KEY }} ssh-key: ${{ secrets.CI_SUNNYPILOT_DOCS_PRIVATE_KEY }}
@@ -120,7 +120,6 @@ jobs:
with: with:
upstream_branch: ${{ matrix.model.ref }} upstream_branch: ${{ matrix.model.ref }}
custom_name: ${{ matrix.model.display_name }} custom_name: ${{ matrix.model.display_name }}
is_20hz: ${{ matrix.model.is_20hz }}
recompiled_dir: ${{ needs.setup.outputs.recompiled_dir }} recompiled_dir: ${{ needs.setup.outputs.recompiled_dir }}
json_version: ${{ needs.setup.outputs.json_version }} json_version: ${{ needs.setup.outputs.json_version }}
secrets: inherit secrets: inherit
@@ -158,7 +157,6 @@ jobs:
with: with:
upstream_branch: ${{ matrix.model.ref }} upstream_branch: ${{ matrix.model.ref }}
custom_name: ${{ matrix.model.display_name }} custom_name: ${{ matrix.model.display_name }}
is_20hz: ${{ matrix.model.is_20hz }}
recompiled_dir: ${{ needs.setup.outputs.recompiled_dir }} recompiled_dir: ${{ needs.setup.outputs.recompiled_dir }}
json_version: ${{ needs.setup.outputs.json_version }} json_version: ${{ needs.setup.outputs.json_version }}
artifact_suffix: -retry artifact_suffix: -retry
@@ -204,7 +202,7 @@ jobs:
- name: Checkout docs repo - name: Checkout docs repo
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
repository: sunnypilot/sunnypilot-models repository: sunnypilot/sunnypilot-docs
ref: gh-pages ref: gh-pages
path: docs path: docs
ssh-key: ${{ secrets.CI_SUNNYPILOT_DOCS_PRIVATE_KEY }} ssh-key: ${{ secrets.CI_SUNNYPILOT_DOCS_PRIVATE_KEY }}
@@ -24,11 +24,6 @@ on:
required: false required: false
type: string type: string
default: '' default: ''
is_20hz:
description: 'Is this a 20Hz model'
required: false
type: boolean
default: true
bypass_push: bypass_push:
description: 'Bypass pushing to GitLab for build-all' description: 'Bypass pushing to GitLab for build-all'
required: false required: false
@@ -44,11 +39,6 @@ on:
description: 'Custom name for the model (no date, only name)' description: 'Custom name for the model (no date, only name)'
required: false required: false
type: string type: string
is_20hz:
description: 'Is this a 20Hz model'
required: false
type: boolean
default: true
recompiled_dir: recompiled_dir:
description: 'Existing recompiled directory number (e.g. 3 for recompiled3)' description: 'Existing recompiled directory number (e.g. 3 for recompiled3)'
required: true required: true
@@ -92,7 +82,7 @@ jobs:
with: with:
upstream_branch: ${{ inputs.upstream_branch }} upstream_branch: ${{ inputs.upstream_branch }}
custom_name: ${{ inputs.custom_name || inputs.upstream_branch }} custom_name: ${{ inputs.custom_name || inputs.upstream_branch }}
is_20hz: ${{ inputs.is_20hz }} is_20hz: true
artifact_suffix: ${{ inputs.artifact_suffix }} artifact_suffix: ${{ inputs.artifact_suffix }}
secrets: inherit secrets: inherit
@@ -129,7 +119,7 @@ jobs:
- name: Checkout docs repo - name: Checkout docs repo
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
repository: sunnypilot/sunnypilot-models repository: sunnypilot/sunnypilot-docs
ref: gh-pages ref: gh-pages
path: docs path: docs
ssh-key: ${{ secrets.CI_SUNNYPILOT_DOCS_PRIVATE_KEY }} ssh-key: ${{ secrets.CI_SUNNYPILOT_DOCS_PRIVATE_KEY }}
+43 -62
View File
@@ -6,7 +6,7 @@ on:
- master - master
pull_request: pull_request:
paths: paths:
- 'openpilot/cereal/**' - 'cereal/**'
workflow_dispatch: workflow_dispatch:
workflow_call: workflow_call:
inputs: inputs:
@@ -23,75 +23,56 @@ env:
CI: 1 CI: 1
jobs: jobs:
generate_cereal_artifact:
name: Generate cereal validation artifacts
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
with:
submodules: true
- run: ./tools/op.sh setup
- name: Build openpilot
run: scons -j$(nproc) cereal
- name: Generate the log file
run: |
export PYTHONPATH=${{ github.workspace }}
python3 cereal/messaging/tests/validate_sp_cereal_upstream.py -g -f schema_instances.bin
- name: 'Prepare artifact'
run: |
mkdir -p "cereal/messaging/tests/cereal_validations"
cp cereal/messaging/tests/validate_sp_cereal_upstream.py "cereal/messaging/tests/cereal_validations/validate_sp_cereal_upstream.py"
cp schema_instances.bin "cereal/messaging/tests/cereal_validations/schema_instances.bin"
- name: 'Upload Artifact'
uses: actions/upload-artifact@v4
with:
name: cereal_validations
path: cereal/messaging/tests/cereal_validations
validate_cereal_with_upstream: validate_cereal_with_upstream:
name: Validate cereal with Upstream name: Validate cereal with Upstream
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
needs: generate_cereal_artifact
steps: steps:
- name: Checkout sunnypilot cereal - name: Checkout sunnypilot
uses: actions/checkout@v6 uses: actions/checkout@v6
with:
sparse-checkout: |
openpilot/cereal
uv.lock
submodules: false
- name: Init sunnypilot opendbc submodule
run: git submodule update --init --depth 1 opendbc_repo
- name: Locate sunnypilot capnp import paths
id: locate-sp-capnp
run: |
SP_IMPORT_ARGS=""
SP_CAR_CAPNP=$(find opendbc_repo -maxdepth 4 -name car.capnp -path '*/opendbc/car/car.capnp' -printf '%h\n' -quit)
if [ -n "$SP_CAR_CAPNP" ]; then
SP_IMPORT_ARGS="-I $SP_CAR_CAPNP"
echo "Found sunnypilot car.capnp at: $SP_CAR_CAPNP"
fi
echo "import_args=$SP_IMPORT_ARGS" >> "$GITHUB_OUTPUT"
- name: Checkout upstream openpilot - name: Checkout upstream openpilot
uses: actions/checkout@v6 uses: actions/checkout@v6
with: with:
repository: 'commaai/openpilot' repository: 'commaai/openpilot'
path: upstream_openpilot path: openpilot
submodules: true
ref: "refs/heads/master" ref: "refs/heads/master"
- run: ./tools/op.sh setup
- name: Init upstream opendbc submodule - name: Build openpilot
working-directory: upstream_openpilot working-directory: openpilot
run: git submodule update --init --depth 1 opendbc_repo run: scons -j$(nproc) cereal
- name: Download build artifacts
- name: Locate upstream capnp paths uses: actions/download-artifact@v4
id: locate-capnp with:
name: cereal_validations
path: openpilot/cereal/messaging/tests/cereal_validations
- name: 'Run the validation'
run: | run: |
CEREAL_DIR=$(find upstream_openpilot -maxdepth 4 -name log.capnp -path '*/cereal/log.capnp' -printf '%h\n' -quit) export PYTHONPATH=${{ github.workspace }}/openpilot
if [ -z "$CEREAL_DIR" ]; then chmod +x openpilot/cereal/messaging/tests/cereal_validations/validate_sp_cereal_upstream.py
echo "::error::Could not locate cereal/log.capnp in upstream openpilot" python3 openpilot/cereal/messaging/tests/cereal_validations/validate_sp_cereal_upstream.py -r -f openpilot/cereal/messaging/tests/cereal_validations/schema_instances.bin
exit 1
fi
echo "cereal_dir=$CEREAL_DIR" >> "$GITHUB_OUTPUT"
echo "Found upstream cereal at: $CEREAL_DIR"
IMPORT_ARGS=""
CAR_CAPNP=$(find upstream_openpilot -maxdepth 5 -name car.capnp -path '*/opendbc/car/car.capnp' -printf '%h\n' -quit)
if [ -n "$CAR_CAPNP" ]; then
IMPORT_ARGS="-I $CAR_CAPNP"
echo "Found car.capnp at: $CAR_CAPNP"
fi
echo "import_args=$IMPORT_ARGS" >> "$GITHUB_OUTPUT"
- name: Install pycapnp
run: |
PYCAPNP_VER=$(python3 -c "import re; m=re.search(r'name = \"pycapnp\"\nversion = \"([^\"]+)\"', open('uv.lock').read()); print(m.group(1))")
pip install "pycapnp==${PYCAPNP_VER}"
- name: Generate sunnypilot schema
run: |
python3 openpilot/cereal/messaging/tests/validate_sp_cereal_upstream.py \
-g -f /tmp/sp_schema.json --cereal-dir openpilot/cereal \
${{ steps.locate-sp-capnp.outputs.import_args }}
- name: Validate against upstream
run: |
python3 openpilot/cereal/messaging/tests/validate_sp_cereal_upstream.py \
-r -f /tmp/sp_schema.json --cereal-dir ${{ steps.locate-capnp.outputs.cereal_dir }} \
${{ steps.locate-capnp.outputs.import_args }}
-57
View File
@@ -1,57 +0,0 @@
name: diff report
on:
pull_request_target:
types: [opened, synchronize, reopened]
jobs:
comment:
name: comment
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
pull-requests: write
actions: read
steps:
- name: Wait for process replay
id: wait
continue-on-error: true
uses: lewagon/wait-on-check-action@1d57e2c51a58d812d2765e036a028b6bdb5a6154
with:
ref: ${{ github.event.pull_request.head.sha }}
check-name: process replay
repo-token: ${{ secrets.GITHUB_TOKEN }}
allowed-conclusions: success,failure
wait-interval: 20
- name: Get tests run ID
if: steps.wait.outcome == 'success'
id: tests-run
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
run_id=$(gh api \
"repos/${{ github.repository }}/actions/workflows/tests.yaml/runs?event=pull_request&head_sha=${{ github.event.pull_request.head.sha }}&per_page=1" \
--jq '.workflow_runs[0].id // empty')
if [ -z "$run_id" ]; then
echo "No tests.yaml run found for ${{ github.event.pull_request.head.sha }}" >&2
exit 1
fi
echo "run-id=$run_id" >> "$GITHUB_OUTPUT"
- name: Download diff
if: steps.wait.outcome == 'success'
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
repository: ${{ github.repository }}
run-id: ${{ steps.tests-run.outputs.run-id }}
name: diff_report_${{ github.event.number }}
path: .
- name: Comment on PR
if: steps.wait.outcome == 'success'
uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b
with:
file-path: diff_report.txt
comment-tag: diff_report
pr-number: ${{ github.event.number }}
github-token: ${{ secrets.GITHUB_TOKEN }}
+6 -6
View File
@@ -22,19 +22,19 @@ jobs:
steps: steps:
- uses: commaai/timeout@v1 - uses: commaai/timeout@v1
- uses: actions/checkout@v7 - uses: actions/checkout@v6
with: with:
submodules: true submodules: true
# Build # Build
- name: Build docs - name: Build docs
run: | run: |
git lfs pull # TODO: can we install just the "docs" dependency group without the normal deps?
pip install zensical pip install mkdocs
python scripts/docs.py build mkdocs build
# Push to docs.comma.ai # Push to docs.comma.ai
- uses: actions/checkout@v7 - uses: actions/checkout@v6
if: github.ref == 'refs/heads/master' && github.repository == 'sunnypilot/sunnypilot' if: github.ref == 'refs/heads/master' && github.repository == 'sunnypilot/sunnypilot'
with: with:
path: openpilot-docs path: openpilot-docs
@@ -45,7 +45,7 @@ jobs:
run: | run: |
set -x set -x
source tools/release/identity.sh source release/identity.sh
cd openpilot-docs cd openpilot-docs
git checkout --orphan tmp git checkout --orphan tmp
+4 -4
View File
@@ -11,7 +11,7 @@ jobs:
contents: write contents: write
steps: steps:
- name: Delete stale Jenkins branches - name: Delete stale Jenkins branches
uses: actions/github-script@v9 uses: actions/github-script@v8
with: with:
script: | script: |
const cutoff = Date.now() - 24 * 60 * 60 * 1000; const cutoff = Date.now() - 24 * 60 * 60 * 1000;
@@ -52,7 +52,7 @@ jobs:
steps: steps:
- name: Check for trigger phrase - name: Check for trigger phrase
id: check_comment id: check_comment
uses: actions/github-script@v9 uses: actions/github-script@v8
with: with:
script: | script: |
const triggerPhrase = "trigger-jenkins"; const triggerPhrase = "trigger-jenkins";
@@ -72,7 +72,7 @@ jobs:
- name: Checkout repository - name: Checkout repository
if: steps.check_comment.outputs.result == 'true' if: steps.check_comment.outputs.result == 'true'
uses: actions/checkout@v7 uses: actions/checkout@v6
with: with:
ref: refs/pull/${{ github.event.issue.number }}/head ref: refs/pull/${{ github.event.issue.number }}/head
@@ -86,7 +86,7 @@ jobs:
- name: Delete trigger comment - name: Delete trigger comment
if: steps.check_comment.outputs.result == 'true' && always() if: steps.check_comment.outputs.result == 'true' && always()
uses: actions/github-script@v9 uses: actions/github-script@v8
with: with:
script: | script: |
await github.rest.issues.deleteComment({ await github.rest.issues.deleteComment({
+4 -4
View File
@@ -4,7 +4,7 @@ on:
pull_request: pull_request:
types: [opened, reopened, synchronize] types: [opened, reopened, synchronize]
paths: paths:
- 'openpilot/selfdrive/modeld/models/*.onnx' - 'selfdrive/modeld/models/*.onnx'
workflow_dispatch: workflow_dispatch:
jobs: jobs:
@@ -16,11 +16,11 @@ jobs:
if: github.repository == 'commaai/openpilot' if: github.repository == 'commaai/openpilot'
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v7 uses: actions/checkout@v6
with: with:
submodules: true submodules: true
- name: Checkout master - name: Checkout master
uses: actions/checkout@v7 uses: actions/checkout@v6
with: with:
ref: master ref: master
path: base path: base
@@ -36,7 +36,7 @@ jobs:
echo "EOF" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT
- name: Post model report comment - name: Post model report comment
uses: marocchino/sticky-pull-request-comment@0ea0beb66eb9baf113663a64ec522f60e49231c0 uses: marocchino/sticky-pull-request-comment@baa7203ed60924babbe5dcd0ac8eae3b66ec5e16
with: with:
header: model-review header: model-review
message: ${{ steps.report.outputs.content }} message: ${{ steps.report.outputs.content }}
+3 -3
View File
@@ -15,17 +15,17 @@ jobs:
steps: steps:
- name: Wait for green check mark - name: Wait for green check mark
if: ${{ github.event_name == 'schedule' }} if: ${{ github.event_name == 'schedule' }}
uses: lewagon/wait-on-check-action@1d57e2c51a58d812d2765e036a028b6bdb5a6154 uses: lewagon/wait-on-check-action@ccfb013c15c8afb7bf2b7c028fb74dc5a068cccc
with: with:
ref: master ref: master
wait-interval: 30 wait-interval: 30
running-workflow-name: 'build __nightly' running-workflow-name: 'build __nightly'
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
check-regexp: ^((?!.*(build prebuilt|create badges).*).)*$ check-regexp: ^((?!.*(build prebuilt|create badges).*).)*$
- uses: actions/checkout@v4 # checkout version > v4 breaks nightly release script - uses: actions/checkout@v6
with: with:
submodules: true submodules: true
fetch-depth: 0 fetch-depth: 0
- run: ./tools/op.sh setup - run: ./tools/op.sh setup
- name: Push __nightly - name: Push __nightly
run: BRANCH=__nightly tools/release/build_stripped.sh run: BRANCH=__nightly release/build_stripped.sh
+27 -48
View File
@@ -3,22 +3,40 @@ name: repo maintenance
on: on:
schedule: schedule:
- cron: "0 14 * * 1" # every Monday at 2am UTC (6am PST) - cron: "0 14 * * 1" # every Monday at 2am UTC (6am PST)
pull_request:
types: [closed]
workflow_dispatch: workflow_dispatch:
env: env:
PYTHONPATH: ${{ github.workspace }} PYTHONPATH: ${{ github.workspace }}
jobs: jobs:
update_translations:
runs-on: ubuntu-latest
if: github.repository == 'sunnypilot/sunnypilot'
steps:
- uses: actions/checkout@v6
with:
submodules: true
- run: ./tools/op.sh setup
- name: Update translations
run: python3 selfdrive/ui/update_translations.py --vanish
- name: Create Pull Request
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0
with:
author: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
commit-message: "Update translations"
title: "[bot] Update translations"
body: "Automatic PR from repo-maintenance -> update_translations"
branch: "update-translations"
base: "master"
delete-branch: true
labels: bot
package_updates: package_updates:
name: package_updates name: package_updates
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: >- if: github.repository == 'sunnypilot/sunnypilot'
github.repository == 'sunnypilot/sunnypilot' &&
(github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v6
with: with:
submodules: true submodules: true
- run: ./tools/op.sh setup - run: ./tools/op.sh setup
@@ -54,10 +72,11 @@ jobs:
git add . git add .
- name: update car docs - name: update car docs
run: | run: |
python openpilot/selfdrive/car/docs.py scons -j$(nproc) --minimal opendbc_repo
python selfdrive/car/docs.py
git add docs/CARS.md git add docs/CARS.md
- name: Create Pull Request - name: Create Pull Request
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0
with: with:
author: github-actions[bot] <github-actions[bot]@users.noreply.github.com> author: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
token: ${{ github.repository == 'commaai/openpilot' && secrets.ACTIONS_CREATE_PR_PAT || secrets.GITHUB_TOKEN }} token: ${{ github.repository == 'commaai/openpilot' && secrets.ACTIONS_CREATE_PR_PAT || secrets.GITHUB_TOKEN }}
@@ -79,43 +98,3 @@ jobs:
${{ steps.pip_tree.outputs.PIP_TREE }} ${{ steps.pip_tree.outputs.PIP_TREE }}
``` ```
labels: bot labels: bot
cleanup_closed_branches:
if: github.repository == 'commaai/openpilot'
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: read
steps:
- uses: actions/github-script@v9
with:
script: |
const { owner, repo } = context.repo;
const upstream = `${owner}/${repo}`;
for await (const response of github.paginate.iterator(github.rest.pulls.list, {
owner,
repo,
state: 'closed',
per_page: 100,
})) {
for (const pr of response.data) {
if (pr.head.repo?.full_name !== upstream) continue;
const branch = pr.head.ref;
try {
await github.rest.git.deleteRef({
owner,
repo,
ref: `heads/${branch}`,
});
console.log(`Deleted branch ${branch} (PR #${pr.number})`);
} catch (error) {
if (error.status === 422 || error.status === 403) {
console.log(`Skipping branch ${branch} (PR #${pr.number}): ${error.message}`);
continue;
}
throw error;
}
}
}
+8 -46
View File
@@ -164,54 +164,18 @@ jobs:
source /etc/profile source /etc/profile
export UV_PROJECT_ENVIRONMENT=${HOME}/venv export UV_PROJECT_ENVIRONMENT=${HOME}/venv
export VIRTUAL_ENV=$UV_PROJECT_ENVIRONMENT export VIRTUAL_ENV=$UV_PROJECT_ENVIRONMENT
export PYTHONPATH="${PYTHONPATH}:${{ env.TINYGRAD_PATH }}:${{ github.workspace }}" export PYTHONPATH="${PYTHONPATH}:${{ env.TINYGRAD_PATH }}"
COMPILE_MODELD="${{ github.workspace }}/sunnypilot/modeld_v2/compile_modeld.py" # Loop through all .onnx files
MODEL_SIZE=$(python3 -c "from openpilot.common.transformations.model import MEDMODEL_INPUT_SIZE as s; print(f'{s[0]}x{s[1]}')")
CAMERA_RES=$(python3 -c "from openpilot.common.transformations.camera import _ar_ox_fisheye as a, _os_fisheye as o; print(f'{a.width}x{a.height} {o.width}x{o.height}')")
TG_FLAGS="DEV=QCOM IMAGE=1 FLOAT16=1 NOLOCALS=1 JIT_BATCH_SIZE=0 OPENPILOT_HACKS=1"
# Generate metadata for all ONNX files
find "${{ env.MODELS_DIR }}" -maxdepth 1 -name '*.onnx' | while IFS= read -r onnx_file; do find "${{ env.MODELS_DIR }}" -maxdepth 1 -name '*.onnx' | while IFS= read -r onnx_file; do
echo "Generating metadata: $onnx_file" base_name=$(basename "$onnx_file" .onnx)
env ${TG_FLAGS} python3 "${{ env.MODELS_DIR }}/../get_model_metadata.py" "$onnx_file" || true output_file="${{ env.MODELS_DIR }}/${base_name}_tinygrad.pkl"
echo "Compiling: $onnx_file -> $output_file"
QCOM=1 python3 "${{ env.TINYGRAD_PATH }}/examples/openpilot/compile3.py" "$onnx_file" "$output_file"
DEV=QCOM FLOAT16=1 NOLOCALS=1 JIT_BATCH_SIZE=0 python3 "${{ env.MODELS_DIR }}/../get_model_metadata.py" "$onnx_file" || true
done done
# Detect model type and build compile args
VISION_ONNX="${{ env.MODELS_DIR }}/driving_vision.onnx"
POLICY_ONNX="${{ env.MODELS_DIR }}/driving_policy.onnx"
OFF_POLICY_ONNX="${{ env.MODELS_DIR }}/driving_off_policy.onnx"
ON_POLICY_ONNX="${{ env.MODELS_DIR }}/driving_on_policy.onnx"
SUPERCOMBO_ONNX="${{ env.MODELS_DIR }}/supercombo.onnx"
MODEL_TYPE="" ONNX_ARGS="" OUTPUT_NAME=""
if [ -f "$VISION_ONNX" ]; then
ONNX_ARGS="--vision-onnx $VISION_ONNX"
if [ -f "$ON_POLICY_ONNX" ] && [ -f "$OFF_POLICY_ONNX" ]; then
MODEL_TYPE=vision_multi_policy
ONNX_ARGS="$ONNX_ARGS --off-policy-onnx $OFF_POLICY_ONNX --on-policy-onnx $ON_POLICY_ONNX"
elif [ -f "$OFF_POLICY_ONNX" ] && [ -f "$POLICY_ONNX" ]; then
MODEL_TYPE=vision_multi_policy
ONNX_ARGS="$ONNX_ARGS --policy-onnx $POLICY_ONNX --off-policy-onnx $OFF_POLICY_ONNX"
elif [ -f "$POLICY_ONNX" ]; then
MODEL_TYPE=vision_policy
ONNX_ARGS="$ONNX_ARGS --policy-onnx $POLICY_ONNX"
fi
elif [ -f "$SUPERCOMBO_ONNX" ]; then
MODEL_TYPE=supercombo
ONNX_ARGS="--supercombo-onnx $SUPERCOMBO_ONNX"
fi
if [ -n "$MODEL_TYPE" ]; then
echo "Detected: $MODEL_TYPE -> driving_tinygrad.pkl"
env ${TG_FLAGS} python3 "$COMPILE_MODELD" \
--model-type $MODEL_TYPE \
--model-size $MODEL_SIZE \
--camera-resolutions $CAMERA_RES \
$ONNX_ARGS \
--output "${{ env.MODELS_DIR }}/driving_tinygrad.pkl"
fi
- name: Validate Model Outputs - name: Validate Model Outputs
run: | run: |
source /etc/profile source /etc/profile
@@ -230,8 +194,6 @@ jobs:
rsync -avm \ rsync -avm \
--include='*.dlc' \ --include='*.dlc' \
--include='*.pkl' \ --include='*.pkl' \
--include='*.chunk*' \
--include='*.chunkmanifest' \
--include='*.onnx' \ --include='*.onnx' \
--exclude='*' \ --exclude='*' \
--delete-excluded \ --delete-excluded \
@@ -79,7 +79,7 @@ jobs:
is_stable_branch="$(echo "$CONFIG" | jq -r '.stable_branch // false')"; is_stable_branch="$(echo "$CONFIG" | jq -r '.stable_branch // false')";
echo "is_stable_branch=$is_stable_branch" >> $GITHUB_OUTPUT echo "is_stable_branch=$is_stable_branch" >> $GITHUB_OUTPUT
stable_version=$(cat openpilot/sunnypilot/common/version.h | grep SUNNYPILOT_VERSION | sed -e 's/[^0-9|.]//g'); stable_version=$(cat sunnypilot/common/version.h | grep SUNNYPILOT_VERSION | sed -e 's/[^0-9|.]//g');
echo "version=$([ "$is_stable_branch" = "true" ] && echo "$stable_version" || echo "$BUILD")" >> $GITHUB_OUTPUT echo "version=$([ "$is_stable_branch" = "true" ] && echo "$stable_version" || echo "$BUILD")" >> $GITHUB_OUTPUT
echo "extra_version_identifier=${environment}" >> $GITHUB_OUTPUT echo "extra_version_identifier=${environment}" >> $GITHUB_OUTPUT
fi fi
@@ -177,7 +177,7 @@ jobs:
- name: Build Main Project - name: Build Main Project
run: | run: |
export PYTHONPATH="$BUILD_DIR" export PYTHONPATH="$BUILD_DIR"
./tools/release/release_files.py | sort | uniq | rsync -rRl${RUNNER_DEBUG:+v} --files-from=- . $BUILD_DIR/ ./release/release_files.py | sort | uniq | rsync -rRl${RUNNER_DEBUG:+v} --files-from=- . $BUILD_DIR/
cd $BUILD_DIR cd $BUILD_DIR
sed -i '/from .board.jungle import PandaJungle, PandaJungleDFU/s/^/#/' panda/__init__.py sed -i '/from .board.jungle import PandaJungle, PandaJungleDFU/s/^/#/' panda/__init__.py
echo "Building sunnypilot's modeld_v2..." echo "Building sunnypilot's modeld_v2..."
@@ -185,7 +185,7 @@ jobs:
echo "Building sunnypilot's locationd..." echo "Building sunnypilot's locationd..."
scons -j2 cache_dir=${{env.SCONS_CACHE_DIR}} --minimal sunnypilot/selfdrive/locationd scons -j2 cache_dir=${{env.SCONS_CACHE_DIR}} --minimal sunnypilot/selfdrive/locationd
echo "Building openpilot's locationd..." echo "Building openpilot's locationd..."
scons -j1 cache_dir=${{env.SCONS_CACHE_DIR}} --minimal selfdrive/locationd scons -j$(nproc) cache_dir=${{env.SCONS_CACHE_DIR}} --minimal selfdrive/locationd
echo "Building rest of sunnypilot" echo "Building rest of sunnypilot"
scons -j$(nproc) cache_dir=${{env.SCONS_CACHE_DIR}} --minimal scons -j$(nproc) cache_dir=${{env.SCONS_CACHE_DIR}} --minimal
touch ${BUILD_DIR}/prebuilt touch ${BUILD_DIR}/prebuilt
@@ -208,17 +208,17 @@ jobs:
--exclude='Jenkinsfile' \ --exclude='Jenkinsfile' \
--exclude='**/release/' \ --exclude='**/release/' \
--exclude='**/.github/' \ --exclude='**/.github/' \
--exclude='**/openpilot/selfdrive/ui/replay/' \ --exclude='**/selfdrive/ui/replay/' \
--exclude='**/__pycache__/' \ --exclude='**/__pycache__/' \
--exclude='${{env.SCONS_CACHE_DIR}}' \ --exclude='${{env.SCONS_CACHE_DIR}}' \
--exclude='**/.git/' \ --exclude='**/.git/' \
--exclude='**/SConstruct' \ --exclude='**/SConstruct' \
--exclude='**/SConscript' \ --exclude='**/SConscript' \
--exclude='**/.venv/' \ --exclude='**/.venv/' \
--exclude='openpilot/selfdrive/modeld/models/*.onnx*' \ --exclude='selfdrive/modeld/models/driving_vision.onnx' \
--exclude='openpilot/sunnypilot/modeld*/models/*.onnx*' \ --exclude='selfdrive/modeld/models/driving_policy.onnx' \
--exclude='openpilot/third_party/*x86*' \ --exclude='third_party/*x86*' \
--exclude='openpilot/third_party/*Darwin*' \ --exclude='third_party/*Darwin*' \
--delete-excluded \ --delete-excluded \
--chown=comma:comma \ --chown=comma:comma \
${BUILD_DIR}/ ${OUTPUT_DIR}/ ${BUILD_DIR}/ ${OUTPUT_DIR}/
+47 -60
View File
@@ -35,27 +35,27 @@ jobs:
STRIPPED_DIR: /tmp/releasepilot STRIPPED_DIR: /tmp/releasepilot
PYTHONPATH: /tmp/releasepilot PYTHONPATH: /tmp/releasepilot
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v6
with: with:
submodules: true submodules: true
- name: Getting LFS files - name: Getting LFS files
uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 uses: nick-fields/retry@7152eba30c6575329ac0576536151aca5a72780e
with: with:
timeout_minutes: 2 timeout_minutes: 2
max_attempts: 3 max_attempts: 3
command: git lfs pull command: git lfs pull
- name: Build devel - name: Build devel
timeout-minutes: 3 timeout-minutes: 1
run: TARGET_DIR=$STRIPPED_DIR tools/release/build_stripped.sh run: TARGET_DIR=$STRIPPED_DIR release/build_stripped.sh
- run: ./tools/op.sh setup - run: ./tools/op.sh setup
- name: Build openpilot and run checks - name: Build openpilot and run checks
timeout-minutes: 30 timeout-minutes: 30
working-directory: ${{ env.STRIPPED_DIR }} working-directory: ${{ env.STRIPPED_DIR }}
run: python3 openpilot/system/manager/build.py run: python3 system/manager/build.py
- name: Run tests - name: Run tests
timeout-minutes: 1 timeout-minutes: 1
working-directory: ${{ env.STRIPPED_DIR }} working-directory: ${{ env.STRIPPED_DIR }}
run: tools/release/check-dirty.sh run: release/check-dirty.sh
- name: Check submodules - name: Check submodules
if: github.repository == 'sunnypilot/sunnypilot' if: github.repository == 'sunnypilot/sunnypilot'
timeout-minutes: 3 timeout-minutes: 3
@@ -75,13 +75,15 @@ jobs:
export CHECK_PR_REFS=true export CHECK_PR_REFS=true
fi fi
fi fi
tools/release/check-submodules.sh release/check-submodules.sh
build_mac: build_mac:
name: build macOS name: build macOS
runs-on: ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-macos-8x14' || 'macos-latest' }} runs-on: ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-macos-8x14' || 'macos-latest' }}
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v6
with:
submodules: true
- name: Remove Homebrew from environment - name: Remove Homebrew from environment
run: | run: |
FILTERED=$(echo "$PATH" | tr ':' '\n' | grep -v '/opt/homebrew' | tr '\n' ':') FILTERED=$(echo "$PATH" | tr ':' '\n' | grep -v '/opt/homebrew' | tr '\n' ':')
@@ -99,7 +101,9 @@ jobs:
&& fromJSON('["namespace-profile-amd64-8x16"]') && fromJSON('["namespace-profile-amd64-8x16"]')
|| fromJSON('["ubuntu-24.04"]') }} || fromJSON('["ubuntu-24.04"]') }}
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v6
with:
submodules: true
- run: ./tools/op.sh setup - run: ./tools/op.sh setup
- name: Static analysis - name: Static analysis
timeout-minutes: 1 timeout-minutes: 1
@@ -114,18 +118,16 @@ jobs:
&& fromJSON('["namespace-profile-amd64-8x16"]') && fromJSON('["namespace-profile-amd64-8x16"]')
|| fromJSON('["ubuntu-24.04"]') }} || fromJSON('["ubuntu-24.04"]') }}
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v6
with:
submodules: true
- run: ./tools/op.sh setup - run: ./tools/op.sh setup
- name: Install raylib headless dependencies
if: ${{ !contains(runner.name, 'nsc') }}
run: sudo apt-get install -y --no-install-recommends libgles2 libegl1
- name: Build openpilot - name: Build openpilot
run: scons run: scons -j$(nproc)
- name: Run unit tests - name: Run unit tests
timeout-minutes: ${{ contains(runner.name, 'nsc') && 2 || 999 }} timeout-minutes: ${{ contains(runner.name, 'nsc') && 2 || 999 }}
env:
RAYLIB_BACKEND: headless
run: | run: |
source selfdrive/test/setup_xvfb.sh
# Pre-compile Python bytecode so each pytest worker doesn't need to # Pre-compile Python bytecode so each pytest worker doesn't need to
$PYTEST --collect-only -m 'not slow' -qq $PYTEST --collect-only -m 'not slow' -qq
MAX_EXAMPLES=1 $PYTEST -m 'not slow' MAX_EXAMPLES=1 $PYTEST -m 'not slow'
@@ -140,42 +142,34 @@ jobs:
&& fromJSON('["namespace-profile-amd64-8x16"]') && fromJSON('["namespace-profile-amd64-8x16"]')
|| fromJSON('["ubuntu-24.04"]') }} || fromJSON('["ubuntu-24.04"]') }}
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v6
with:
submodules: true
- run: ./tools/op.sh setup - run: ./tools/op.sh setup
- name: Build openpilot - name: Build openpilot
run: scons run: scons -j$(nproc)
- name: Run replay - name: Run replay
timeout-minutes: ${{ contains(runner.name, 'nsc') && 2 || 20 }} timeout-minutes: ${{ contains(runner.name, 'nsc') && 2 || 20 }}
continue-on-error: ${{ github.ref == 'refs/heads/master' }} continue-on-error: ${{ github.ref == 'refs/heads/master' }}
run: openpilot/selfdrive/test/process_replay/test_processes.py -j$(nproc) run: selfdrive/test/process_replay/test_processes.py -j$(nproc)
- name: Print diff - name: Print diff
id: print-diff id: print-diff
if: always() if: always()
run: cat openpilot/selfdrive/test/process_replay/diff.txt run: cat selfdrive/test/process_replay/diff.txt
- name: Print diff report - uses: actions/upload-artifact@v6
if: always()
run: cat openpilot/selfdrive/test/process_replay/diff_report.txt
- uses: actions/upload-artifact@v7
if: always() if: always()
continue-on-error: true continue-on-error: true
with: with:
name: process_replay_diff.txt name: process_replay_diff.txt
path: openpilot/selfdrive/test/process_replay/diff.txt path: selfdrive/test/process_replay/diff.txt
- name: Upload diff report
uses: actions/upload-artifact@v7
if: always() && github.event_name == 'pull_request'
continue-on-error: true
with:
name: diff_report_${{ github.event.number }}
path: openpilot/selfdrive/test/process_replay/diff_report.txt
- name: Checkout ci-artifacts - name: Checkout ci-artifacts
if: github.repository == 'commaai/openpilot' && github.ref == 'refs/heads/master' if: github.repository == 'commaai/openpilot' && github.ref == 'refs/heads/master'
uses: actions/checkout@v7 uses: actions/checkout@v4
with: with:
repository: commaai/ci-artifacts repository: commaai/ci-artifacts
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }} ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
path: ${{ github.workspace }}/ci-artifacts path: ${{ github.workspace }}/ci-artifacts
- name: Prepare refs - name: Push refs
if: github.repository == 'commaai/openpilot' && github.ref == 'refs/heads/master' if: github.repository == 'commaai/openpilot' && github.ref == 'refs/heads/master'
working-directory: ${{ github.workspace }}/ci-artifacts working-directory: ${{ github.workspace }}/ci-artifacts
run: | run: |
@@ -183,23 +177,17 @@ jobs:
git config user.email "<>" git config user.email "<>"
git fetch origin process-replay || true git fetch origin process-replay || true
git checkout process-replay 2>/dev/null || git checkout --orphan process-replay git checkout process-replay 2>/dev/null || git checkout --orphan process-replay
cp ${{ github.workspace }}/openpilot/selfdrive/test/process_replay/fakedata/*.zst . cp ${{ github.workspace }}/selfdrive/test/process_replay/fakedata/*.zst .
echo "${{ github.sha }}" > ref_commit echo "${{ github.sha }}" > ref_commit
git add . git add .
git commit -m "process-replay refs for ${{ github.repository }}@${{ github.sha }}" || echo "No changes to commit" git commit -m "process-replay refs for ${{ github.repository }}@${{ github.sha }}" || echo "No changes to commit"
- name: Push refs git push origin process-replay
if: github.repository == 'commaai/openpilot' && github.ref == 'refs/heads/master'
uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60
with:
timeout_minutes: 2
max_attempts: 3
command: cd ${{ github.workspace }}/ci-artifacts && git push origin process-replay --force
- name: Run regen - name: Run regen
if: false if: false
timeout-minutes: 4 timeout-minutes: 4
env: env:
ONNXCPU: 1 ONNXCPU: 1
run: $PYTEST openpilot/selfdrive/test/process_replay/test_regen.py run: $PYTEST selfdrive/test/process_replay/test_regen.py
simulator_driving: simulator_driving:
name: simulator driving name: simulator driving
@@ -211,16 +199,17 @@ jobs:
|| fromJSON('["ubuntu-24.04"]') }} || fromJSON('["ubuntu-24.04"]') }}
if: false # FIXME: Started to timeout recently if: false # FIXME: Started to timeout recently
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v6
with:
submodules: true
- run: ./tools/op.sh setup - run: ./tools/op.sh setup
- name: Build openpilot - name: Build openpilot
run: scons run: scons -j$(nproc)
- name: Driving test - name: Driving test
timeout-minutes: 2 timeout-minutes: 2
env: run: |
# MetaDrive renders offscreen through panda3d's EGL pipe on llvmpipe source selfdrive/test/setup_xvfb.sh
EGL_PLATFORM: surfaceless pytest -s tools/sim/tests/test_metadrive_bridge.py
run: pytest -s openpilot/tools/sim/tests/test_metadrive_bridge.py
create_ui_report: create_ui_report:
name: Create UI Report name: Create UI Report
@@ -231,21 +220,19 @@ jobs:
&& fromJSON('["namespace-profile-amd64-8x16"]') && fromJSON('["namespace-profile-amd64-8x16"]')
|| fromJSON('["ubuntu-24.04"]') }} || fromJSON('["ubuntu-24.04"]') }}
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v6
with:
submodules: true
- run: ./tools/op.sh setup - run: ./tools/op.sh setup
- name: Install raylib headless dependencies
if: ${{ !contains(runner.name, 'nsc') }}
run: sudo apt-get install -y --no-install-recommends libgles2 libegl1
- name: Build openpilot - name: Build openpilot
run: scons run: scons -j$(nproc)
- name: Create UI Report - name: Create UI Report
env:
RAYLIB_BACKEND: headless
run: | run: |
python3 openpilot/selfdrive/ui/tests/diff/replay.py source selfdrive/test/setup_xvfb.sh
python3 openpilot/selfdrive/ui/tests/diff/replay.py --big python3 selfdrive/ui/tests/diff/replay.py
python3 selfdrive/ui/tests/diff/replay.py --big
- name: Upload UI Report - name: Upload UI Report
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v6
with: with:
name: ui-report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }} name: ui-report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
path: openpilot/selfdrive/ui/tests/diff/report path: selfdrive/ui/tests/diff/report
+17 -17
View File
@@ -8,9 +8,9 @@ on:
branches: branches:
- 'master' - 'master'
paths: paths:
- 'openpilot/selfdrive/assets/**' - 'selfdrive/assets/**'
- 'openpilot/selfdrive/ui/**' - 'selfdrive/ui/**'
- 'openpilot/system/ui/**' - 'system/ui/**'
workflow_dispatch: workflow_dispatch:
env: env:
@@ -34,12 +34,12 @@ jobs:
pull-requests: write pull-requests: write
actions: read actions: read
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v6
with: with:
submodules: true submodules: true
- name: Waiting for ui generation to end - name: Waiting for ui generation to end
uses: lewagon/wait-on-check-action@1d57e2c51a58d812d2765e036a028b6bdb5a6154 uses: lewagon/wait-on-check-action@v1.3.4
with: with:
ref: ${{ env.SHA }} ref: ${{ env.SHA }}
check-name: ${{ env.UI_JOB_NAME }} check-name: ${{ env.UI_JOB_NAME }}
@@ -53,7 +53,7 @@ jobs:
echo "run_id=$(curl https://api.github.com/repos/${{ github.repository }}/commits/${{ env.SHA }}/check-runs | jq -r '.check_runs[] | select(.name == "${{ env.UI_JOB_NAME }}") | .html_url | capture("(?<number>[0-9]+)") | .number')" >> $GITHUB_OUTPUT echo "run_id=$(curl https://api.github.com/repos/${{ github.repository }}/commits/${{ env.SHA }}/check-runs | jq -r '.check_runs[] | select(.name == "${{ env.UI_JOB_NAME }}") | .html_url | capture("(?<number>[0-9]+)") | .number')" >> $GITHUB_OUTPUT
- name: Getting proposed ui - name: Getting proposed ui
uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151 uses: dawidd6/action-download-artifact@v6
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
run_id: ${{ steps.get_run_id.outputs.run_id }} run_id: ${{ steps.get_run_id.outputs.run_id }}
@@ -62,7 +62,7 @@ jobs:
path: ${{ github.workspace }}/pr_ui path: ${{ github.workspace }}/pr_ui
- name: Getting mici master ui - name: Getting mici master ui
uses: actions/checkout@v7 uses: actions/checkout@v6
with: with:
repository: sunnypilot/ci-artifacts repository: sunnypilot/ci-artifacts
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }} ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
@@ -70,7 +70,7 @@ jobs:
ref: openpilot_master_ui_mici_raylib ref: openpilot_master_ui_mici_raylib
- name: Getting big master ui - name: Getting big master ui
uses: actions/checkout@v7 uses: actions/checkout@v6
with: with:
repository: sunnypilot/ci-artifacts repository: sunnypilot/ci-artifacts
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }} ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
@@ -97,7 +97,7 @@ jobs:
done done
- name: Setup FFmpeg - name: Setup FFmpeg
uses: AnimMouse/setup-ffmpeg@178e0b4a408f06ce40d896c3cd79f50fa6f8b0c3 uses: AnimMouse/setup-ffmpeg@ae28d57dabbb148eff63170b6bf7f2b60062cbae
- name: Finding diffs - name: Finding diffs
if: github.event_name == 'pull_request_target' if: github.event_name == 'pull_request_target'
@@ -115,13 +115,13 @@ jobs:
cp "${{ github.workspace }}/master_${name}/${video}.mp4" "${{ github.workspace }}/pr_ui/${video}_master.mp4" cp "${{ github.workspace }}/master_${name}/${video}.mp4" "${{ github.workspace }}/pr_ui/${video}_master.mp4"
diff_exit_code=0 diff_exit_code=0
python3 ${{ github.workspace }}/openpilot/selfdrive/ui/tests/diff/diff.py \ python3 ${{ github.workspace }}/selfdrive/ui/tests/diff/diff.py \
"${{ github.workspace }}/pr_ui/${video}_master.mp4" \ "${{ github.workspace }}/pr_ui/${video}_master.mp4" \
"${{ github.workspace }}/pr_ui/${video}_proposed.mp4" \ "${{ github.workspace }}/pr_ui/${video}_proposed.mp4" \
"${diff_name}.html" --basedir "$baseurl" --no-open || diff_exit_code=$? "${diff_name}.html" --basedir "$baseurl" --no-open || diff_exit_code=$?
cp "${{ github.workspace }}/openpilot/selfdrive/ui/tests/diff/report/${diff_name}.html" "${{ github.workspace }}/pr_ui/" cp "${{ github.workspace }}/selfdrive/ui/tests/diff/report/${diff_name}.html" "${{ github.workspace }}/pr_ui/"
cp "${{ github.workspace }}/openpilot/selfdrive/ui/tests/diff/report/${diff_name}.mp4" "${{ github.workspace }}/pr_ui/" cp "${{ github.workspace }}/selfdrive/ui/tests/diff/report/${diff_name}.mp4" "${{ github.workspace }}/pr_ui/"
REPORT_URL="https://sunnypilot.github.io/ci-artifacts/${diff_name}_pr_${{ github.event.number }}.html" REPORT_URL="https://sunnypilot.github.io/ci-artifacts/${diff_name}_pr_${{ github.event.number }}.html"
if [ $diff_exit_code -eq 0 ]; then if [ $diff_exit_code -eq 0 ]; then
@@ -156,7 +156,7 @@ jobs:
for variant in $VARIANTS; do for variant in $VARIANTS; do
IFS=':' read -r name _ _ <<< "$variant" IFS=':' read -r name _ _ <<< "$variant"
diff_name="${name}_diff" diff_name="${name}_diff"
cp "${{ github.workspace }}/openpilot/selfdrive/ui/tests/diff/report/${diff_name}.html" "${diff_name}_pr_${{ github.event.number }}.html" cp "${{ github.workspace }}/selfdrive/ui/tests/diff/report/${diff_name}.html" "${diff_name}_pr_${{ github.event.number }}.html"
git add "${diff_name}_pr_${{ github.event.number }}.html" git add "${diff_name}_pr_${{ github.event.number }}.html"
done done
git commit -m "ui diff reports for PR #${{ github.event.number }}" || echo "No changes to commit" git commit -m "ui diff reports for PR #${{ github.event.number }}" || echo "No changes to commit"
@@ -164,12 +164,12 @@ jobs:
- name: Comment on PR - name: Comment on PR
if: github.event_name == 'pull_request_target' if: github.event_name == 'pull_request_target'
uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b uses: thollander/actions-comment-pull-request@v2
with: with:
message: | message: |
<!-- _(run_id_ui_preview **${{ github.run_id }}**)_ --> <!-- _(run_id_ui_preview **${{ github.run_id }}**)_ -->
## UI Preview ## UI Preview
${{ steps.find_diff.outputs.COMMENT }} ${{ steps.find_diff.outputs.COMMENT }}
comment-tag: run_id_ui_preview comment_tag: run_id_ui_preview
pr-number: ${{ github.event.number }} pr_number: ${{ github.event.number }}
github-token: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+33 -29
View File
@@ -13,20 +13,13 @@ venv/
a.out a.out
.hypothesis .hypothesis
.cache/ .cache/
bin/
# created at launch for TICI PYTHONPATH (PC uses editable installs via pyproject.toml) /docs_site/
/msgq
/opendbc
/rednose
/teleoprtc
/tinygrad
*.mp4 *.mp4
*.dylib *.dylib
*.DSYM *.DSYM
*.d *.d
*.pem
*.pyc *.pyc
*.pyo *.pyo
.*.swp .*.swp
@@ -46,57 +39,68 @@ bin/
*.mo *.mo
*_pyx.cpp *_pyx.cpp
*.stats *.stats
*.pkl
*.pkl*
config.json config.json
clcache
compile_commands.json compile_commands.json
compare_runtime*.html compare_runtime*.html
openpilot/selfdrive/modeld/models/tg_input_devices.json
# build artifacts selfdrive/pandad/pandad
docs_site/ cereal/services.h
openpilot/selfdrive/pandad/pandad cereal/gen
openpilot/cereal/services.h cereal/messaging/bridge
openpilot/cereal/gen selfdrive/ui/translations/tmp
openpilot/cereal/messaging/bridge selfdrive/car/tests/cars_dump
openpilot/selfdrive/ui/translations/tmp system/camerad/camerad
openpilot/selfdrive/car/tests/cars_dump system/camerad/test/ae_gray_test
openpilot/system/camerad/camerad
openpilot/system/camerad/test/ae_gray_test
.coverage* .coverage*
coverage.xml coverage.xml
htmlcov htmlcov
pandaextra
.mypy_cache/
flycheck_*
cppcheck_report.txt
comma*.sh
selfdrive/modeld/models/*.pkl*
sunnypilot/modeld*/models/*.pkl
# openpilot log files # openpilot log files
*.bz2 *.bz2
*.zst *.zst
*.rlog
build/ build/
!**/.gitkeep !**/.gitkeep
poetry.toml
Pipfile
### VisualStudioCode ### ### VisualStudioCode ###
*.vsix
.history
.ionide
.vscode/* .vscode/*
.history/
!.vscode/settings.json !.vscode/settings.json
!.vscode/tasks.json !.vscode/tasks.json
!.vscode/launch.json !.vscode/launch.json
!.vscode/extensions.json !.vscode/extensions.json
!.vscode/*.code-snippets !.vscode/*.code-snippets
# agents # Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
.claude/ .claude/
.context/ .context/
PLAN.md PLAN.md
TASK.md TASK.md
CLAUDE.md
SKILL.md
### JetBrains ### ### JetBrains ###
!.idea/customTargets.xml !.idea/customTargets.xml
+2 -2
View File
@@ -6,7 +6,7 @@
url = https://github.com/sunnypilot/opendbc.git url = https://github.com/sunnypilot/opendbc.git
[submodule "msgq"] [submodule "msgq"]
path = msgq_repo path = msgq_repo
url = https://github.com/sunnypilot/msgq.git url = https://github.com/commaai/msgq.git
[submodule "rednose_repo"] [submodule "rednose_repo"]
path = rednose_repo path = rednose_repo
url = https://github.com/commaai/rednose.git url = https://github.com/commaai/rednose.git
@@ -17,5 +17,5 @@
path = tinygrad_repo path = tinygrad_repo
url = https://github.com/sunnypilot/tinygrad.git url = https://github.com/sunnypilot/tinygrad.git
[submodule "sunnypilot/neural_network_data"] [submodule "sunnypilot/neural_network_data"]
path = openpilot/sunnypilot/neural_network_data path = sunnypilot/neural_network_data
url = https://github.com/sunnypilot/neural-network-data.git url = https://github.com/sunnypilot/neural-network-data.git
-1
View File
@@ -1 +0,0 @@
3.12.13
+4 -7
View File
@@ -6,9 +6,9 @@
"type": "pickString", "type": "pickString",
"description": "Select the process to debug", "description": "Select the process to debug",
"options": [ "options": [
"openpilot/selfdrive/controls/controlsd.py", "selfdrive/controls/controlsd.py",
"openpilot/system/timed/timed.py", "system/timed/timed.py",
"openpilot/tools/sim/run_bridge.py" "tools/sim/run_bridge.py"
] ]
}, },
{ {
@@ -16,7 +16,7 @@
"type": "pickString", "type": "pickString",
"description": "Select the process to debug", "description": "Select the process to debug",
"options": [ "options": [
"openpilot/selfdrive/ui/ui" "selfdrive/ui/ui"
] ]
}, },
{ {
@@ -52,9 +52,6 @@
"type": "lldb", "type": "lldb",
"request": "attach", "request": "attach",
"pid": "${command:pickMyProcess}", "pid": "${command:pickMyProcess}",
"sourceMap": {
".": "${workspaceFolder}/opendbc/safety"
},
"initCommands": [ "initCommands": [
"script import time; time.sleep(3)" "script import time; time.sleep(3)"
] ]
+6
View File
@@ -17,5 +17,11 @@
"**/.git", "**/.git",
"**/.venv", "**/.venv",
"**/__pycache__", "**/__pycache__",
// exclude directories that should be using the symlinked version
"common/**",
"selfdrive/**",
"system/**",
"third_party/**",
"tools/**",
] ]
} }
+1 -118
View File
@@ -1,36 +1,4 @@
sunnypilot Version 2026.002.000 (2026-06-28) sunnypilot Version 2026.001.000 (2026-03-xx)
========================
* What's Changed (sunnypilot/sunnypilot)
* ui: update gates for certain toggles by @sunnyhaibin in https://github.com/sunnypilot/sunnypilot/pull/1830
* release: ignore upstream IsReleaseBranch by @sunnyhaibin in https://github.com/sunnypilot/sunnypilot/pull/1831
* manager: disable DEVELOPMENT_ONLY reset by @sunnyhaibin in https://github.com/sunnypilot/sunnypilot/pull/1833
* sunnylink: fix max time offroad values by @nayan8teen in https://github.com/sunnypilot/sunnypilot/pull/1835
* ui: show default model name by @nayan8teen in https://github.com/sunnypilot/sunnypilot/pull/1837
* sunnylink: add CarParams fallback for brand-specific capabilities by @sunnyhaibin in https://github.com/sunnypilot/sunnypilot/pull/1839
* sunnylink SDUI: tweak DisableUpdate param for clarity by @sunnyhaibin in https://github.com/sunnypilot/sunnypilot/pull/1842
* Revert "DM: Lancia Delta HF Integrale model" by @sunnyhaibin in https://github.com/sunnypilot/sunnypilot/pull/1849
* modeld_v2: safe model validation by @Discountchubbs in https://github.com/sunnypilot/sunnypilot/pull/1855
* Revert "deprecate `carState.brake`" for Honda Gas Interceptor by @mvl-boston in https://github.com/sunnypilot/sunnypilot/pull/1860
* sunnylink: deprecate legacy params metadata by @sunnyhaibin in https://github.com/sunnypilot/sunnypilot/pull/1862
* ui: reset Enforce Torque Control and NNLC if both are enabled by @sunnyhaibin in https://github.com/sunnypilot/sunnypilot/pull/1863
* What's Changed (sunnypilot/opendbc)
* Rivian: suppress ACM hold-the-wheel warning during MADS-only lateral by @lukasloetkolben in https://github.com/sunnypilot/opendbc/pull/465
* Sync: `commaai/opendbc:master``sunnypilot/opendbc:master` by @sunnyhaibin in https://github.com/sunnypilot/opendbc/pull/479
* safety: add option to ignore frequency check for RX checks by @sunnyhaibin in https://github.com/sunnypilot/opendbc/pull/480
* Revert "deprecate carState.brake" for Honda Gas Interceptor by @mvl-boston in https://github.com/sunnypilot/opendbc/pull/481
* New Contributors (sunnypilot/sunnypilot)
* @mvl-boston made their first contribution in https://github.com/sunnypilot/sunnypilot/pull/1860
* Full Changelog: https://github.com/sunnypilot/sunnypilot/compare/v2026.001.007...v2026.002.000
************************
* Synced with commaai's openpilot (v0.11.1)
* master commit 69e2c321e49760e52f7983eaa0a5f77cb95de637 (June 02, 2026)
* New driver monitoring model
* Improved image processing pipeline for driver camera
* Improved thermal policy for comma four
* Acura MDX 2022-24 support thanks to mvl-boston!
* Rivian R1S and R1T 2025 support thanks to lukasloetkolben!
sunnypilot Version 2026.001.000 (2026-05-06)
======================== ========================
* What's Changed (sunnypilot/sunnypilot) * What's Changed (sunnypilot/sunnypilot)
* Complete rewrite of the user interface from Qt C++ to Raylib Python * Complete rewrite of the user interface from Qt C++ to Raylib Python
@@ -98,64 +66,6 @@ sunnypilot Version 2026.001.000 (2026-05-06)
* Pause Lateral Control with Blinker: Post-Blinker Delay by @CHaucke89 * Pause Lateral Control with Blinker: Post-Blinker Delay by @CHaucke89
* SCC-V: Use p97 for predicted lateral accel by @yasu-oh * SCC-V: Use p97 for predicted lateral accel by @yasu-oh
* Controls: Support for Torque Lateral Control v0 Tune by @sunnyhaibin * Controls: Support for Torque Lateral Control v0 Tune by @sunnyhaibin
* [TIZI/TICI] ui: ensure null checks for `CarParams` and `CarParamsSP` by @sunnyhaibin
* [TIZI/TICI] ui: use `vCruiseCluster` and `vEgoCluster` for SLA `preActive` by @sunnyhaibin
* Fix display of values when using use_float_scaling by @CHaucke89
* models: fix default & index "0" by @nayan8teen
* [TIZI/TICI] visuals: Improved speed limit by @angaz
* ICBM: ensure button timers update on disable to clear stale presses by @jamesmikesell
* [TIZI/TICI] ui: simplify Smart Cruise Control text rendering by @sunnyhaibin
* controlsd: fix steer_limited_by_safety not updating under MADS by @zephleggett
* soundd: trigger timeout warning during MADS lateral-only by @zephleggett
* pandad: flasher for Rivian long upgrade module by @lukasloetkolben
* modeld_v2: tinygrad transformation warp by @Discountchubbs
* tools: block `manage_sunnylinkd` in sim startup script by @sunnyhaibin
* [MICI] ui: need superclass `_render` in `HudRendererSP` by @sunnyhaibin
* [TIZI/TICI] ui: Speed Limit Assist active status by @sunnyhaibin
* ui: reimplement "Screen Off" option to Onroad Brightness by @sunnyhaibin
* ui: don't hide steering wheel when blindspot disabled by @royjr
* ui: Speed Limit Assist `preActive` improvements by @sunnyhaibin
* ui: consolidate Speed Limit Assist `preActive` status rendering by @sunnyhaibin
* [MICI] ui: Speed Limit Assist `preActive` status by @sunnyhaibin
* sunnypilot modeld: remove thneed modeld by @Discountchubbs
* modeld_v2: decouple planplus scaling from accel by @Discountchubbs
* sunnylink: Handle exceptions in `getParamsAllKeysV1` to log crashes by @devtekve
* [TIZI/TICI] ui: Developer UI cleanup by @sunnyhaibin
* [TIZI/TICI] ui: dynamic alert size by @nayan8teen
* i18n(fr): Add French translations by @didlawowo
* Toyota: Stop and Go Hack (Alpha) by @sunnyhaibin
* ui: `AlertFadeAnimator` for longitudinal-related statuses by @sunnyhaibin
* pandad: gate unsupported pandas before flashing by @sunnyhaibin
* Rivian: Flash xnor's Longitudinal Upgrade Kit prior supported panda check by @lukasloetkolben
* [TIZI/TICI] ui: add back gate steering arc behind toggle by @sunnyhaibin
* ui: gate Onroad Brightness Delay on readiness by @sunnyhaibin
* ui: add new timer options for Onroad Brightness Delay by @sunnyhaibin
* [TIZI/TICI] ui: branch switcher is always available by @sunnyhaibin
* pandad: always prioritize internal panda by @sunnyhaibin
* sunnylinkd: fetch compressed params schema by @sunnyhaibin
* sunnypilot locationd: remove unused car_ekf filter by @sunnyhaibin
* modeld_v2: update deprecated temporalPose ref by @sunnyhaibin
* NNLC: restore pre-v1 PID gains in torque extension by @mmmorks
* MADS safety: enable heartbeat and lateral controls mismatch checks by @sunnyhaibin
* [MICI] ui: models panel enhancements by @nayan8teen
* [TIZI/TICI] ui: fix unintended selection while scrolling in TreeOptionDialog by @TheSecurityDev
* tools: script for video concatenation by @Discountchubbs
* tools: profile memory usage by @Discountchubbs
* [TIZI/TICI] ui: remove per-frame param sync by @sunnyhaibin
* [MICI] ui: always offroad by @nayan8teen
* controls: always default Torque Lateral Control to v0 Tune by @sunnyhaibin
* Revert "controls: always default Torque Lateral Control to v0 Tune" by @sunnyhaibin
* Reapply "controls: always default Torque Lateral Control to v0 Tune" (#1806) by @sunnyhaibin
* [MICI] ui: add sunnylink info & connectivity check by @nayan8teen
* sunnylink: Remove unused API endpoint by @devtekve
* DM: wheel touch enforcement in MADS by @sunnyhaibin
* torque: show static override values in Dev UI & gate `useParams` on custom torque tune by @sunnyhaibin
* MADS: suppress espActive event when long is not engaged by @sunnyhaibin
* sunnylink: SDUI by @sunnyhaibin
* [MICI] ui: align upstream changes with sunnypilot settings buttons by @nayan8teen
* ui: fix cellular toggles by @AmyJeanes
* sunnylink: switch athena domain by @devtekve
* Platform List: dynamically migrate CarPlatformBundle by @sunnyhaibin
* What's Changed (sunnypilot/opendbc) * What's Changed (sunnypilot/opendbc)
* Honda: DBC for Accord 9th Generation by @mvl-boston * Honda: DBC for Accord 9th Generation by @mvl-boston
* FCA: update tire stiffness values for `RAM_HD` by @dparring * FCA: update tire stiffness values for `RAM_HD` by @dparring
@@ -174,25 +84,12 @@ sunnypilot Version 2026.001.000 (2026-05-06)
* Honda: add missing `GasInterceptor` messages to Taiwan Odyssey DBC by @mvl-boston * Honda: add missing `GasInterceptor` messages to Taiwan Odyssey DBC by @mvl-boston
* GM: remove `CHEVROLET_EQUINOX_NON_ACC_3RD_GEN` from `dashcamOnly` by @sunnyhaibin * GM: remove `CHEVROLET_EQUINOX_NON_ACC_3RD_GEN` from `dashcamOnly` by @sunnyhaibin
* GM: remove `CHEVROLET_BOLT_NON_ACC_2ND_GEN` from `dashcamOnly` by @sunnyhaibin * GM: remove `CHEVROLET_BOLT_NON_ACC_2ND_GEN` from `dashcamOnly` by @sunnyhaibin
* Hyundai Longitudinal: deprecate ramp update for dynamic tune by @Discountchubbs
* Rivian: long upgrade messages on bus 1 by @lukasloetkolben
* Toyota: Stop and Go Hack (Alpha) by @sunnyhaibin
* Toyota: gate Smart DSU behind Alpha Longitudinal by @sunnyhaibin
* Toyota: Gas Interceptor always set `standstill_req` by @sunnyhaibin
* MADS safety: dedicated `controls_allowed_lateral` by @sunnyhaibin
* Platform List: include community supported platforms by @sunnyhaibin
* New Contributors (sunnypilot/sunnypilot) * New Contributors (sunnypilot/sunnypilot)
* @TheSecurityDev made their first contribution in "ui: fix sidebar scroll in UI screenshots" * @TheSecurityDev made their first contribution in "ui: fix sidebar scroll in UI screenshots"
* @zikeji made their first contribution in "sunnylink: block remote modification of SSH key parameters" * @zikeji made their first contribution in "sunnylink: block remote modification of SSH key parameters"
* @Candy0707 made their first contribution in "[TIZI/TICI] ui: Fix misaligned turn signals and blindspot indicators with sidebar" * @Candy0707 made their first contribution in "[TIZI/TICI] ui: Fix misaligned turn signals and blindspot indicators with sidebar"
* @CHaucke89 made their first contribution in "Pause Lateral Control with Blinker: Post-Blinker Delay" * @CHaucke89 made their first contribution in "Pause Lateral Control with Blinker: Post-Blinker Delay"
* @yasu-oh made their first contribution in "SCC-V: Use p97 for predicted lateral accel" * @yasu-oh made their first contribution in "SCC-V: Use p97 for predicted lateral accel"
* @angaz made their first contribution in "[TIZI/TICI] visuals: Improved speed limit"
* @jamesmikesell made their first contribution in "ICBM: ensure button timers update on disable to clear stale presses"
* @zephleggett made their first contribution in "controlsd: fix steer_limited_by_safety not updating under MADS"
* @lukasloetkolben made their first contribution in "pandad: flasher for Rivian long upgrade module"
* @didlawowo made their first contribution in "i18n(fr): Add French translations"
* @mmmorks made their first contribution in "NNLC: restore pre-v1 PID gains in torque extension"
* New Contributors (sunnypilot/opendbc) * New Contributors (sunnypilot/opendbc)
* @AmyJeanes made their first contribution in "Tesla: Fix stock LKAS being blocked when MADS is enabled" * @AmyJeanes made their first contribution in "Tesla: Fix stock LKAS being blocked when MADS is enabled"
* @mvl-boston made their first contribution in "Honda: Update Clarity brake to renamed DBC message name" * @mvl-boston made their first contribution in "Honda: Update Clarity brake to renamed DBC message name"
@@ -202,20 +99,6 @@ sunnypilot Version 2026.001.000 (2026-05-06)
* @royjr made their first contribution in "HKG: add KIA_FORTE_2019_NON_SCC fingerprint" * @royjr made their first contribution in "HKG: add KIA_FORTE_2019_NON_SCC fingerprint"
* @ssysm made their first contribution in "Tesla: remove `TESLA_MODEL_X` from `dashcamOnly`" * @ssysm made their first contribution in "Tesla: remove `TESLA_MODEL_X` from `dashcamOnly`"
* Full Changelog: https://github.com/sunnypilot/sunnypilot/compare/v2025.002.000...v2026.001.000 * Full Changelog: https://github.com/sunnypilot/sunnypilot/compare/v2025.002.000...v2026.001.000
************************
* Synced with commaai's openpilot (v0.11.1)
* master commit c001f3c9b490a80e69539f0af6022f6e07ceb721 (April 16, 2026)
* New driver monitoring model
* Improved image processing pipeline for driver camera
* Rivian R1S and R1T 2025 support thanks to lukasloetkolben!
* New driving model #36798
* Fully trained using a learned simulator
* Improved longitudinal performance in Experimental mode
* Reduce comma four standby power usage by 77% to 52 mW
* Kia K7 2017 support thanks to royjr!
* Lexus LS 2018 support thanks to Hacheoy!
* Improved inter-process communication memory efficiency
* comma four support
sunnypilot Version 2025.002.000 (2025-11-06) sunnypilot Version 2025.002.000 (2025-11-06)
======================== ========================
+38
View File
@@ -0,0 +1,38 @@
FROM ubuntu:24.04
ENV PYTHONUNBUFFERED=1
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \
apt-get install -y --no-install-recommends sudo tzdata locales && \
rm -rf /var/lib/apt/lists/*
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
ENV LANG=en_US.UTF-8
ENV LANGUAGE=en_US:en
ENV LC_ALL=en_US.UTF-8
ENV NVIDIA_VISIBLE_DEVICES=all
ENV NVIDIA_DRIVER_CAPABILITIES=graphics,utility,compute
ARG USER=batman
ARG USER_UID=1001
RUN useradd -m -s /bin/bash -u $USER_UID $USER
RUN usermod -aG sudo $USER
RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
USER $USER
ENV OPENPILOT_PATH=/home/$USER/openpilot
RUN mkdir -p ${OPENPILOT_PATH}
WORKDIR ${OPENPILOT_PATH}
COPY --chown=$USER . ${OPENPILOT_PATH}/
ENV UV_BIN="/home/$USER/.local/bin/"
ENV VIRTUAL_ENV=${OPENPILOT_PATH}/.venv
ENV PATH="$UV_BIN:$VIRTUAL_ENV/bin:$PATH"
RUN tools/setup_dependencies.sh && \
sudo rm -rf /var/lib/apt/lists/*
USER root
RUN git config --global --add safe.directory '*'
Vendored
+29 -30
View File
@@ -12,7 +12,7 @@ def retryWithDelay(int maxRetries, int delay, Closure body) {
def device(String ip, String step_label, String cmd) { def device(String ip, String step_label, String cmd) {
withCredentials([file(credentialsId: 'id_rsa', variable: 'key_file')]) { withCredentials([file(credentialsId: 'id_rsa', variable: 'key_file')]) {
def ssh_cmd = """ def ssh_cmd = """
ssh -o ControlMaster=auto -o ControlPath=/tmp/ssh_control_%C -o ControlPersist=yes -o ConnectTimeout=5 -o ServerAliveInterval=5 -o ServerAliveCountMax=2 -o BatchMode=yes -o StrictHostKeyChecking=no -i ${key_file} 'comma@${ip}' exec /usr/bin/bash <<'END' ssh -o ConnectTimeout=5 -o ServerAliveInterval=5 -o ServerAliveCountMax=2 -o BatchMode=yes -o StrictHostKeyChecking=no -i ${key_file} 'comma@${ip}' exec /usr/bin/bash <<'END'
set -e set -e
@@ -63,8 +63,6 @@ if [ -f /data/openpilot/launch_env.sh ]; then
source /data/openpilot/launch_env.sh source /data/openpilot/launch_env.sh
fi fi
export LD_LIBRARY_PATH="\$(python -c 'import ffmpeg; print(ffmpeg.LIB_DIR)'):/usr/local/lib:\${LD_LIBRARY_PATH:-}"
ln -snf ${env.TEST_DIR} /data/pythonpath ln -snf ${env.TEST_DIR} /data/pythonpath
cd ${env.TEST_DIR} || true cd ${env.TEST_DIR} || true
@@ -95,7 +93,7 @@ def deviceStage(String stageName, String deviceType, List extra_env, def steps)
retry (3) { retry (3) {
def date = sh(script: 'date', returnStdout: true).trim(); def date = sh(script: 'date', returnStdout: true).trim();
device(device_ip, "set time", "date -s '" + date + "'") device(device_ip, "set time", "date -s '" + date + "'")
device(device_ip, "git checkout", extra + "\n" + readFile("openpilot/selfdrive/test/setup_device_ci.sh")) device(device_ip, "git checkout", extra + "\n" + readFile("selfdrive/test/setup_device_ci.sh"))
} }
steps.each { item -> steps.each { item ->
def name = item[0] def name = item[0]
@@ -168,8 +166,8 @@ node {
env.GIT_BRANCH = checkout(scm).GIT_BRANCH env.GIT_BRANCH = checkout(scm).GIT_BRANCH
env.GIT_COMMIT = checkout(scm).GIT_COMMIT env.GIT_COMMIT = checkout(scm).GIT_COMMIT
def excludeBranches = ['__nightly', 'devel', 'devel-staging', def excludeBranches = ['__nightly', 'devel', 'devel-staging', 'release3', 'release3-staging',
'release-tizi', 'release-tizi-staging', 'release-mici', 'release-mici-staging', 'testing-closet*', 'hotfix-*'] 'release-tici', 'release-tizi', 'release-tizi-staging', 'testing-closet*', 'hotfix-*']
def excludeRegex = excludeBranches.join('|').replaceAll('\\*', '.*') def excludeRegex = excludeBranches.join('|').replaceAll('\\*', '.*')
if (env.BRANCH_NAME != 'master' && !env.BRANCH_NAME.contains('__jenkins_loop_')) { if (env.BRANCH_NAME != 'master' && !env.BRANCH_NAME.contains('__jenkins_loop_')) {
@@ -181,7 +179,7 @@ node {
try { try {
if (env.BRANCH_NAME == 'devel-staging') { if (env.BRANCH_NAME == 'devel-staging') {
deviceStage("build release-tizi-staging", "tizi-needs-can", [], [ deviceStage("build release-tizi-staging", "tizi-needs-can", [], [
step("build release-tizi-staging", "RELEASE_BRANCH=release-tizi-staging,release-mici-staging $SOURCE_DIR/tools/release/build_release.sh"), step("build release-tizi-staging", "RELEASE_BRANCH=release-tizi-staging $SOURCE_DIR/release/build_release.sh"),
]) ])
} }
@@ -189,12 +187,12 @@ node {
parallel ( parallel (
'nightly': { 'nightly': {
deviceStage("build nightly", "tizi-needs-can", [], [ deviceStage("build nightly", "tizi-needs-can", [], [
step("build nightly", "RELEASE_BRANCH=nightly $SOURCE_DIR/tools/release/build_release.sh"), step("build nightly", "RELEASE_BRANCH=nightly $SOURCE_DIR/release/build_release.sh"),
]) ])
}, },
'nightly-dev': { 'nightly-dev': {
deviceStage("build nightly-dev", "tizi-needs-can", [], [ deviceStage("build nightly-dev", "tizi-needs-can", [], [
step("build nightly-dev", "PANDA_DEBUG_BUILD=1 RELEASE_BRANCH=nightly-dev $SOURCE_DIR/tools/release/build_release.sh"), step("build nightly-dev", "PANDA_DEBUG_BUILD=1 RELEASE_BRANCH=nightly-dev $SOURCE_DIR/release/build_release.sh"),
]) ])
}, },
) )
@@ -204,51 +202,52 @@ node {
parallel ( parallel (
'onroad tests': { 'onroad tests': {
deviceStage("onroad", "tizi-needs-can", ["UNSAFE=1"], [ deviceStage("onroad", "tizi-needs-can", ["UNSAFE=1"], [
step("build openpilot", "cd openpilot/system/manager && ./build.py"), step("build openpilot", "cd system/manager && ./build.py"),
step("check dirty", "tools/release/check-dirty.sh"), step("check dirty", "release/check-dirty.sh"),
step("onroad tests", "pytest openpilot/selfdrive/test/test_onroad.py -s", [timeout: 60]), step("onroad tests", "pytest selfdrive/test/test_onroad.py -s", [timeout: 60]),
]) ])
}, },
'HW + Unit Tests': { 'HW + Unit Tests': {
deviceStage("tizi-hardware", "tizi-common", ["UNSAFE=1"], [ deviceStage("tizi-hardware", "tizi-common", ["UNSAFE=1"], [
step("build", "cd openpilot/system/manager && ./build.py"), step("build", "cd system/manager && ./build.py"),
step("test power draw", "pytest -s openpilot/selfdrive/test//test_power_draw.py"), step("test power draw", "pytest -s system/hardware/tici/tests/test_power_draw.py"),
step("test encoder", "pytest openpilot/system/loggerd/tests/test_encoder.py", [diffPaths: ["openpilot/system/loggerd/"]]), step("test encoder", "LD_LIBRARY_PATH=/usr/local/lib pytest system/loggerd/tests/test_encoder.py", [diffPaths: ["system/loggerd/"]]),
step("test manager", "pytest openpilot/system/manager/test/test_manager.py"), step("test manager", "pytest system/manager/test/test_manager.py"),
]) ])
}, },
'camerad OX03C10': { 'camerad OX03C10': {
deviceStage("OX03C10", "tizi-ox03c10", ["UNSAFE=1"], [ deviceStage("OX03C10", "tizi-ox03c10", ["UNSAFE=1"], [
step("build", "cd openpilot/system/manager && ./build.py"), step("build", "cd system/manager && ./build.py"),
step("test pandad", "pytest openpilot/selfdrive/pandad/tests/test_pandad.py"), step("test pandad", "pytest selfdrive/pandad/tests/test_pandad.py", [diffPaths: ["panda", "selfdrive/pandad/"]]),
step("test camerad", "pytest openpilot/system/camerad/test/test_camerad.py", [timeout: 90]), step("test camerad", "pytest system/camerad/test/test_camerad.py", [timeout: 90]),
]) ])
}, },
'camerad OS04C10': { 'camerad OS04C10': {
deviceStage("OS04C10", "tici-os04c10", ["UNSAFE=1"], [ deviceStage("OS04C10", "tici-os04c10", ["UNSAFE=1"], [
step("build", "cd openpilot/system/manager && ./build.py"), step("build", "cd system/manager && ./build.py"),
step("test pandad", "pytest openpilot/selfdrive/pandad/tests/test_pandad.py"), step("test pandad", "pytest selfdrive/pandad/tests/test_pandad.py", [diffPaths: ["panda", "selfdrive/pandad/"]]),
step("test camerad", "pytest openpilot/system/camerad/test/test_camerad.py", [timeout: 90]), step("test camerad", "pytest system/camerad/test/test_camerad.py", [timeout: 90]),
]) ])
}, },
'sensord': { 'sensord': {
deviceStage("LSM + MMC", "tizi-lsmc", ["UNSAFE=1"], [ deviceStage("LSM + MMC", "tizi-lsmc", ["UNSAFE=1"], [
step("build", "cd openpilot/system/manager && ./build.py"), step("build", "cd system/manager && ./build.py"),
step("test sensord", "pytest openpilot/system/sensord/tests/test_sensord.py"), step("test sensord", "pytest system/sensord/tests/test_sensord.py"),
]) ])
}, },
'replay': { 'replay': {
deviceStage("model-replay", "tizi-replay", ["UNSAFE=1"], [ deviceStage("model-replay", "tizi-replay", ["UNSAFE=1"], [
step("build", "cd openpilot/system/manager && ./build.py", [diffPaths: ["openpilot/selfdrive/modeld/", "tinygrad_repo", "openpilot/selfdrive/test/process_replay/model_replay.py"]]), step("build", "cd system/manager && ./build.py", [diffPaths: ["selfdrive/modeld/", "tinygrad_repo", "selfdrive/test/process_replay/model_replay.py"]]),
step("model replay", "openpilot/selfdrive/test/process_replay/model_replay.py", [diffPaths: ["openpilot/selfdrive/modeld/", "tinygrad_repo", "openpilot/selfdrive/test/process_replay/model_replay.py"]]), step("model replay", "selfdrive/test/process_replay/model_replay.py", [diffPaths: ["selfdrive/modeld/", "tinygrad_repo", "selfdrive/test/process_replay/model_replay.py"]]),
]) ])
}, },
'tizi': { 'tizi': {
deviceStage("tizi", "tizi", ["UNSAFE=1"], [ deviceStage("tizi", "tizi", ["UNSAFE=1"], [
step("build openpilot", "cd openpilot/system/manager && ./build.py"), step("build openpilot", "cd system/manager && ./build.py"),
step("test pandad loopback", "pytest openpilot/selfdrive/pandad/tests/test_pandad_loopback.py"), step("test pandad loopback", "pytest selfdrive/pandad/tests/test_pandad_loopback.py"),
step("test pandad spi", "pytest openpilot/selfdrive/pandad/tests/test_pandad_spi.py"), step("test pandad spi", "pytest selfdrive/pandad/tests/test_pandad_spi.py"),
step("test amp", "pytest openpilot/common/hardware/tici/tests/test_amplifier.py"), step("test amp", "pytest system/hardware/tici/tests/test_amplifier.py"),
step("test qcomgpsd", "pytest system/qcomgpsd/tests/test_qcomgpsd.py", [diffPaths: ["system/qcomgpsd/"]]),
]) ])
}, },
+3 -18
View File
@@ -1,23 +1,8 @@
Version 0.11.2 (2026-06-15) Version 0.10.4 (2026-02-17)
======================== ========================
Version 0.11.1 (2026-05-18)
========================
* New driver monitoring model
* Improved image processing pipeline for driver camera
* Improved thermal policy for comma four
* Acura MDX 2022-24 support thanks to mvl-boston!
* Rivian R1S and R1T 2025 support thanks to lukasloetkolben!
Version 0.11.0 (2026-03-17)
========================
* New driving model #36798
* Fully trained using a learned simulator
* Improved longitudinal performance in Experimental mode
* Reduce comma four standby power usage by 77% to 52 mW
* Kia K7 2017 support thanks to royjr! * Kia K7 2017 support thanks to royjr!
* Lexus LS 2018 support thanks to Hacheoy! * Lexus LS 2018 support thanks to Hacheoy!
* Reduce comma four standby power usage by 77% to 52 mW
Version 0.10.3 (2025-12-17) Version 0.10.3 (2025-12-17)
======================== ========================
@@ -1104,7 +1089,7 @@ Version 0.2.1 (2016-12-14)
Version 0.2 (2016-12-12) Version 0.2 (2016-12-12)
========================= =========================
* Car/Radar abstraction layers have shipped, see openpilot/cereal/car.capnp * Car/Radar abstraction layers have shipped, see cereal/car.capnp
* controlsd has been refactored * controlsd has been refactored
* Shipped plant model and testing maneuvers * Shipped plant model and testing maneuvers
* visiond exits more gracefully now * visiond exits more gracefully now
+84 -182
View File
@@ -4,49 +4,32 @@ import sys
import sysconfig import sysconfig
import platform import platform
import shlex import shlex
import importlib
import numpy as np import numpy as np
import SCons.Errors import SCons.Errors
from SCons.Defaults import _stripixes
TICI = os.path.isfile('/TICI')
SCons.Warnings.warningAsException(True) SCons.Warnings.warningAsException(True)
Decider('MD5-timestamp') Decider('MD5-timestamp')
SetOption('num_jobs', max(1, int(os.cpu_count()/(1 if "CI" in os.environ else 2)))) SetOption('num_jobs', max(1, int(os.cpu_count()/2)))
AddOption('--asan', action='store_true', help='turn on ASAN')
AddOption('--ubsan', action='store_true', help='turn on UBSan')
AddOption('--mutation', action='store_true', help='generate mutation-ready code')
AddOption('--ccflags', action='store', type='string', default='', help='pass arbitrary flags over the command line') AddOption('--ccflags', action='store', type='string', default='', help='pass arbitrary flags over the command line')
AddOption('--verbose', action='store_true', default=False, help='show full build commands') AddOption('--verbose', action='store_true', default=False, help='show full build commands')
release = not os.path.exists(File('#.gitattributes').abspath) # file absent on release branch, see release_files.py
AddOption('--minimal', AddOption('--minimal',
action='store_false', action='store_false',
dest='extras', dest='extras',
default=(not TICI and not release), default=os.path.exists(File('#.gitattributes').abspath), # minimal by default on release branch (where there's no LFS)
help='the minimum build to run openpilot. no tests, tools, etc.') help='the minimum build to run openpilot. no tests, tools, etc.')
submodule_python_paths = [
Dir("#").abspath,
Dir("#msgq_repo").abspath,
Dir("#opendbc_repo").abspath,
Dir("#rednose_repo").abspath,
Dir("#teleoprtc_repo").abspath,
Dir("#tinygrad_repo").abspath,
]
for p in reversed(submodule_python_paths):
if p not in sys.path:
sys.path.insert(0, p)
if external_pythonpath := os.environ.get("PYTHONPATH"):
submodule_python_paths += [p for p in external_pythonpath.split(os.pathsep) if p and p not in submodule_python_paths]
# Detect platform # Detect platform
arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip() arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip()
if platform.system() == "Darwin": if platform.system() == "Darwin":
arch = "Darwin" arch = "Darwin"
elif arch == "aarch64" and TICI: elif arch == "aarch64" and os.path.isfile('/TICI'):
arch = "larch64" arch = "larch64"
assert arch in [ assert arch in [
"larch64", # linux tici arm64 "larch64", # linux tici arm64
@@ -55,76 +38,32 @@ assert arch in [
"Darwin", # macOS arm64 (x86 not supported) "Darwin", # macOS arm64 (x86 not supported)
] ]
pkg_names = ['acados', 'bzip2', 'capnproto', 'catch2', 'eigen', 'ffmpeg', 'json11', 'ncurses', 'zeromq', 'zstd'] if arch != "larch64":
pkgs = [importlib.import_module(name) for name in pkg_names] import bzip2
acados = pkgs[pkg_names.index('acados')] import capnproto
ffmpeg = pkgs[pkg_names.index('ffmpeg')] import eigen
# Shared package ships .so/.dylib; older device venvs still have static .a only. import ffmpeg as ffmpeg_pkg
# Keep static link deps (x264/z/va/drm) when the installed package is static so import libjpeg
# TICI CI works without upgrading the device venv yet. import libyuv
# TODO: drop the static fallback once device venvs have comma-deps-ffmpeg>=7.1.0.post94 import ncurses
_ffmpeg_lib_names = os.listdir(ffmpeg.LIB_DIR) if os.path.isdir(ffmpeg.LIB_DIR) else [] import openssl3
ffmpeg_shared = any( import python3_dev
n.startswith('libavcodec.so') or (n.startswith('libavcodec') and n.endswith('.dylib')) import zeromq
for n in _ffmpeg_lib_names import zstd
) pkgs = [bzip2, capnproto, eigen, ffmpeg_pkg, libjpeg, libyuv, ncurses, openssl3, zeromq, zstd]
ffmpeg_libs = ['avformat', 'avcodec', 'swresample', 'avutil'] py_include = python3_dev.INCLUDE_DIR
if not ffmpeg_shared: else:
ffmpeg_libs += ['x264', 'z'] # TODO: remove when AGNOS has our new vendor pkgs
if arch != "Darwin": pkgs = []
ffmpeg_libs += ['va', 'va-drm', 'drm'] py_include = sysconfig.get_paths()['include']
acados_include_dirs = [
acados.INCLUDE_DIR,
os.path.join(acados.INCLUDE_DIR, "blasfeo", "include"),
os.path.join(acados.INCLUDE_DIR, "hpipm", "include"),
]
# ***** enforce a whitelist of system libraries *****
# this prevents silently relying on a 3rd party package,
# e.g. apt-installed libusb. all libraries should either
# be distributed with all Linux distros and macOS, or
# vendored in commaai/dependencies.
allowed_system_libs = {
"EGL", "GLESv2", "GL",
"Qt5Charts", "Qt5Core", "Qt5Gui", "Qt5Widgets",
"dl", "drm", "gbm", "m", "pthread",
}
def _resolve_lib(env, name):
for d in env.Flatten(env.get('LIBPATH', [])):
p = Dir(str(d)).abspath
for ext in ('.a', '.so', '.dylib'):
f = File(os.path.join(p, f'lib{name}{ext}'))
if f.exists() or f.has_builder():
return name
if name in allowed_system_libs:
return name
raise SCons.Errors.UserError(f"Unexpected non-vendored library '{name}'")
def _libflags(target, source, env, for_signature):
libs = []
lp = env.subst('$LIBLITERALPREFIX')
for lib in env.Flatten(env.get('LIBS', [])):
if isinstance(lib, str):
if os.sep in lib or lib.startswith('#'):
libs.append(File(lib))
elif lib.startswith('-') or (lp and lib.startswith(lp)):
libs.append(lib)
else:
libs.append(_resolve_lib(env, lib))
else:
libs.append(lib)
return _stripixes(env['LIBLINKPREFIX'], libs, env['LIBLINKSUFFIX'],
env['LIBPREFIXES'], env['LIBSUFFIXES'], env, env['LIBLITERALPREFIX'])
env = Environment( env = Environment(
ENV={ ENV={
"PATH": os.environ['PATH'], "PATH": os.environ['PATH'],
"PYTHONPATH": os.pathsep.join(submodule_python_paths), "PYTHONPATH": Dir("#").abspath + ':' + Dir(f"#third_party/acados").abspath,
"ACADOS_SOURCE_DIR": acados.DIR, "ACADOS_SOURCE_DIR": Dir("#third_party/acados").abspath,
"ACADOS_PYTHON_INTERFACE_PATH": acados.TEMPLATE_DIR, "ACADOS_PYTHON_INTERFACE_PATH": Dir("#third_party/acados/acados_template").abspath,
"TERA_PATH": acados.TERA_PATH "TERA_PATH": Dir("#").abspath + f"/third_party/acados/{arch}/t_renderer"
}, },
CCFLAGS=[ CCFLAGS=[
"-g", "-g",
@@ -142,38 +81,41 @@ env = Environment(
CFLAGS=["-std=gnu11"], CFLAGS=["-std=gnu11"],
CXXFLAGS=["-std=c++1z"], CXXFLAGS=["-std=c++1z"],
CPPPATH=[ CPPPATH=[
"#openpilot",
"#msgq_repo", # #include "msgq/..."
"#opendbc_repo", # #include "opendbc/..."
"#rednose_repo", # #include "rednose/..."
"#rednose_repo/rednose", # #include "logger/..." (rednose package root)
"#openpilot/cereal/gen/cpp",
acados_include_dirs,
[x.INCLUDE_DIR for x in pkgs],
"#", "#",
"#msgq",
"#third_party",
"#third_party/json11",
"#third_party/linux/include",
"#third_party/acados/include",
"#third_party/acados/include/blasfeo/include",
"#third_party/acados/include/hpipm/include",
"#third_party/catch2/include",
[x.INCLUDE_DIR for x in pkgs],
], ],
LIBPATH=[ LIBPATH=[
"#openpilot/common", "#common",
"#msgq_repo", "#msgq_repo",
"#openpilot/selfdrive/pandad", "#third_party",
"#rednose_repo/rednose/helpers", "#selfdrive/pandad",
"#rednose/helpers",
f"#third_party/acados/{arch}/lib",
[x.LIB_DIR for x in pkgs], [x.LIB_DIR for x in pkgs],
], ],
RPATH=[ffmpeg.LIB_DIR] if ffmpeg_shared else [], RPATH=[],
CYTHONCFILESUFFIX=".cpp", CYTHONCFILESUFFIX=".cpp",
COMPILATIONDB_USE_ABSPATH=True, COMPILATIONDB_USE_ABSPATH=True,
REDNOSE_ROOT="#rednose_repo", REDNOSE_ROOT="#",
tools=["default", "cython", "compilation_db", "rednose_filter"], tools=["default", "cython", "compilation_db", "rednose_filter"],
toolpath=["#site_scons/site_tools", "#rednose_repo/site_scons/site_tools"], toolpath=["#site_scons/site_tools", "#rednose_repo/site_scons/site_tools"],
) )
if arch != "larch64":
env['_LIBFLAGS'] = _libflags
# Arch-specific flags and paths # Arch-specific flags and paths
if arch == "larch64": if arch == "larch64":
env["CC"] = "clang" env["CC"] = "clang"
env["CXX"] = "clang++" env["CXX"] = "clang++"
env.Append(LIBPATH=[ env.Append(LIBPATH=[
"/usr/local/lib",
"/system/vendor/lib64",
"/usr/lib/aarch64-linux-gnu", "/usr/lib/aarch64-linux-gnu",
]) ])
arch_flags = ["-D__TICI__", "-mcpu=cortex-a57", "-DQCOM2"] arch_flags = ["-D__TICI__", "-mcpu=cortex-a57", "-DQCOM2"]
@@ -185,6 +127,19 @@ elif arch == "Darwin":
]) ])
env.Append(CCFLAGS=["-DGL_SILENCE_DEPRECATION"]) env.Append(CCFLAGS=["-DGL_SILENCE_DEPRECATION"])
env.Append(CXXFLAGS=["-DGL_SILENCE_DEPRECATION"]) env.Append(CXXFLAGS=["-DGL_SILENCE_DEPRECATION"])
else:
env.Append(LIBPATH=[
"/usr/lib",
"/usr/local/lib",
])
# Sanitizers and extra CCFLAGS from CLI
if GetOption('asan'):
env.Append(CCFLAGS=["-fsanitize=address", "-fno-omit-frame-pointer"])
env.Append(LINKFLAGS=["-fsanitize=address"])
elif GetOption('ubsan'):
env.Append(CCFLAGS=["-fsanitize=undefined"])
env.Append(LINKFLAGS=["-fsanitize=undefined"])
_extra_cc = shlex.split(GetOption('ccflags') or '') _extra_cc = shlex.split(GetOption('ccflags') or '')
if _extra_cc: if _extra_cc:
@@ -210,9 +165,19 @@ if not GetOption('verbose'):
): ):
env[f"{action}COMSTR"] = f" [{short}] $TARGET" env[f"{action}COMSTR"] = f" [{short}] $TARGET"
# progress output
node_interval = 5
node_count = 0
def progress_function(node):
global node_count
node_count += node_interval
sys.stderr.write("progress: %d\n" % node_count)
if os.environ.get('SCONS_PROGRESS'):
Progress(progress_function, interval=node_interval)
# ********** Cython build environment ********** # ********** Cython build environment **********
envCython = env.Clone() envCython = env.Clone()
envCython["CPPPATH"] += [sysconfig.get_paths()['include'], np.get_include()] envCython["CPPPATH"] += [py_include, np.get_include()]
envCython["CCFLAGS"] += ["-Wno-#warnings", "-Wno-cpp", "-Wno-shadow", "-Wno-deprecated-declarations"] envCython["CCFLAGS"] += ["-Wno-#warnings", "-Wno-cpp", "-Wno-shadow", "-Wno-deprecated-declarations"]
envCython["CCFLAGS"].remove("-Werror") envCython["CCFLAGS"].remove("-Werror")
@@ -225,28 +190,18 @@ else:
np_version = SCons.Script.Value(np.__version__) np_version = SCons.Script.Value(np.__version__)
Export('envCython', 'np_version') Export('envCython', 'np_version')
Export('env', 'arch', 'acados', 'release', 'ffmpeg_libs') Export('env', 'arch')
# Setup cache dir # Setup cache dir
default_cache_dir = '/data/scons_cache' if arch == "larch64" else '/tmp/scons_cache' default_cache_dir = '/data/scons_cache' if arch == "larch64" else '/tmp/scons_cache'
cache_dir = ARGUMENTS.get('cache_dir', default_cache_dir) cache_dir = ARGUMENTS.get('cache_dir', default_cache_dir)
cache_size_limit = 4e9 if "CI" in os.environ else 2e9
CacheDir(cache_dir) CacheDir(cache_dir)
Clean(["."], cache_dir) Clean(["."], cache_dir)
def prune_cache_dir(target=None, source=None, env=None):
cache_files = sorted((os.path.join(root, f) for root, _, files in os.walk(cache_dir) for f in files), key=os.path.getmtime)
cache_size = sum(os.path.getsize(f) for f in cache_files)
for f in cache_files:
if cache_size < cache_size_limit:
break
cache_size -= os.path.getsize(f)
os.unlink(f)
# ********** start building stuff ********** # ********** start building stuff **********
# Build common module # Build common module
SConscript(['openpilot/common/SConscript']) SConscript(['common/SConscript'])
Import('_common') Import('_common')
common = [_common, 'json11', 'zmq'] common = [_common, 'json11', 'zmq']
Export('common') Export('common')
@@ -256,8 +211,9 @@ Export('common')
env_swaglog = env.Clone() env_swaglog = env.Clone()
env_swaglog['CXXFLAGS'].append('-DSWAGLOG="\\"common/swaglog.h\\""') env_swaglog['CXXFLAGS'].append('-DSWAGLOG="\\"common/swaglog.h\\""')
SConscript(['msgq_repo/SConscript'], exports={'env': env_swaglog}) SConscript(['msgq_repo/SConscript'], exports={'env': env_swaglog})
SConscript(['opendbc_repo/SConscript'], exports={'env': env_swaglog})
SConscript(['openpilot/cereal/SConscript']) SConscript(['cereal/SConscript'])
Import('socketmaster', 'msgq') Import('socketmaster', 'msgq')
messaging = [socketmaster, msgq, 'capnp', 'kj',] messaging = [socketmaster, msgq, 'capnp', 'kj',]
@@ -268,79 +224,25 @@ Export('messaging')
SConscript(['panda/SConscript']) SConscript(['panda/SConscript'])
# Build rednose library # Build rednose library
SConscript(['rednose_repo/rednose/SConscript']) SConscript(['rednose/SConscript'])
# Build system services # Build system services
SConscript([ SConscript([
'openpilot/system/loggerd/SConscript', 'system/loggerd/SConscript',
]) ])
if arch == "larch64": if arch == "larch64":
SConscript(['openpilot/system/camerad/SConscript']) SConscript(['system/camerad/SConscript'])
# Build selfdrive # Build openpilot
SConscript([ SConscript(['third_party/SConscript'])
'openpilot/selfdrive/pandad/SConscript',
'openpilot/selfdrive/controls/lib/longitudinal_mpc_lib/SConscript',
'openpilot/selfdrive/locationd/SConscript',
'openpilot/selfdrive/modeld/SConscript',
'openpilot/selfdrive/ui/SConscript',
])
SConscript(['openpilot/sunnypilot/SConscript']) SConscript(['selfdrive/SConscript'])
# Build desktop-only tools SConscript(['sunnypilot/SConscript'])
if GetOption('extras') and arch != "larch64":
SConscript([ if Dir('#tools/cabana/').exists() and arch != "larch64":
'openpilot/tools/replay/SConscript', SConscript(['tools/cabana/SConscript'])
'openpilot/tools/cabana/SConscript',
'openpilot/tools/jotpluggler/SConscript',
])
env.CompilationDatabase('compile_commands.json') env.CompilationDatabase('compile_commands.json')
# progress output
def count_scons_nodes(nodes):
seen = set()
stack = list(nodes)
while stack:
node = stack.pop().disambiguate()
if node in seen:
continue
seen.add(node)
if hasattr(node, 'has_builder') and node.has_builder():
build_product_nodes.add(node)
executor = node.get_executor()
if executor is not None:
stack += executor.get_all_prerequisites() + executor.get_all_children()
return len(seen)
progress_interval = 5
progress_count = 0
build_product_nodes = set()
progress_total = max(1, count_scons_nodes(env.arg2nodes(BUILD_TARGETS or [Dir('.')], env.fs.Entry)))
def progress_function(node):
global progress_count
if progress_count >= progress_total:
return
progress_count = min(progress_count + progress_interval, progress_total)
progress = round(100. * progress_count / progress_total, 1)
sys.stderr.write("\rBuilding: %5.1f%%" % progress if sys.stderr.isatty() else "progress: %.1f\n" % progress)
if progress == 100. and sys.stderr.isatty():
sys.stderr.write("\n")
sys.stderr.flush()
Progress(progress_function, interval=progress_interval)
AddPostAction(BUILD_TARGETS or [Dir('.')], prune_cache_dir)
def check_build_product_size(target, source, env):
limit = 50 * 1024 * 1024 # GitHub max size
for t in target:
if hasattr(t, 'isfile') and t.isfile() and (size := os.path.getsize(t.abspath)) > limit:
raise SCons.Errors.UserError(f"{t} is {size / (1024 * 1024):.1f} MiB, exceeding the {limit / (1024 * 1024):.1f} MiB limit")
if not GetOption('extras'):
AddPostAction(list(build_product_nodes), Action(check_build_product_size, None))
@@ -24,17 +24,17 @@ things are not. Read more details [here](https://capnproto.org/language.html).
### Custom forks ### Custom forks
Forks of [openpilot](https://github.com/commaai/openpilot) might want to add things to the messaging Forks of [openpilot](https://github.com/commaai/openpilot) might want to add things to the messaging
spec, however this could conflict with future changes made in mainline openpilot's cereal spec. Rebasing against mainline openpilot spec, however this could conflict with future changes made in mainline cereal/openpilot. Rebasing against mainline openpilot
then means breaking backwards-compatibility with all old logs of your fork. So we added reserved events in then means breaking backwards-compatibility with all old logs of your fork. So we added reserved events in
[custom.capnp](custom.capnp) that we will leave empty in mainline openpilot's cereal spec. **If you only modify those, you can ensure your [custom.capnp](custom.capnp) that we will leave empty in mainline cereal/openpilot. **If you only modify those, you can ensure your
fork will remain backwards-compatible with all versions of mainline openpilot and your fork.** fork will remain backwards-compatible with all versions of mainline openpilot and your fork.**
An example of compatible changes: An example of compatible changes:
```diff ```diff
diff --git a/openpilot/cereal/custom.capnp b/openpilot/cereal/custom.capnp diff --git a/cereal/custom.capnp b/cereal/custom.capnp
index 3348e859e..3365c7b98 100644 index 3348e859e..3365c7b98 100644
--- a/openpilot/cereal/custom.capnp --- a/cereal/custom.capnp
+++ b/openpilot/cereal/custom.capnp +++ b/cereal/custom.capnp
@@ -10,7 +10,11 @@ $Cxx.namespace("cereal"); @@ -10,7 +10,11 @@ $Cxx.namespace("cereal");
# DO rename the structs # DO rename the structs
# DON'T change the identifier (e.g. @0x81c2f05a394cf4af) # DON'T change the identifier (e.g. @0x81c2f05a394cf4af)
@@ -48,10 +48,10 @@ index 3348e859e..3365c7b98 100644
} }
struct CustomReserved1 @0xaedffd8f31e7b55d { struct CustomReserved1 @0xaedffd8f31e7b55d {
diff --git a/openpilot/cereal/log.capnp b/openpilot/cereal/log.capnp diff --git a/cereal/log.capnp b/cereal/log.capnp
index 1209f3fd9..b189f58b6 100644 index 1209f3fd9..b189f58b6 100644
--- a/openpilot/cereal/log.capnp --- a/cereal/log.capnp
+++ b/openpilot/cereal/log.capnp +++ b/cereal/log.capnp
@@ -2558,14 +2558,14 @@ struct Event { @@ -2558,14 +2558,14 @@ struct Event {
# DO change the name of the field # DO change the name of the field
@@ -76,7 +76,7 @@ index 1209f3fd9..b189f58b6 100644
Example Example
--- ---
```python ```python
import openpilot.cereal.messaging as messaging import cereal.messaging as messaging
# in subscriber # in subscriber
sm = messaging.SubMaster(['sensorEvents']) sm = messaging.SubMaster(['sensorEvents'])
+20
View File
@@ -0,0 +1,20 @@
Import('env', 'common', 'msgq')
cereal_dir = Dir('.')
gen_dir = Dir('gen')
# Build cereal
schema_files = ['log.capnp', 'car.capnp', 'legacy.capnp', 'custom.capnp']
env.Command([f'gen/cpp/{s}.c++' for s in schema_files] + [f'gen/cpp/{s}.h' for s in schema_files],
schema_files,
f"capnpc --src-prefix={cereal_dir.path} $SOURCES -o c++:{gen_dir.path}/cpp/")
cereal = env.Library('cereal', [f'gen/cpp/{s}.c++' for s in schema_files])
# Build messaging
services_h = env.Command(['services.h'], ['services.py'], 'python3 ' + cereal_dir.path + '/services.py > $TARGET')
env.Program('messaging/bridge', ['messaging/bridge.cc', 'messaging/msgq_to_zmq.cc', 'messaging/bridge_zmq.cc'], LIBS=[msgq, common, 'pthread'])
socketmaster = env.Library('socketmaster', ['messaging/socketmaster.cc'])
Export('cereal', 'socketmaster')
+11
View File
@@ -0,0 +1,11 @@
import os
import capnp
from importlib.resources import as_file, files
capnp.remove_import_hook()
with as_file(files("cereal")) as fspath:
CEREAL_PATH = fspath.as_posix()
log = capnp.load(os.path.join(CEREAL_PATH, "log.capnp"))
car = capnp.load(os.path.join(CEREAL_PATH, "car.capnp"))
custom = capnp.load(os.path.join(CEREAL_PATH, "custom.capnp"))
+1
View File
@@ -0,0 +1 @@
../opendbc_repo/opendbc/car/car.capnp
@@ -1,4 +1,4 @@
using Cxx = import "/include/c++.capnp"; using Cxx = import "./include/c++.capnp";
$Cxx.namespace("cereal"); $Cxx.namespace("cereal");
@0xb526ba661d550a59; @0xb526ba661d550a59;
@@ -154,7 +154,6 @@ struct ModelManagerSP @0xaedffd8f31e7b55d {
vision @2; vision @2;
policy @3; policy @3;
offPolicy @4; offPolicy @4;
onPolicy @5;
} }
} }
@@ -1,9 +1,9 @@
using Cxx = import "/include/c++.capnp"; using Cxx = import "./include/c++.capnp";
$Cxx.namespace("cereal"); $Cxx.namespace("cereal");
@0x80ef1ec4889c2a63; @0x80ef1ec4889c2a63;
# deprecated.capnp: a home for deprecated structs # legacy.capnp: a home for deprecated structs
struct LogRotate @0x9811e1f38f62f2d1 { struct LogRotate @0x9811e1f38f62f2d1 {
segmentNum @0 :Int32; segmentNum @0 :Int32;
@@ -571,207 +571,4 @@ struct LidarPts @0xe3d6685d4e9d8f7a {
pkt @4 :Data; pkt @4 :Data;
} }
struct LiveTracksDEPRECATED @0xb16f60103159415a {
trackId @0 :Int32;
dRel @1 :Float32;
yRel @2 :Float32;
vRel @3 :Float32;
aRel @4 :Float32;
timeStamp @5 :Float32;
status @6 :Float32;
currentTime @7 :Float32;
stationary @8 :Bool;
oncoming @9 :Bool;
}
struct LiveMpcData @0x92a5e332a85f32a0 {
x @0 :List(Float32);
y @1 :List(Float32);
psi @2 :List(Float32);
curvature @3 :List(Float32);
qpIterations @4 :UInt32;
calculationTime @5 :UInt64;
cost @6 :Float64;
}
struct LiveLongitudinalMpcData @0xe7e17c434f865ae2 {
xEgo @0 :List(Float32);
vEgo @1 :List(Float32);
aEgo @2 :List(Float32);
xLead @3 :List(Float32);
vLead @4 :List(Float32);
aLead @5 :List(Float32);
aLeadTau @6 :Float32; # lead accel time constant
qpIterations @7 :UInt32;
mpcId @8 :UInt32;
calculationTime @9 :UInt64;
cost @10 :Float64;
}
struct DriverStateDEPRECATED @0xb83c6cc593ed0a00 {
frameId @0 :UInt32;
modelExecutionTime @14 :Float32;
dspExecutionTime @16 :Float32;
rawPredictions @15 :Data;
faceOrientation @3 :List(Float32);
facePosition @4 :List(Float32);
faceProb @5 :Float32;
leftEyeProb @6 :Float32;
rightEyeProb @7 :Float32;
leftBlinkProb @8 :Float32;
rightBlinkProb @9 :Float32;
faceOrientationStd @11 :List(Float32);
facePositionStd @12 :List(Float32);
sunglassesProb @13 :Float32;
poorVision @17 :Float32;
partialFace @18 :Float32;
distractedPose @19 :Float32;
distractedEyes @20 :Float32;
eyesOnRoad @21 :Float32;
phoneUse @22 :Float32;
occludedProb @23 :Float32;
readyProb @24 :List(Float32);
notReadyProb @25 :List(Float32);
irPwrDEPRECATED @10 :Float32;
descriptorDEPRECATED @1 :List(Float32);
stdDEPRECATED @2 :Float32;
}
struct NavModelData @0xac3de5c437be057a {
frameId @0 :UInt32;
locationMonoTime @6 :UInt64;
modelExecutionTime @1 :Float32;
dspExecutionTime @2 :Float32;
features @3 :List(Float32);
# predicted future position
position @4 :XYData;
desirePrediction @5 :List(Float32);
# All SI units and in device frame
struct XYData @0xbe09e615b2507e26 {
x @0 :List(Float32);
y @1 :List(Float32);
xStd @2 :List(Float32);
yStd @3 :List(Float32);
}
}
struct AndroidBuildInfo @0xfe2919d5c21f426c {
board @0 :Text;
bootloader @1 :Text;
brand @2 :Text;
device @3 :Text;
display @4 :Text;
fingerprint @5 :Text;
hardware @6 :Text;
host @7 :Text;
id @8 :Text;
manufacturer @9 :Text;
model @10 :Text;
product @11 :Text;
radioVersion @12 :Text;
serial @13 :Text;
supportedAbis @14 :List(Text);
tags @15 :Text;
time @16 :Int64;
type @17 :Text;
user @18 :Text;
versionCodename @19 :Text;
versionRelease @20 :Text;
versionSdk @21 :Int32;
versionSecurityPatch @22 :Text;
}
struct AndroidSensor @0x9b513b93a887dbcd {
id @0 :Int32;
name @1 :Text;
vendor @2 :Text;
version @3 :Int32;
handle @4 :Int32;
type @5 :Int32;
maxRange @6 :Float32;
resolution @7 :Float32;
power @8 :Float32;
minDelay @9 :Int32;
fifoReservedEventCount @10 :UInt32;
fifoMaxEventCount @11 :UInt32;
stringType @12 :Text;
maxDelay @13 :Int32;
}
struct IosBuildInfo @0xd97e3b28239f5580 {
appVersion @0 :Text;
appBuild @1 :UInt32;
osVersion @2 :Text;
deviceModel @3 :Text;
}
enum FrameTypeDEPRECATED @0xa37f0d8558e193fd {
unknown @0;
neo @1;
chffrAndroid @2;
front @3;
}
struct AndroidCaptureResult @0xbcc3efbac41d2048 {
sensitivity @0 :Int32;
frameDuration @1 :Int64;
exposureTime @2 :Int64;
rollingShutterSkew @3 :UInt64;
colorCorrectionTransform @4 :List(Int32);
colorCorrectionGains @5 :List(Float32);
displayRotation @6 :Int8;
}
enum UsbPowerModeDEPRECATED @0xa8883583b32c9877 {
none @0;
client @1;
cdp @2;
dcp @3;
}
struct LateralINDIState @0x939463348632375e {
active @0 :Bool;
steeringAngleDeg @1 :Float32;
steeringRateDeg @2 :Float32;
steeringAccelDeg @3 :Float32;
rateSetPoint @4 :Float32;
accelSetPoint @5 :Float32;
accelError @6 :Float32;
delayedOutput @7 :Float32;
delta @8 :Float32;
output @9 :Float32;
saturated @10 :Bool;
steeringAngleDesiredDeg @11 :Float32;
steeringRateDesiredDeg @12 :Float32;
}
struct LateralLQRState @0x9024e2d790c82ade {
active @0 :Bool;
steeringAngleDeg @1 :Float32;
i @2 :Float32;
output @3 :Float32;
lqrOutput @4 :Float32;
saturated @5 :Bool;
steeringAngleDesiredDeg @6 :Float32;
}
struct LateralPlannerSolution @0x84caeca5a6b4acfe {
x @0 :List(Float32);
y @1 :List(Float32);
yaw @2 :List(Float32);
yawRate @3 :List(Float32);
xStd @4 :List(Float32);
yStd @5 :List(Float32);
yawStd @6 :List(Float32);
yawRateStd @7 :List(Float32);
}
struct GpsTrajectory @0x8cfeb072f5301000 {
x @0 :List(Float32);
y @1 :List(Float32);
}
File diff suppressed because it is too large Load Diff
@@ -9,8 +9,8 @@ import time
from typing import Optional, List, Union, Dict from typing import Optional, List, Union, Dict
from openpilot.cereal import log from cereal import log
from openpilot.cereal.services import SERVICE_LIST from cereal.services import SERVICE_LIST
from openpilot.common.utils import MovingAverage from openpilot.common.utils import MovingAverage
NO_TRAVERSAL_LIMIT = 2**64-1 NO_TRAVERSAL_LIMIT = 2**64-1
@@ -259,11 +259,11 @@ class PubMaster:
self.sock[s].send(dat) self.sock[s].send(dat)
def wait_for_readers_to_update(self, s: str, timeout: int, dt: float = 0.05) -> bool: def wait_for_readers_to_update(self, s: str, timeout: int, dt: float = 0.05) -> bool:
try: for _ in range(int(timeout*(1./dt))):
self.sock[s].wait_for_readers(timeout=timeout, interval=dt) if self.sock[s].all_readers_updated():
return True return True
except TimeoutError: time.sleep(dt)
return False return False
def all_readers_updated(self, s: str) -> bool: def all_readers_updated(self, s: str) -> bool:
return self.sock[s].all_readers_updated() return self.sock[s].all_readers_updated() # type: ignore
@@ -1,7 +1,7 @@
#include <cassert> #include <cassert>
#include "openpilot/cereal/messaging/msgq_to_zmq.h" #include "cereal/messaging/msgq_to_zmq.h"
#include "openpilot/cereal/services.h" #include "cereal/services.h"
#include "common/util.h" #include "common/util.h"
ExitHandler do_exit; ExitHandler do_exit;
@@ -1,4 +1,4 @@
#include "openpilot/cereal/messaging/bridge_zmq.h" #include "cereal/messaging/bridge_zmq.h"
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
@@ -8,7 +8,7 @@
#include <capnp/serialize.h> #include <capnp/serialize.h>
#include "openpilot/cereal/gen/cpp/log.capnp.h" #include "cereal/gen/cpp/log.capnp.h"
#include "common/timing.h" #include "common/timing.h"
#include "msgq/ipc.h" #include "msgq/ipc.h"
@@ -1,8 +1,8 @@
#include "openpilot/cereal/messaging/msgq_to_zmq.h" #include "cereal/messaging/msgq_to_zmq.h"
#include <cassert> #include <cassert>
#include "openpilot/cereal/services.h" #include "cereal/services.h"
#include "common/util.h" #include "common/util.h"
extern ExitHandler do_exit; extern ExitHandler do_exit;
@@ -8,7 +8,7 @@
#include <vector> #include <vector>
#include "msgq/impl_msgq.h" #include "msgq/impl_msgq.h"
#include "openpilot/cereal/messaging/bridge_zmq.h" #include "cereal/messaging/bridge_zmq.h"
class MsgqToZmq { class MsgqToZmq {
public: public:
@@ -3,8 +3,8 @@
#include <string> #include <string>
#include <mutex> #include <mutex>
#include "openpilot/cereal/services.h" #include "cereal/services.h"
#include "openpilot/cereal/messaging/messaging.h" #include "cereal/messaging/messaging.h"
const bool SIMULATION = (getenv("SIMULATION") != nullptr) && (std::string(getenv("SIMULATION")) == "1"); const bool SIMULATION = (getenv("SIMULATION") != nullptr) && (std::string(getenv("SIMULATION")) == "1");
@@ -8,10 +8,9 @@ import time
from openpilot.common.parameterized import parameterized from openpilot.common.parameterized import parameterized
import pytest import pytest
from openpilot.cereal import log from cereal import log, car
from opendbc.car.structs import car import cereal.messaging as messaging
import openpilot.cereal.messaging as messaging from cereal.services import SERVICE_LIST
from openpilot.cereal.services import SERVICE_LIST
events = [evt for evt in log.Event.schema.union_fields if evt in SERVICE_LIST.keys()] events = [evt for evt in log.Event.schema.union_fields if evt in SERVICE_LIST.keys()]
@@ -31,7 +30,7 @@ def zmq_sleep(t=1):
# TODO: this should take any capnp struct and returrn a msg with random populated data # TODO: this should take any capnp struct and returrn a msg with random populated data
def random_carstate(): def random_carstate():
fields = ["vEgo", "aEgo", "steeringTorque", "steeringAngleDeg"] fields = ["vEgo", "aEgo", "brake", "steeringAngleDeg"]
msg = messaging.new_message("carState") msg = messaging.new_message("carState")
cs = msg.carState cs = msg.carState
for f in fields: for f in fields:
@@ -2,11 +2,11 @@ import random
import time import time
from typing import Sized, cast from typing import Sized, cast
import openpilot.cereal.messaging as messaging import cereal.messaging as messaging
from openpilot.cereal.messaging.tests.test_messaging import events, random_sock, random_socks, \ from cereal.messaging.tests.test_messaging import events, random_sock, random_socks, \
random_bytes, random_carstate, assert_carstate, \ random_bytes, random_carstate, assert_carstate, \
zmq_sleep zmq_sleep
from openpilot.cereal.services import SERVICE_LIST from cereal.services import SERVICE_LIST
class TestSubMaster: class TestSubMaster:
@@ -1,10 +1,10 @@
import subprocess import os
import tempfile import tempfile
from typing import Dict
from openpilot.common.parameterized import parameterized from openpilot.common.parameterized import parameterized
import openpilot.cereal.services as services import cereal.services as services
from openpilot.cereal.services import SERVICE_LIST from cereal.services import SERVICE_LIST
class TestServices: class TestServices:
@@ -17,5 +17,5 @@ class TestServices:
def test_generated_header(self): def test_generated_header(self):
with tempfile.NamedTemporaryFile(suffix=".h") as f: with tempfile.NamedTemporaryFile(suffix=".h") as f:
ret = subprocess.run(f"python3 {services.__file__} > {f.name} && clang++ {f.name} -std=c++11", shell=True).returncode ret = os.system(f"python3 {services.__file__} > {f.name} && clang++ {f.name} -std=c++11")
assert ret == 0, "generated services header is not valid C" assert ret == 0, "generated services header is not valid C"
+222
View File
@@ -0,0 +1,222 @@
#!/usr/bin/env python3
import argparse
import sys
from typing import Any, List, Tuple
DEBUG = False
def print_debug(string: str) -> None:
if DEBUG:
print(string)
def create_schema_instance(struct: Any, prop: Tuple[str, Any]) -> Any:
"""
Create a new instance of a schema type, handling different field types.
Args:
struct: The Cap'n Proto schema structure
prop: A tuple containing the field name and field metadata
Returns:
A new initialized schema instance
"""
struct_instance = struct.new_message()
field_name, field_metadata = prop
try:
field_type = field_metadata.proto.slot.type.which()
# Initialize different types of fields
if field_type in ('list', 'text', 'data'):
struct_instance.init(field_name, 1)
print_debug(f"Initialized list/text/data field: {field_name}")
elif field_type in ('struct', 'object'):
struct_instance.init(field_name)
print_debug(f"Initialized struct/object field: {field_name}")
return struct_instance
except Exception as e:
print(f"Error creating instance for {field_name}: {e}")
return None
def get_schema_fields(schema_struct: Any) -> List[Tuple[str, Any]]:
"""
Retrieve all fields from a given schema structure.
Args:
schema_struct: The Cap'n Proto schema structure
Returns:
A list of field names and their metadata
"""
try:
# Get all fields from the schema
schema_fields = list(schema_struct.schema.fields.items())
print_debug("Discovered schema fields:")
for field_name, field_metadata in schema_fields:
print_debug(f"- {field_name}")
return schema_fields
except Exception as e:
print(f"Error retrieving schema fields: {e}")
return []
def generate_schema_instances(schema_struct: Any) -> List[Any]:
"""
Generate instances for all fields in a given schema.
Args:
schema_struct: The Cap'n Proto schema structure
Returns:
A list of schema instances
"""
schema_fields = get_schema_fields(schema_struct)
instances = []
for field_prop in schema_fields:
try:
instance = create_schema_instance(schema_struct, field_prop)
if instance is not None:
instances.append(instance)
except Exception as e:
print(f"Skipping field due to error: {e}")
print(f"Generated {len(instances)} schema instances")
return instances
def persist_instances(instances: List[Any], filename: str) -> None:
"""
Write schema instances to a binary file.
Args:
instances: List of schema instances
filename: Output file path
"""
try:
with open(filename, 'wb') as f:
for instance in instances:
f.write(instance.to_bytes())
print(f"Successfully wrote {len(instances)} instances to {filename}")
except Exception as e:
print(f"Error persisting instances: {e}")
sys.exit(1)
def read_instances(filename: str, schema_type: Any) -> List[Any]:
"""
Read schema instances from a binary file.
Args:
filename: Input file path
schema_type: The schema type to use for reading
Returns:
A list of read schema instances
"""
try:
with open(filename, 'rb') as f:
data = f.read()
instances = list(schema_type.read_multiple_bytes(data))
print(f"Read {len(instances)} instances from {filename}")
return instances
except Exception as e:
print(f"Error reading instances: {e}")
sys.exit(1)
def compare_schemas(original_instances: List[Any], read_instances: List[Any]) -> bool:
"""
Compare original and read-back instances to detect potential breaking changes.
Args:
original_instances: List of originally generated instances
read_instances: List of instances read back from file
Returns:
Boolean indicating whether schemas appear compatible
"""
if len(original_instances) != len(read_instances):
print("❌ Schema Compatibility Warning: Instance count mismatch")
return False
compatible = True
for struct in read_instances:
try:
getattr(struct, struct.which()) # Attempting to access the field to validate readability
except Exception as e:
print(f"❌ Structural change detected: {struct.which()} is not readable.\nFull error: {e}")
compatible = False
return compatible
def main():
"""
CLI entry point for schema compatibility testing.
"""
# Setup argument parser
parser = argparse.ArgumentParser(
description='Cap\'n Proto Schema Compatibility Testing Tool',
epilog='Test schema compatibility by generating and reading back instances.'
)
# Add mutually exclusive group for generation or reading mode
mode_group = parser.add_mutually_exclusive_group(required=True)
mode_group.add_argument('-g', '--generate', action='store_true',
help='Generate schema instances')
mode_group.add_argument('-r', '--read', action='store_true',
help='Read and validate schema instances')
# Common arguments
parser.add_argument('-f', '--file',
default='schema_instances.bin',
help='Output/input binary file (default: schema_instances.bin)')
# Parse arguments
args = parser.parse_args()
# Import the schema dynamically
try:
from cereal import log
schema_type = log.Event
except ImportError:
print("Error: Unable to import schema. Ensure 'cereal' is installed.")
sys.exit(1)
# Execute based on mode
if args.generate:
print("🔧 Generating Schema Instances")
instances = generate_schema_instances(schema_type)
persist_instances(instances, args.file)
print("✅ Instance generation complete")
elif args.read:
print("🔍 Reading and Validating Schema Instances")
generated_instances = generate_schema_instances(schema_type)
read_back_instances = read_instances(args.file, schema_type)
# Compare schemas
if compare_schemas(generated_instances, read_back_instances):
print("✅ Schema Compatibility: No breaking changes detected")
sys.exit(0)
else:
print("❌ Potential Schema Breaking Changes Detected")
sys.exit(1)
if __name__ == "__main__":
main()
@@ -24,7 +24,10 @@ _services: dict[str, tuple] = {
# note: the "EncodeIdx" packets will still be in the log # note: the "EncodeIdx" packets will still be in the log
"gyroscope": (True, 104., 104), "gyroscope": (True, 104., 104),
"accelerometer": (True, 104., 104), "accelerometer": (True, 104., 104),
"magnetometer": (True, 25.),
"lightSensor": (True, 100., 100),
"temperatureSensor": (True, 2., 200), "temperatureSensor": (True, 2., 200),
"gpsNMEA": (True, 9.),
"deviceState": (True, 2., 1), "deviceState": (True, 2., 1),
"touch": (True, 20., 1), "touch": (True, 20., 1),
"can": (True, 100., 2053, QueueSize.BIG), # decimation gives ~3 msgs in a full segment "can": (True, 100., 2053, QueueSize.BIG), # decimation gives ~3 msgs in a full segment
@@ -36,23 +39,23 @@ _services: dict[str, tuple] = {
"roadEncodeIdx": (False, 20., 1), "roadEncodeIdx": (False, 20., 1),
"liveTracks": (True, 20.), "liveTracks": (True, 20.),
"sendcan": (True, 100., 139, QueueSize.MEDIUM), "sendcan": (True, 100., 139, QueueSize.MEDIUM),
"logMessage": (True, 0., None, QueueSize.BIG), "logMessage": (True, 0.),
"errorLogMessage": (True, 0., 1, QueueSize.BIG), "errorLogMessage": (True, 0., 1),
"liveCalibration": (True, 4., 4), "liveCalibration": (True, 4., 4),
"liveTorqueParameters": (True, 4., 1), "liveTorqueParameters": (True, 4., 1),
"liveDelay": (True, 4., 1), "liveDelay": (True, 4., 1),
"operatingSystemLog": (True, 0.), "androidLog": (True, 0.),
"carState": (True, 100., 10), "carState": (True, 100., 10),
"carControl": (True, 100., 10), "carControl": (True, 100., 10),
"carOutput": (True, 100., 10), "carOutput": (True, 100., 10),
"longitudinalPlan": (True, 20., 10), "longitudinalPlan": (True, 20., 10),
"lateralManeuverPlan": (True, 20.),
"driverAssistance": (True, 20., 20), "driverAssistance": (True, 20., 20),
"procLog": (True, 0.5, 15, QueueSize.BIG), "procLog": (True, 0.5, 15, QueueSize.BIG),
"gpsLocationExternal": (True, 10., 10), "gpsLocationExternal": (True, 10., 10),
"gpsLocation": (True, 1., 1), "gpsLocation": (True, 1., 1),
"ubloxGnss": (True, 10.), "ubloxGnss": (True, 10.),
"qcomGnss": (True, 2.), "qcomGnss": (True, 2.),
"gnssMeasurements": (True, 10., 10),
"clocks": (True, 0.1, 1), "clocks": (True, 0.1, 1),
"ubloxRaw": (True, 20.), "ubloxRaw": (True, 20.),
"livePose": (True, 20., 4), "livePose": (True, 20., 4),
@@ -71,6 +74,10 @@ _services: dict[str, tuple] = {
"drivingModelData": (True, 20., 10), "drivingModelData": (True, 20., 10),
"modelV2": (True, 20., None, QueueSize.BIG), "modelV2": (True, 20., None, QueueSize.BIG),
"managerState": (True, 2., 1), "managerState": (True, 2., 1),
"uploaderState": (True, 0., 1),
"navInstruction": (True, 1., 10),
"navRoute": (True, 0.),
"navThumbnail": (True, 0.),
"qRoadEncodeIdx": (False, 20.), "qRoadEncodeIdx": (False, 20.),
"userBookmark": (True, 0., 1), "userBookmark": (True, 0., 1),
"soundPressure": (True, 10., 10), "soundPressure": (True, 10., 10),
@@ -106,6 +113,8 @@ _services: dict[str, tuple] = {
"livestreamRoadEncodeData": (False, 20., None, QueueSize.MEDIUM), "livestreamRoadEncodeData": (False, 20., None, QueueSize.MEDIUM),
"livestreamDriverEncodeData": (False, 20., None, QueueSize.MEDIUM), "livestreamDriverEncodeData": (False, 20., None, QueueSize.MEDIUM),
"customReservedRawData0": (True, 0.), "customReservedRawData0": (True, 0.),
"customReservedRawData1": (True, 0.),
"customReservedRawData2": (True, 0.),
} }
SERVICE_LIST = {name: Service(*vals) for SERVICE_LIST = {name: Service(*vals) for
idx, (name, vals) in enumerate(_services.items())} idx, (name, vals) in enumerate(_services.items())}
+1
View File
@@ -0,0 +1 @@
*.cpp
@@ -1,11 +1,10 @@
Import('env', 'envCython') Import('env', 'envCython', 'arch')
common_libs = [ common_libs = [
'params.cc', 'params.cc',
'swaglog.cc', 'swaglog.cc',
'util.cc', 'util.cc',
'ratekeeper.cc', 'ratekeeper.cc',
'yuv.cc',
] ]
_common = env.Library('common', common_libs, LIBS="json11") _common = env.Library('common', common_libs, LIBS="json11")
@@ -13,7 +12,7 @@ Export('_common')
if GetOption('extras'): if GetOption('extras'):
env.Program('tests/test_common', env.Program('tests/test_common',
['tests/test_runner.cc', 'tests/test_util.cc', 'tests/test_swaglog.cc'], ['tests/test_runner.cc', 'tests/test_params.cc', 'tests/test_util.cc', 'tests/test_swaglog.cc'],
LIBS=[_common, 'json11', 'zmq', 'pthread']) LIBS=[_common, 'json11', 'zmq', 'pthread'])
# Cython bindings # Cython bindings
@@ -3,8 +3,8 @@ import os
import requests import requests
import unicodedata import unicodedata
from datetime import datetime, timedelta, UTC from datetime import datetime, timedelta, UTC
from openpilot.common.hardware.hw import Paths from openpilot.system.hardware.hw import Paths
from openpilot.common.version import get_version from openpilot.system.version import get_version
# name: jwt signature algorithm # name: jwt signature algorithm
KEYS = {"id_rsa": "RS256", KEYS = {"id_rsa": "RS256",
@@ -1,4 +1,4 @@
import os import os
BASEDIR = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../..")) BASEDIR = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../"))
+37
View File
@@ -0,0 +1,37 @@
import math
import os
from pathlib import Path
CHUNK_SIZE = 45 * 1024 * 1024 # 45MB, under GitHub's 50MB limit
def get_chunk_name(name, idx, num_chunks):
return f"{name}.chunk{idx+1:02d}of{num_chunks:02d}"
def get_manifest_path(name):
return f"{name}.chunkmanifest"
def get_chunk_paths(path, file_size):
num_chunks = math.ceil(file_size / CHUNK_SIZE)
return [get_manifest_path(path)] + [get_chunk_name(path, i, num_chunks) for i in range(num_chunks)]
def chunk_file(path, targets):
manifest_path, *chunk_paths = targets
with open(path, 'rb') as f:
data = f.read()
actual_num_chunks = max(1, math.ceil(len(data) / CHUNK_SIZE))
assert len(chunk_paths) >= actual_num_chunks, f"Allowed {len(chunk_paths)} chunks but needs at least {actual_num_chunks}, for path {path}"
for i, chunk_path in enumerate(chunk_paths):
with open(chunk_path, 'wb') as f:
f.write(data[i * CHUNK_SIZE:(i + 1) * CHUNK_SIZE])
Path(manifest_path).write_text(str(len(chunk_paths)))
os.remove(path)
def read_file_chunked(path):
manifest_path = get_manifest_path(path)
if os.path.isfile(manifest_path):
num_chunks = int(Path(manifest_path).read_text().strip())
return b''.join(Path(get_chunk_name(path, i, num_chunks)).read_bytes() for i in range(num_chunks))
if os.path.isfile(path):
return Path(path).read_bytes()
raise FileNotFoundError(path)
@@ -28,7 +28,7 @@ class BounceFilter(FirstOrderFilter):
scale = self.dt / (1.0 / 60.0) # tuned at 60 fps scale = self.dt / (1.0 / 60.0) # tuned at 60 fps
self.velocity.x += (x - self.x) * self.bounce * scale * self.dt self.velocity.x += (x - self.x) * self.bounce * scale * self.dt
self.velocity.update(0.0) self.velocity.update(0.0)
if abs(self.velocity.x) < 1e-3: if abs(self.velocity.x) < 1e-5:
self.velocity.x = 0.0 self.velocity.x = 0.0
self.x += self.velocity.x self.x += self.velocity.x
return self.x return self.x
@@ -1,13 +1,13 @@
""" """
Utilities for generating mock messages for testing. Utilities for generating mock messages for testing.
example in openpilot/common/tests/test_mock.py example in common/tests/test_mock.py
""" """
import functools import functools
import threading import threading
from openpilot.cereal.messaging import PubMaster from cereal.messaging import PubMaster
from openpilot.cereal.services import SERVICE_LIST from cereal.services import SERVICE_LIST
from openpilot.common.mock.generators import generate_livePose from openpilot.common.mock.generators import generate_livePose
from openpilot.common.realtime import Ratekeeper from openpilot.common.realtime import Ratekeeper
@@ -1,4 +1,4 @@
from openpilot.cereal import messaging from cereal import messaging
def generate_livePose(): def generate_livePose():
+1
View File
@@ -0,0 +1 @@
#define DEFAULT_MODEL "CD210 (Default)"
@@ -1,13 +1,8 @@
import re
import sys import sys
import pytest import pytest
import inspect import inspect
def _to_safe_name(s):
return re.sub(r"[^a-zA-Z0-9_]+", "_", str(s)).strip("_")
class parameterized: class parameterized:
@staticmethod @staticmethod
def expand(cases): def expand(cases):
@@ -39,9 +34,7 @@ def parameterized_class(attrs, input_list=None):
def decorator(cls): def decorator(cls):
globs = sys._getframe(1).f_globals globs = sys._getframe(1).f_globals
for i, params in enumerate(params_list): for i, params in enumerate(params_list):
# append sanitized string param values so pytest -k can filter by them name = f"{cls.__name__}_{i}"
suffix = "_".join(filter(None, (_to_safe_name(v) for v in params.values() if isinstance(v, str))))
name = f"{cls.__name__}_{i}" + (f"_{suffix}" if suffix else "")
new_cls = type(name, (cls,), dict(params)) new_cls = type(name, (cls,), dict(params))
new_cls.__module__ = cls.__module__ new_cls.__module__ = cls.__module__
new_cls.__test__ = True # override inherited False so pytest collects this subclass new_cls.__test__ = True # override inherited False so pytest collects this subclass
@@ -12,7 +12,7 @@
#include "common/queue.h" #include "common/queue.h"
#include "common/swaglog.h" #include "common/swaglog.h"
#include "common/util.h" #include "common/util.h"
#include "common/hardware/hw.h" #include "system/hardware/hw.h"
namespace { namespace {
@@ -14,6 +14,6 @@ if __name__ == "__main__":
if len(sys.argv) == 3: if len(sys.argv) == 3:
val = sys.argv[2] val = sys.argv[2]
print(f"SET: {key} = {val}") print(f"SET: {key} = {val}")
params.put(key, val, block=True) params.put(key, val)
elif len(sys.argv) == 2: elif len(sys.argv) == 2:
print(f"GET: {key} = {params.get(key)}") print(f"GET: {key} = {params.get(key)}")
@@ -3,7 +3,7 @@
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include "openpilot/cereal/gen/cpp/log.capnp.h" #include "cereal/gen/cpp/log.capnp.h"
inline static std::unordered_map<std::string, ParamKeyAttributes> keys = { inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"AccessToken", {CLEAR_ON_MANAGER_START | DONT_LOG, STRING}}, {"AccessToken", {CLEAR_ON_MANAGER_START | DONT_LOG, STRING}},
@@ -58,11 +58,12 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"IsDriverViewEnabled", {CLEAR_ON_MANAGER_START, BOOL}}, {"IsDriverViewEnabled", {CLEAR_ON_MANAGER_START, BOOL}},
{"IsEngaged", {PERSISTENT, BOOL}}, {"IsEngaged", {PERSISTENT, BOOL}},
{"IsLdwEnabled", {PERSISTENT | BACKUP, BOOL}}, {"IsLdwEnabled", {PERSISTENT | BACKUP, BOOL}},
{"IsLiveStreaming", {CLEAR_ON_MANAGER_START, BOOL}},
{"IsMetric", {PERSISTENT | BACKUP, BOOL}}, {"IsMetric", {PERSISTENT | BACKUP, BOOL}},
{"IsOffroad", {CLEAR_ON_MANAGER_START, BOOL}}, {"IsOffroad", {CLEAR_ON_MANAGER_START, BOOL}},
{"IsOnroad", {PERSISTENT, BOOL}},
{"IsRhdDetected", {PERSISTENT, BOOL}}, {"IsRhdDetected", {PERSISTENT, BOOL}},
{"IsReleaseBranch", {CLEAR_ON_MANAGER_START, BOOL}}, {"IsReleaseBranch", {CLEAR_ON_MANAGER_START, BOOL}},
{"IsTakingSnapshot", {CLEAR_ON_MANAGER_START, BOOL}},
{"IsTestedBranch", {CLEAR_ON_MANAGER_START, BOOL}}, {"IsTestedBranch", {CLEAR_ON_MANAGER_START, BOOL}},
{"JoystickDebugMode", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}}, {"JoystickDebugMode", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
{"LanguageSetting", {PERSISTENT | BACKUP, STRING, "en"}}, {"LanguageSetting", {PERSISTENT | BACKUP, STRING, "en"}},
@@ -79,11 +80,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"LiveDelay", {PERSISTENT | BACKUP, BYTES}}, {"LiveDelay", {PERSISTENT | BACKUP, BYTES}},
{"LiveParameters", {PERSISTENT, JSON}}, {"LiveParameters", {PERSISTENT, JSON}},
{"LiveParametersV2", {PERSISTENT, BYTES}}, {"LiveParametersV2", {PERSISTENT, BYTES}},
{"LivestreamEncoderBitrate", {CLEAR_ON_MANAGER_START | DONT_LOG, INT}},
{"LivestreamRequestKeyframe", {CLEAR_ON_MANAGER_START | DONT_LOG, BOOL}},
{"LiveTorqueParameters", {PERSISTENT | DONT_LOG, BYTES}}, {"LiveTorqueParameters", {PERSISTENT | DONT_LOG, BYTES}},
{"LocationFilterInitialState", {PERSISTENT, BYTES}}, {"LocationFilterInitialState", {PERSISTENT, BYTES}},
{"LateralManeuverMode", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
{"LongitudinalManeuverMode", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}}, {"LongitudinalManeuverMode", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
{"LongitudinalPersonality", {PERSISTENT | BACKUP, INT, std::to_string(static_cast<int>(cereal::LongitudinalPersonality::STANDARD))}}, {"LongitudinalPersonality", {PERSISTENT | BACKUP, INT, std::to_string(static_cast<int>(cereal::LongitudinalPersonality::STANDARD))}},
{"NetworkMetered", {PERSISTENT | BACKUP, BOOL}}, {"NetworkMetered", {PERSISTENT | BACKUP, BOOL}},
@@ -93,6 +91,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"Offroad_ConnectivityNeeded", {CLEAR_ON_MANAGER_START, JSON}}, {"Offroad_ConnectivityNeeded", {CLEAR_ON_MANAGER_START, JSON}},
{"Offroad_ConnectivityNeededPrompt", {CLEAR_ON_MANAGER_START, JSON}}, {"Offroad_ConnectivityNeededPrompt", {CLEAR_ON_MANAGER_START, JSON}},
{"Offroad_ExcessiveActuation", {PERSISTENT, JSON}}, {"Offroad_ExcessiveActuation", {PERSISTENT, JSON}},
{"Offroad_IsTakingSnapshot", {CLEAR_ON_MANAGER_START, JSON}},
{"Offroad_NeosUpdate", {CLEAR_ON_MANAGER_START, JSON}}, {"Offroad_NeosUpdate", {CLEAR_ON_MANAGER_START, JSON}},
{"Offroad_NoFirmware", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}}, {"Offroad_NoFirmware", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}},
{"Offroad_Recalibration", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}}, {"Offroad_Recalibration", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}},
@@ -103,6 +102,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"OnroadCycleRequested", {CLEAR_ON_MANAGER_START, BOOL}}, {"OnroadCycleRequested", {CLEAR_ON_MANAGER_START, BOOL}},
{"OpenpilotEnabledToggle", {PERSISTENT | BACKUP, BOOL, "1"}}, {"OpenpilotEnabledToggle", {PERSISTENT | BACKUP, BOOL, "1"}},
{"PandaHeartbeatLost", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}}, {"PandaHeartbeatLost", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
{"PandaSomResetTriggered", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
{"PandaSignatures", {CLEAR_ON_MANAGER_START, BYTES}},
{"PrimeType", {PERSISTENT, INT}}, {"PrimeType", {PERSISTENT, INT}},
{"RecordAudio", {PERSISTENT | BACKUP, BOOL}}, {"RecordAudio", {PERSISTENT | BACKUP, BOOL}},
{"RecordAudioFeedback", {PERSISTENT | BACKUP, BOOL, "0"}}, {"RecordAudioFeedback", {PERSISTENT | BACKUP, BOOL, "0"}},
@@ -130,8 +131,6 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"UpdaterLastFetchTime", {PERSISTENT, TIME}}, {"UpdaterLastFetchTime", {PERSISTENT, TIME}},
{"UptimeOffroad", {PERSISTENT, FLOAT, "0.0"}}, {"UptimeOffroad", {PERSISTENT, FLOAT, "0.0"}},
{"UptimeOnroad", {PERSISTENT, FLOAT, "0.0"}}, {"UptimeOnroad", {PERSISTENT, FLOAT, "0.0"}},
{"UsbGpuPresent", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
{"UsbGpuCompiled", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
{"Version", {PERSISTENT, STRING}}, {"Version", {PERSISTENT, STRING}},
// --- sunnypilot params --- // // --- sunnypilot params --- //
@@ -173,7 +172,6 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"OnroadScreenOffBrightness", {PERSISTENT | BACKUP, INT, "0"}}, {"OnroadScreenOffBrightness", {PERSISTENT | BACKUP, INT, "0"}},
{"OnroadScreenOffBrightnessMigrated", {PERSISTENT | BACKUP, STRING, "0.0"}}, {"OnroadScreenOffBrightnessMigrated", {PERSISTENT | BACKUP, STRING, "0.0"}},
{"OnroadScreenOffTimer", {PERSISTENT | BACKUP, INT, "15"}}, {"OnroadScreenOffTimer", {PERSISTENT | BACKUP, INT, "15"}},
{"OnroadScreenOffTimerMigrated", {PERSISTENT | BACKUP, STRING, "0.0"}},
{"OnroadUploads", {PERSISTENT | BACKUP, BOOL, "1"}}, {"OnroadUploads", {PERSISTENT | BACKUP, BOOL, "1"}},
{"QuickBootToggle", {PERSISTENT | BACKUP, BOOL, "0"}}, {"QuickBootToggle", {PERSISTENT | BACKUP, BOOL, "0"}},
{"QuietMode", {PERSISTENT | BACKUP, BOOL, "0"}}, {"QuietMode", {PERSISTENT | BACKUP, BOOL, "0"}},
@@ -204,7 +202,6 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
// sunnylink params // sunnylink params
{"EnableSunnylinkUploader", {PERSISTENT | BACKUP, BOOL}}, {"EnableSunnylinkUploader", {PERSISTENT | BACKUP, BOOL}},
{"LastSunnylinkPingTime", {CLEAR_ON_MANAGER_START, INT}}, {"LastSunnylinkPingTime", {CLEAR_ON_MANAGER_START, INT}},
{"ParamsVersion", {PERSISTENT, INT}},
{"SunnylinkCache_Roles", {PERSISTENT, STRING}}, {"SunnylinkCache_Roles", {PERSISTENT, STRING}},
{"SunnylinkCache_Users", {PERSISTENT, STRING}}, {"SunnylinkCache_Users", {PERSISTENT, STRING}},
{"SunnylinkDongleId", {PERSISTENT, STRING}}, {"SunnylinkDongleId", {PERSISTENT, STRING}},
@@ -273,7 +270,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"EnforceTorqueControl", {PERSISTENT | BACKUP, BOOL}}, {"EnforceTorqueControl", {PERSISTENT | BACKUP, BOOL}},
{"LiveTorqueParamsToggle", {PERSISTENT | BACKUP , BOOL}}, {"LiveTorqueParamsToggle", {PERSISTENT | BACKUP , BOOL}},
{"LiveTorqueParamsRelaxedToggle", {PERSISTENT | BACKUP , BOOL}}, {"LiveTorqueParamsRelaxedToggle", {PERSISTENT | BACKUP , BOOL}},
{"TorqueControlTune", {PERSISTENT | BACKUP, FLOAT, "0.0"}}, {"TorqueControlTune", {PERSISTENT | BACKUP, FLOAT}},
{"TorqueParamsOverrideEnabled", {PERSISTENT | BACKUP, BOOL, "0"}}, {"TorqueParamsOverrideEnabled", {PERSISTENT | BACKUP, BOOL, "0"}},
{"TorqueParamsOverrideFriction", {PERSISTENT | BACKUP, FLOAT, "0.1"}}, {"TorqueParamsOverrideFriction", {PERSISTENT | BACKUP, FLOAT, "0.1"}},
{"TorqueParamsOverrideLatAccelFactor", {PERSISTENT | BACKUP, FLOAT, "2.5"}}, {"TorqueParamsOverrideLatAccelFactor", {PERSISTENT | BACKUP, FLOAT, "2.5"}},
@@ -142,28 +142,33 @@ cdef class Params:
cdef ParamKeyType t = self.p.getKeyType(k) cdef ParamKeyType t = self.p.getKeyType(k)
return ensure_bytes(self.python2cpp(type(dat), t, dat, key)) return ensure_bytes(self.python2cpp(type(dat), t, dat, key))
def put(self, key, dat, bool block = False): def put(self, key, dat):
""" """
Warning: block=True blocks until the param is written to disk! Warning: This function blocks until the param is written to disk!
In very rare cases this can take over a second, and your code will hang. In very rare cases this can take over a second, and your code will hang.
Use block=False in time sensitive code, but in general try to avoid Use the put_nonblocking, put_bool_nonblocking in time sensitive code, but
writing params as much as possible. in general try to avoid writing params as much as possible.
""" """
cdef string k = self.check_key(key) cdef string k = self.check_key(key)
cdef string dat_bytes = self._put_cast(key, dat) cdef string dat_bytes = self._put_cast(key, dat)
with nogil: with nogil:
if block: self.p.put(k, dat_bytes)
self.p.put(k, dat_bytes)
else:
self.p.putNonBlocking(k, dat_bytes)
def put_bool(self, key, bool val, bool block = False): def put_bool(self, key, bool val):
cdef string k = self.check_key(key) cdef string k = self.check_key(key)
with nogil: with nogil:
if block: self.p.putBool(k, val)
self.p.putBool(k, val)
else: def put_nonblocking(self, key, dat):
self.p.putBoolNonBlocking(k, val) cdef string k = self.check_key(key)
cdef string dat_bytes = self._put_cast(key, dat)
with nogil:
self.p.putNonBlocking(k, dat_bytes)
def put_bool_nonblocking(self, key, bool val):
cdef string k = self.check_key(key)
with nogil:
self.p.putBoolNonBlocking(k, val)
def remove(self, key): def remove(self, key):
cdef string k = self.check_key(key) cdef string k = self.check_key(key)
@@ -5,7 +5,7 @@
#include "common/params.h" #include "common/params.h"
#include "common/util.h" #include "common/util.h"
#include "common/hardware/hw.h" #include "system/hardware/hw.h"
class OpenpilotPrefix { class OpenpilotPrefix {
public: public:
@@ -5,9 +5,9 @@ import uuid
from openpilot.common.params import Params from openpilot.common.params import Params
from openpilot.common.hardware import PC from openpilot.system.hardware import PC
from openpilot.common.hardware.hw import Paths from openpilot.system.hardware.hw import Paths
from openpilot.common.hardware.hw import DEFAULT_DOWNLOAD_CACHE_ROOT from openpilot.system.hardware.hw import DEFAULT_DOWNLOAD_CACHE_ROOT
class OpenpilotPrefix: class OpenpilotPrefix:
def __init__(self, prefix: str | None = None, create_dirs_on_enter: bool = True, clean_dirs_on_exit: bool = True, shared_download_cache: bool = False): def __init__(self, prefix: str | None = None, create_dirs_on_enter: bool = True, clean_dirs_on_exit: bool = True, shared_download_cache: bool = False):
@@ -7,7 +7,7 @@ import time
from setproctitle import getproctitle from setproctitle import getproctitle
from openpilot.common.utils import MovingAverage from openpilot.common.utils import MovingAverage
from openpilot.common.hardware import PC from openpilot.system.hardware import PC
# time step for each process # time step for each process
@@ -28,11 +28,6 @@ class Priority:
CTRL_HIGH = 53 CTRL_HIGH = 53
def drop_realtime() -> None:
if sys.platform == 'linux' and not PC:
os.sched_setscheduler(0, os.SCHED_OTHER, os.sched_param(0))
def set_core_affinity(cores: list[int]) -> None: def set_core_affinity(cores: list[int]) -> None:
if sys.platform == 'linux' and not PC: if sys.platform == 'linux' and not PC:
os.sched_setaffinity(0, cores) os.sched_setaffinity(0, cores)
@@ -8,7 +8,7 @@ class Spinner:
try: try:
self.spinner_proc = subprocess.Popen(["./spinner.py"], self.spinner_proc = subprocess.Popen(["./spinner.py"],
stdin=subprocess.PIPE, stdin=subprocess.PIPE,
cwd=os.path.join(BASEDIR, "openpilot/system", "ui"), cwd=os.path.join(BASEDIR, "system", "ui"),
close_fds=True) close_fds=True)
except OSError: except OSError:
self.spinner_proc = None self.spinner_proc = None
@@ -11,9 +11,9 @@
#include <zmq.h> #include <zmq.h>
#include <stdarg.h> #include <stdarg.h>
#include "json11/json11.hpp" #include "third_party/json11/json11.hpp"
#include "common/version.h" #include "common/version.h"
#include "common/hardware/hw.h" #include "system/hardware/hw.h"
#include "sunnypilot/common/version.h" #include "sunnypilot/common/version.h"
@@ -8,7 +8,7 @@ from logging.handlers import BaseRotatingHandler
import zmq import zmq
from openpilot.common.logging_extra import SwagLogger, SwagFormatter, SwagLogFileFormatter from openpilot.common.logging_extra import SwagLogger, SwagFormatter, SwagLogFileFormatter
from openpilot.common.hardware.hw import Paths from openpilot.system.hardware.hw import Paths
def get_file_handler(): def get_file_handler():
+27
View File
@@ -0,0 +1,27 @@
#include "catch2/catch.hpp"
#define private public
#include "common/params.h"
#include "common/util.h"
TEST_CASE("params_nonblocking_put") {
char tmp_path[] = "/tmp/asyncWriter_XXXXXX";
const std::string param_path = mkdtemp(tmp_path);
auto param_names = {"CarParams", "IsMetric"};
{
Params params(param_path);
for (const auto &name : param_names) {
params.putNonBlocking(name, "1");
// param is empty
REQUIRE(params.get(name).empty());
}
// check if thread is running
REQUIRE(params.future.valid());
REQUIRE(params.future.wait_for(std::chrono::milliseconds(0)) == std::future_status::timeout);
}
// check results
Params p(param_path);
for (const auto &name : param_names) {
REQUIRE(p.get(name) == "1");
}
}
@@ -12,17 +12,17 @@ class TestParams:
self.params = Params() self.params = Params()
def test_params_put_and_get(self): def test_params_put_and_get(self):
self.params.put("DongleId", "cb38263377b873ee", block=True) self.params.put("DongleId", "cb38263377b873ee")
assert self.params.get("DongleId") == "cb38263377b873ee" assert self.params.get("DongleId") == "cb38263377b873ee"
def test_params_non_ascii(self): def test_params_non_ascii(self):
st = b"\xe1\x90\xff" st = b"\xe1\x90\xff"
self.params.put("CarParams", st, block=True) self.params.put("CarParams", st)
assert self.params.get("CarParams") == st assert self.params.get("CarParams") == st
def test_params_get_cleared_manager_start(self): def test_params_get_cleared_manager_start(self):
self.params.put("CarParams", b"test", block=True) self.params.put("CarParams", b"test")
self.params.put("DongleId", "cb38263377b873ee", block=True) self.params.put("DongleId", "cb38263377b873ee")
assert self.params.get("CarParams") == b"test" assert self.params.get("CarParams") == b"test"
undefined_param = self.params.get_param_path(uuid.uuid4().hex) undefined_param = self.params.get_param_path(uuid.uuid4().hex)
@@ -36,15 +36,15 @@ class TestParams:
assert not os.path.isfile(undefined_param) assert not os.path.isfile(undefined_param)
def test_params_two_things(self): def test_params_two_things(self):
self.params.put("DongleId", "bob", block=True) self.params.put("DongleId", "bob")
self.params.put("AthenadPid", 123, block=True) self.params.put("AthenadPid", 123)
assert self.params.get("DongleId") == "bob" assert self.params.get("DongleId") == "bob"
assert self.params.get("AthenadPid") == 123 assert self.params.get("AthenadPid") == 123
def test_params_get_block(self): def test_params_get_block(self):
def _delayed_writer(): def _delayed_writer():
time.sleep(0.1) time.sleep(0.1)
self.params.put("CarParams", b"test", block=True) self.params.put("CarParams", b"test")
threading.Thread(target=_delayed_writer).start() threading.Thread(target=_delayed_writer).start()
assert self.params.get("CarParams") is None assert self.params.get("CarParams") is None
assert self.params.get("CarParams", block=True) == b"test" assert self.params.get("CarParams", block=True) == b"test"
@@ -57,10 +57,10 @@ class TestParams:
self.params.get_bool("swag") self.params.get_bool("swag")
with pytest.raises(UnknownKeyName): with pytest.raises(UnknownKeyName):
self.params.put("swag", "abc", block=True) self.params.put("swag", "abc")
with pytest.raises(UnknownKeyName): with pytest.raises(UnknownKeyName):
self.params.put_bool("swag", True, block=True) self.params.put_bool("swag", True)
def test_remove_not_there(self): def test_remove_not_there(self):
assert self.params.get("CarParams") is None assert self.params.get("CarParams") is None
@@ -71,23 +71,23 @@ class TestParams:
self.params.remove("IsMetric") self.params.remove("IsMetric")
assert not self.params.get_bool("IsMetric") assert not self.params.get_bool("IsMetric")
self.params.put_bool("IsMetric", True, block=True) self.params.put_bool("IsMetric", True)
assert self.params.get_bool("IsMetric") assert self.params.get_bool("IsMetric")
self.params.put_bool("IsMetric", False, block=True) self.params.put_bool("IsMetric", False)
assert not self.params.get_bool("IsMetric") assert not self.params.get_bool("IsMetric")
self.params.put("IsMetric", True, block=True) self.params.put("IsMetric", True)
assert self.params.get_bool("IsMetric") assert self.params.get_bool("IsMetric")
self.params.put("IsMetric", False, block=True) self.params.put("IsMetric", False)
assert not self.params.get_bool("IsMetric") assert not self.params.get_bool("IsMetric")
def test_put_non_blocking_with_get_block(self): def test_put_non_blocking_with_get_block(self):
q = Params() q = Params()
def _delayed_writer(): def _delayed_writer():
time.sleep(0.1) time.sleep(0.1)
Params().put("CarParams", b"test") Params().put_nonblocking("CarParams", b"test")
threading.Thread(target=_delayed_writer).start() threading.Thread(target=_delayed_writer).start()
assert q.get("CarParams") is None assert q.get("CarParams") is None
assert q.get("CarParams", True) == b"test" assert q.get("CarParams", True) == b"test"
@@ -96,7 +96,7 @@ class TestParams:
q = Params() q = Params()
def _delayed_writer(): def _delayed_writer():
time.sleep(0.1) time.sleep(0.1)
Params().put_bool("CarParams", True) Params().put_bool_nonblocking("CarParams", True)
threading.Thread(target=_delayed_writer).start() threading.Thread(target=_delayed_writer).start()
assert q.get("CarParams") is None assert q.get("CarParams") is None
assert q.get("CarParams", True) == b"1" assert q.get("CarParams", True) == b"1"
@@ -123,19 +123,19 @@ class TestParams:
def test_params_get_type(self): def test_params_get_type(self):
# json # json
self.params.put("ApiCache_FirehoseStats", {"a": 0}, block=True) self.params.put("ApiCache_FirehoseStats", {"a": 0})
assert self.params.get("ApiCache_FirehoseStats") == {"a": 0} assert self.params.get("ApiCache_FirehoseStats") == {"a": 0}
# int # int
self.params.put("BootCount", 1441, block=True) self.params.put("BootCount", 1441)
assert self.params.get("BootCount") == 1441 assert self.params.get("BootCount") == 1441
# bool # bool
self.params.put("AdbEnabled", True, block=True) self.params.put("AdbEnabled", True)
assert self.params.get("AdbEnabled") assert self.params.get("AdbEnabled")
assert isinstance(self.params.get("AdbEnabled"), bool) assert isinstance(self.params.get("AdbEnabled"), bool)
# time # time
now = datetime.datetime.now(datetime.UTC) now = datetime.datetime.now(datetime.UTC)
self.params.put("InstallDate", now, block=True) self.params.put("InstallDate", now)
assert self.params.get("InstallDate") == now assert self.params.get("InstallDate") == now
@@ -6,8 +6,8 @@
#include "common/swaglog.h" #include "common/swaglog.h"
#include "common/util.h" #include "common/util.h"
#include "common/version.h" #include "common/version.h"
#include "common/hardware/hw.h" #include "system/hardware/hw.h"
#include "json11/json11.hpp" #include "third_party/json11/json11.hpp"
#include "sunnypilot/common/version.h" #include "sunnypilot/common/version.h"
@@ -10,7 +10,7 @@ class TextWindow:
try: try:
self.text_proc = subprocess.Popen(["./text.py", text], self.text_proc = subprocess.Popen(["./text.py", text],
stdin=subprocess.PIPE, stdin=subprocess.PIPE,
cwd=os.path.join(BASEDIR, "openpilot/system", "ui"), cwd=os.path.join(BASEDIR, "system", "ui"),
close_fds=True) close_fds=True)
except OSError: except OSError:
self.text_proc = None self.text_proc = None

Some files were not shown because too many files have changed in this diff Show More