Compare commits

..

1 Commits

Author SHA1 Message Date
rav4kumar 41ce29af86 feat: AccelPersonalityController 2026-05-27 11:58:59 -07:00
1942 changed files with 102027 additions and 13172 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 }}
@@ -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
@@ -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
+14 -46
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:
@@ -30,68 +30,36 @@ jobs:
- name: Checkout sunnypilot cereal - name: Checkout sunnypilot cereal
uses: actions/checkout@v6 uses: actions/checkout@v6
with: with:
sparse-checkout: | sparse-checkout: cereal
openpilot/cereal
uv.lock
submodules: false
- name: Init sunnypilot opendbc submodule - name: Init sunnypilot opendbc submodule
run: git submodule update --init --depth 1 opendbc_repo run: git submodule update --init --depth 1 opendbc_repo
- name: Locate sunnypilot capnp import paths - name: Checkout upstream openpilot cereal
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
uses: actions/checkout@v6 uses: actions/checkout@v6
with: with:
repository: 'commaai/openpilot' repository: 'commaai/openpilot'
path: upstream_openpilot path: upstream_openpilot
sparse-checkout: cereal
ref: "refs/heads/master" ref: "refs/heads/master"
- name: Init upstream opendbc submodule - name: Init upstream opendbc submodule
working-directory: upstream_openpilot working-directory: upstream_openpilot
run: git submodule update --init --depth 1 opendbc_repo run: git submodule update --init --depth 1 opendbc_repo
- name: Locate upstream capnp paths - name: Install uv
id: locate-capnp run: pip install uv
run: |
CEREAL_DIR=$(find upstream_openpilot -maxdepth 4 -name log.capnp -path '*/cereal/log.capnp' -printf '%h\n' -quit)
if [ -z "$CEREAL_DIR" ]; then
echo "::error::Could not locate cereal/log.capnp in upstream openpilot"
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 - name: Generate sunnypilot schema
run: | run: |
python3 openpilot/cereal/messaging/tests/validate_sp_cereal_upstream.py \ PYCAPNP_VER=$(python3 -c "import re; m=re.search(r'name = \"pycapnp\"\nversion = \"([^\"]+)\"', open('uv.lock').read()); print(m.group(1))")
-g -f /tmp/sp_schema.json --cereal-dir openpilot/cereal \ uv run --isolated --with "pycapnp==${PYCAPNP_VER}" \
${{ steps.locate-sp-capnp.outputs.import_args }} python3 cereal/messaging/tests/validate_sp_cereal_upstream.py \
-g -f /tmp/sp_schema.json --cereal-dir cereal
- name: Validate against upstream - name: Validate against upstream
run: | run: |
python3 openpilot/cereal/messaging/tests/validate_sp_cereal_upstream.py \ PYCAPNP_VER=$(python3 -c "import re; m=re.search(r'name = \"pycapnp\"\nversion = \"([^\"]+)\"', open('uv.lock').read()); print(m.group(1))")
-r -f /tmp/sp_schema.json --cereal-dir ${{ steps.locate-capnp.outputs.cereal_dir }} \ uv run --isolated --with "pycapnp==${PYCAPNP_VER}" \
${{ steps.locate-capnp.outputs.import_args }} python3 cereal/messaging/tests/validate_sp_cereal_upstream.py \
-r -f /tmp/sp_schema.json --cereal-dir upstream_openpilot/cereal
+12 -24
View File
@@ -17,41 +17,29 @@ jobs:
- name: Wait for process replay - name: Wait for process replay
id: wait id: wait
continue-on-error: true continue-on-error: true
uses: lewagon/wait-on-check-action@1d57e2c51a58d812d2765e036a028b6bdb5a6154 uses: lewagon/wait-on-check-action@v1.3.4
with: with:
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
check-name: process replay check-name: process replay
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
allowed-conclusions: success,failure allowed-conclusions: success,failure
wait-interval: 20 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 - name: Download diff
if: steps.wait.outcome == 'success' if: steps.wait.outcome == 'success'
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c uses: dawidd6/action-download-artifact@v6
with: with:
github-token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
repository: ${{ github.repository }} workflow: tests.yaml
run-id: ${{ steps.tests-run.outputs.run-id }} workflow_conclusion: ''
pr: ${{ github.event.number }}
name: diff_report_${{ github.event.number }} name: diff_report_${{ github.event.number }}
path: . path: .
allow_forks: true
- name: Comment on PR - name: Comment on PR
if: steps.wait.outcome == 'success' if: steps.wait.outcome == 'success'
uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b uses: thollander/actions-comment-pull-request@v2
with: with:
file-path: diff_report.txt filePath: diff_report.txt
comment-tag: diff_report comment_tag: diff_report
pr-number: ${{ github.event.number }} pr_number: ${{ github.event.number }}
github-token: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+3 -3
View File
@@ -22,7 +22,7 @@ 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
@@ -34,7 +34,7 @@ jobs:
python scripts/docs.py build python scripts/docs.py 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@v4
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
+4 -48
View File
@@ -3,8 +3,6 @@ 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:
@@ -14,11 +12,9 @@ jobs:
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 +50,10 @@ jobs:
git add . git add .
- name: update car docs - name: update car docs
run: | run: |
python openpilot/selfdrive/car/docs.py 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 +75,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..."
@@ -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}/
+50 -53
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,44 @@ 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 - name: Print diff report
if: always() if: always()
run: cat openpilot/selfdrive/test/process_replay/diff_report.txt run: cat selfdrive/test/process_replay/diff_report.txt
- uses: actions/upload-artifact@v7 - uses: actions/upload-artifact@v6
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 - name: Upload diff report
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v6
if: always() && github.event_name == 'pull_request' if: always() && github.event_name == 'pull_request'
continue-on-error: true continue-on-error: true
with: with:
name: diff_report_${{ github.event.number }} name: diff_report_${{ github.event.number }}
path: openpilot/selfdrive/test/process_replay/diff_report.txt path: 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 +187,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 --force
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 +209,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 +230,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 }}
+9 -16
View File
@@ -15,13 +15,6 @@ a.out
.cache/ .cache/
bin/ bin/
# created at launch for TICI PYTHONPATH (PC uses editable installs via pyproject.toml)
/msgq
/opendbc
/rednose
/teleoprtc
/tinygrad
*.mp4 *.mp4
*.dylib *.dylib
*.DSYM *.DSYM
@@ -51,18 +44,18 @@ bin/
config.json config.json
compile_commands.json compile_commands.json
compare_runtime*.html compare_runtime*.html
openpilot/selfdrive/modeld/models/tg_input_devices.json selfdrive/modeld/models/tg_compiled_flags.json
# build artifacts # build artifacts
docs_site/ docs_site/
openpilot/selfdrive/pandad/pandad selfdrive/pandad/pandad
openpilot/cereal/services.h cereal/services.h
openpilot/cereal/gen cereal/gen
openpilot/cereal/messaging/bridge cereal/messaging/bridge
openpilot/selfdrive/ui/translations/tmp selfdrive/ui/translations/tmp
openpilot/selfdrive/car/tests/cars_dump selfdrive/car/tests/cars_dump
openpilot/system/camerad/camerad system/camerad/camerad
openpilot/system/camerad/test/ae_gray_test system/camerad/test/ae_gray_test
.coverage* .coverage*
coverage.xml coverage.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
+4 -4
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"
] ]
}, },
{ {
+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 -30
View File
@@ -1,34 +1,5 @@
sunnypilot Version 2026.002.000 (2026-06-28) sunnypilot Version 2026.002.000 (2026-xx-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) sunnypilot Version 2026.001.000 (2026-05-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', 'release-mici-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 && git push -f origin release-tizi-staging:release-mici-staging"),
]) ])
} }
@@ -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"),
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"),
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/"]]),
]) ])
}, },
+2 -8
View File
@@ -1,13 +1,7 @@
Version 0.11.2 (2026-06-15) Version 0.11.1 (2026-04-22)
========================
Version 0.11.1 (2026-05-18)
======================== ========================
* New driver monitoring model * New driver monitoring model
* Improved image processing pipeline for driver camera * 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! * Rivian R1S and R1T 2025 support thanks to lukasloetkolben!
Version 0.11.0 (2026-03-17) Version 0.11.0 (2026-03-17)
@@ -1104,7 +1098,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
+55 -132
View File
@@ -10,43 +10,25 @@ import numpy as np
import SCons.Errors import SCons.Errors
from SCons.Defaults import _stripixes 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('--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,29 +37,8 @@ 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'] pkg_names = ['bzip2', 'capnproto', 'eigen', 'ffmpeg', 'libjpeg', 'libyuv', 'ncurses', 'zeromq', 'zstd']
pkgs = [importlib.import_module(name) for name in pkg_names] pkgs = [importlib.import_module(name) for name in pkg_names]
acados = pkgs[pkg_names.index('acados')]
ffmpeg = pkgs[pkg_names.index('ffmpeg')]
# Shared package ships .so/.dylib; older device venvs still have static .a only.
# Keep static link deps (x264/z/va/drm) when the installed package is static so
# TICI CI works without upgrading the device venv yet.
# TODO: drop the static fallback once device venvs have comma-deps-ffmpeg>=7.1.0.post94
_ffmpeg_lib_names = os.listdir(ffmpeg.LIB_DIR) if os.path.isdir(ffmpeg.LIB_DIR) else []
ffmpeg_shared = any(
n.startswith('libavcodec.so') or (n.startswith('libavcodec') and n.endswith('.dylib'))
for n in _ffmpeg_lib_names
)
ffmpeg_libs = ['avformat', 'avcodec', 'swresample', 'avutil']
if not ffmpeg_shared:
ffmpeg_libs += ['x264', 'z']
if arch != "Darwin":
ffmpeg_libs += ['va', 'va-drm', 'drm']
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 ***** # ***** enforce a whitelist of system libraries *****
@@ -121,10 +82,10 @@ def _libflags(target, source, env, for_signature):
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,27 +103,30 @@ 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"],
) )
@@ -210,6 +174,16 @@ 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"] += [sysconfig.get_paths()['include'], np.get_include()]
@@ -225,28 +199,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')
@@ -257,7 +221,7 @@ 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(['openpilot/cereal/SConscript']) SConscript(['cereal/SConscript'])
Import('socketmaster', 'msgq') Import('socketmaster', 'msgq')
messaging = [socketmaster, msgq, 'capnp', 'kj',] messaging = [socketmaster, msgq, 'capnp', 'kj',]
@@ -268,79 +232,38 @@ 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 openpilot
SConscript(['third_party/SConscript'])
# Build selfdrive # Build selfdrive
SConscript([ SConscript([
'openpilot/selfdrive/pandad/SConscript', 'selfdrive/pandad/SConscript',
'openpilot/selfdrive/controls/lib/longitudinal_mpc_lib/SConscript', 'selfdrive/controls/lib/lateral_mpc_lib/SConscript',
'openpilot/selfdrive/locationd/SConscript', 'selfdrive/controls/lib/longitudinal_mpc_lib/SConscript',
'openpilot/selfdrive/modeld/SConscript', 'selfdrive/locationd/SConscript',
'openpilot/selfdrive/ui/SConscript', 'selfdrive/modeld/SConscript',
'selfdrive/ui/SConscript',
]) ])
SConscript(['openpilot/sunnypilot/SConscript']) SConscript(['sunnypilot/SConscript'])
# Build desktop-only tools # Build tools
if GetOption('extras') and arch != "larch64": if arch != "larch64":
SConscript([ SConscript([
'openpilot/tools/replay/SConscript', 'tools/replay/SConscript',
'openpilot/tools/cabana/SConscript', 'tools/cabana/SConscript',
'openpilot/tools/jotpluggler/SConscript', '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', 'deprecated.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;
@@ -194,6 +194,13 @@ struct LongitudinalPlanSP @0xf35cc4560bbf6ec2 {
aTarget @5 :Float32; aTarget @5 :Float32;
events @6 :List(OnroadEventSP.Event); events @6 :List(OnroadEventSP.Event);
e2eAlerts @7 :E2eAlerts; e2eAlerts @7 :E2eAlerts;
accelPersonality @8 :AccelerationPersonality;
enum AccelerationPersonality {
sport @0;
normal @1;
eco @2;
}
struct DynamicExperimentalControl { struct DynamicExperimentalControl {
state @0 :DynamicExperimentalControlState; state @0 :DynamicExperimentalControlState;
@@ -1,4 +1,4 @@
using Cxx = import "/include/c++.capnp"; using Cxx = import "./include/c++.capnp";
$Cxx.namespace("cereal"); $Cxx.namespace("cereal");
@0x80ef1ec4889c2a63; @0x80ef1ec4889c2a63;
@@ -760,6 +760,18 @@ struct LateralLQRState @0x9024e2d790c82ade {
steeringAngleDesiredDeg @6 :Float32; steeringAngleDesiredDeg @6 :Float32;
} }
struct LateralCurvatureState @0xad9d8095c06f7c61 {
active @0 :Bool;
actualCurvature @1 :Float32;
desiredCurvature @2 :Float32;
error @3 :Float32;
p @4 :Float32;
i @5 :Float32;
f @6 :Float32;
output @7 :Float32;
saturated @8 :Bool;
}
struct LateralPlannerSolution @0x84caeca5a6b4acfe { struct LateralPlannerSolution @0x84caeca5a6b4acfe {
x @0 :List(Float32); x @0 :List(Float32);
y @1 :List(Float32); y @1 :List(Float32);
+49 -182
View File
@@ -1,7 +1,7 @@
using Cxx = import "/include/c++.capnp"; using Cxx = import "./include/c++.capnp";
$Cxx.namespace("cereal"); $Cxx.namespace("cereal");
using Car = import "/car.capnp"; using Car = import "car.capnp";
using Deprecated = import "deprecated.capnp"; using Deprecated = import "deprecated.capnp";
using Custom = import "custom.capnp"; using Custom = import "custom.capnp";
@@ -273,7 +273,11 @@ struct GPSNMEAData {
nmea @2 :Text; nmea @2 :Text;
} }
# android sensor_event_t
struct SensorEventData { struct SensorEventData {
version @0 :Int32;
sensor @1 :Int32;
type @2 :Int32;
timestamp @3 :Int64; timestamp @3 :Int64;
union { union {
@@ -292,10 +296,7 @@ struct SensorEventData {
struct SensorVec { struct SensorVec {
v @0 :List(Float32); v @0 :List(Float32);
status @1 :Int8;
deprecated :group {
status @1 :Int8;
}
} }
enum SensorSource { enum SensorSource {
@@ -313,11 +314,7 @@ struct SensorEventData {
mmc5603nj @11; mmc5603nj @11;
} }
# formerly based on android sensor_event_t
deprecated :group { deprecated :group {
version @0 :Int32;
sensor @1 :Int32;
type @2 :Int32;
uncalibrated @10 :Bool; uncalibrated @10 :Bool;
} }
} }
@@ -414,10 +411,6 @@ struct CanData {
struct DeviceState @0xa4d8b5af2aa492eb { struct DeviceState @0xa4d8b5af2aa492eb {
deviceType @45 :InitData.DeviceType; deviceType @45 :InitData.DeviceType;
# usb
chestnutPresent @51 :Bool;
usbState @52 :UsbState;
networkType @22 :NetworkType; networkType @22 :NetworkType;
networkInfo @31 :NetworkInfo; networkInfo @31 :NetworkInfo;
networkStrength @24 :NetworkStrength; networkStrength @24 :NetworkStrength;
@@ -464,10 +457,10 @@ struct DeviceState @0xa4d8b5af2aa492eb {
} }
enum ThermalStatus { enum ThermalStatus {
ok @0; green @0;
warmDEPRECATED @1; yellow @1;
overheated @2; red @2;
critical @3; danger @3;
} }
enum NetworkType { enum NetworkType {
@@ -688,47 +681,33 @@ struct PeripheralState {
} }
} }
struct UsbState {
devices @0 :List(Device);
struct Device {
busnum @0 :UInt8;
devnum @1 :UInt8;
vendorId @2 :UInt16;
productId @3 :UInt16;
speedMbps @4 :UInt16;
manufacturer @6 :Text;
product @5 :Text;
linkErrorCount @7 :UInt16;
}
}
struct RadarState @0x9a185389d6fdd05f { struct RadarState @0x9a185389d6fdd05f {
mdMonoTime @6 :UInt64; # for debugging mdMonoTime @6 :UInt64;
carStateMonoTime @11 :UInt64;
radarErrors @13 :Car.RadarData.Error; radarErrors @13 :Car.RadarData.Error;
leadOne @3 :LeadData; leadOne @3 :LeadData;
leadTwo @4 :LeadData; leadTwo @4 :LeadData;
struct LeadData { struct LeadData {
dRel @0 :Float32; # m from the front bumper of the car dRel @0 :Float32;
yRel @1 :Float32; # m in car frame, left positive yRel @1 :Float32;
vRel @2 :Float32; # m/s relative longitudinal speed vRel @2 :Float32;
vLead @4 :Float32; # m/s absolute lead speed aRel @3 :Float32;
vLeadK @8 :Float32; # kalman-filtered lead speed vLead @4 :Float32;
aLeadK @9 :Float32; # kalman-filtered lead accel dPath @6 :Float32;
present @11 :Bool; # true if a lead is present vLat @7 :Float32;
aLeadTau @12 :Float32; # lead accel time constant vLeadK @8 :Float32;
modelProb @13 :Float32; # vision model lead probability aLeadK @9 :Float32;
radar @14 :Bool; # true if lead is radar-matched (vs vision-only) fcw @10 :Bool;
radarTrackId @15 :Int32 = -1; # for debugging status @11 :Bool;
aLeadTau @12 :Float32;
modelProb @13 :Float32;
radar @14 :Bool;
radarTrackId @15 :Int32 = -1;
deprecated :group { deprecated :group {
aRel @3 :Float32;
aLead @5 :Float32; aLead @5 :Float32;
dPath @6 :Float32;
vLat @7 :Float32;
fcw @10 :Bool;
} }
} }
@@ -741,7 +720,6 @@ struct RadarState @0x9a185389d6fdd05f {
calPerc @9 :Int8; calPerc @9 :Int8;
canMonoTimes @10 :List(UInt64); canMonoTimes @10 :List(UInt64);
cumLagMs @5 :Float32; cumLagMs @5 :Float32;
carStateMonoTime @11 :UInt64;
radarErrors @12 :List(Car.RadarData.ErrorDEPRECATED); radarErrors @12 :List(Car.RadarData.ErrorDEPRECATED);
} }
} }
@@ -790,30 +768,13 @@ struct SelfdriveState {
alertStatus @5 :AlertStatus; alertStatus @5 :AlertStatus;
alertSize @6 :AlertSize; alertSize @6 :AlertSize;
alertType @7 :Text; alertType @7 :Text;
alertSound @13 :AudibleAlert; alertSound @8 :Car.CarControl.HUDControl.AudibleAlert;
alertHudVisual @12 :Car.CarControl.HUDControl.VisualAlert; alertHudVisual @12 :Car.CarControl.HUDControl.VisualAlert;
# configurable driving settings # configurable driving settings
experimentalMode @10 :Bool; experimentalMode @10 :Bool;
personality @11 :LongitudinalPersonality; personality @11 :LongitudinalPersonality;
enum AudibleAlert {
none @0;
engage @1;
disengage @2;
refuse @3;
warningSoft @4;
warningImmediate @5;
prompt @6;
promptRepeat @7;
promptDistracted @8;
preAlert @9;
}
enum OpenpilotState @0xdbe58b96d2d1ac61 { enum OpenpilotState @0xdbe58b96d2d1ac61 {
disabled @0; disabled @0;
preEnabled @1; preEnabled @1;
@@ -834,10 +795,6 @@ struct SelfdriveState {
mid @2; mid @2;
full @3; full @3;
} }
deprecated :group {
alertSound @8 :Car.CarControl.HUDControl.AudibleAlert;
}
} }
struct ControlsState @0x97ff69c53601abf1 { struct ControlsState @0x97ff69c53601abf1 {
@@ -858,7 +815,7 @@ struct ControlsState @0x97ff69c53601abf1 {
debugState @59 :LateralDebugState; debugState @59 :LateralDebugState;
torqueState @60 :LateralTorqueState; torqueState @60 :LateralTorqueState;
curvatureState @65 :LateralCurvatureState; curvatureStateDEPRECATED @65 :Deprecated.LateralCurvatureState;
lqrStateDEPRECATED @55 :Deprecated.LateralLQRState; lqrStateDEPRECATED @55 :Deprecated.LateralLQRState;
indiStateDEPRECATED @52 :Deprecated.LateralINDIState; indiStateDEPRECATED @52 :Deprecated.LateralINDIState;
} }
@@ -907,18 +864,6 @@ struct ControlsState @0x97ff69c53601abf1 {
saturated @3 :Bool; saturated @3 :Bool;
} }
struct LateralCurvatureState @0xad9d8095c06f7c61 {
active @0 :Bool;
actualCurvature @1 :Float32;
desiredCurvature @2 :Float32;
error @3 :Float32;
p @4 :Float32;
i @5 :Float32;
f @6 :Float32;
output @7 :Float32;
saturated @8 :Bool;
}
deprecated :group { deprecated :group {
vEgo @0 :Float32; vEgo @0 :Float32;
vEgoRaw @32 :Float32; vEgoRaw @32 :Float32;
@@ -958,7 +903,7 @@ struct ControlsState @0x97ff69c53601abf1 {
alertStatus @38 :SelfdriveState.AlertStatus; alertStatus @38 :SelfdriveState.AlertStatus;
alertSize @39 :SelfdriveState.AlertSize; alertSize @39 :SelfdriveState.AlertSize;
alertType @44 :Text; alertType @44 :Text;
alertSound2 @56 :SelfdriveState.AudibleAlert; alertSound2 @56 :Car.CarControl.HUDControl.AudibleAlert;
engageable @41 :Bool; # can OP be engaged? engageable @41 :Bool; # can OP be engaged?
state @31 :SelfdriveState.OpenpilotState; state @31 :SelfdriveState.OpenpilotState;
enabled @19 :Bool; enabled @19 :Bool;
@@ -1041,6 +986,7 @@ struct ModelDataV2 {
roadEdgeStds @14 :List(Float32); roadEdgeStds @14 :List(Float32);
# predicted lead cars # predicted lead cars
leads @11 :List(LeadDataV2);
leadsV3 @18 :List(LeadDataV3); leadsV3 @18 :List(LeadDataV3);
meta @12 :MetaData; meta @12 :MetaData;
@@ -1050,9 +996,8 @@ struct ModelDataV2 {
action @26: Action; action @26: Action;
lateralPlannerSolutionDEPRECATED @25: Deprecated.LateralPlannerSolution; lateralPlannerSolutionDEPRECATED @25: Deprecated.LateralPlannerSolution;
leadsDEPRECATED @11 :List(LeadDataV2DEPRECATED);
struct LeadDataV2DEPRECATED { struct LeadDataV2 {
prob @0 :Float32; # probability that car is your lead at time t prob @0 :Float32; # probability that car is your lead at time t
t @1 :Float32; t @1 :Float32;
@@ -1172,7 +1117,7 @@ struct EncodeIndex {
} }
} }
struct OperatingSystemLogEntry { struct AndroidLogEntry {
id @0 :UInt8; id @0 :UInt8;
ts @1 :UInt64; ts @1 :UInt64;
priority @2 :UInt8; priority @2 :UInt8;
@@ -1310,7 +1255,7 @@ struct LateralPlan @0xe1e9318e2ae8b51e {
struct LiveLocationKalman { struct LiveLocationKalman {
# More info on reference frames: # More info on reference frames:
# https://github.com/commaai/openpilot/tree/master/openpilot/common/transformations # https://github.com/commaai/openpilot/tree/master/common/transformations
positionECEF @0 : Measurement; positionECEF @0 : Measurement;
positionGeodetic @1 : Measurement; positionGeodetic @1 : Measurement;
@@ -1367,7 +1312,7 @@ struct LiveLocationKalman {
struct LivePose { struct LivePose {
# More info on reference frames: # More info on reference frames:
# https://github.com/commaai/openpilot/tree/master/openpilot/common/transformations # https://github.com/commaai/openpilot/tree/master/common/transformations
orientationNED @0 :XYZMeasurement; orientationNED @0 :XYZMeasurement;
velocityDevice @1 :XYZMeasurement; velocityDevice @1 :XYZMeasurement;
accelerationDevice @2 :XYZMeasurement; accelerationDevice @2 :XYZMeasurement;
@@ -2115,7 +2060,6 @@ struct DriverStateV2 {
rightBlinkProb @8 :Float32; rightBlinkProb @8 :Float32;
sunglassesProb @9 :Float32; sunglassesProb @9 :Float32;
phoneProb @13 :Float32; phoneProb @13 :Float32;
sleepProb @14 :Float32;
deprecated :group { deprecated :group {
notReadyProb @12 :List(Float32); notReadyProb @12 :List(Float32);
@@ -2130,7 +2074,7 @@ struct DriverStateV2 {
} }
} }
struct DriverMonitoringStateDEPRECATED @0xb83cda094a1da284 { struct DriverMonitoringState @0xb83cda094a1da284 {
events @18 :List(OnroadEvent); events @18 :List(OnroadEvent);
faceDetected @1 :Bool; faceDetected @1 :Bool;
isDistracted @2 :Bool; isDistracted @2 :Bool;
@@ -2158,82 +2102,6 @@ struct DriverMonitoringStateDEPRECATED @0xb83cda094a1da284 {
} }
} }
struct DriverMonitoringState {
lockout @0 :Bool;
lockoutRecoveryPercent @11 :Int8;
alert3Count @12 :Int8;
noResponseCount @13 :Int8;
noResponseForceDecel @14 :Bool;
alwaysOn @3 :Bool;
alwaysOnLockout @4 :Bool;
alertLevel @5 :AlertLevel;
activePolicy @6 :MonitoringPolicy;
isRHD @7 :Bool;
rhdCalibration @8 :CalibrationState;
visionPolicyState @9 :VisionPolicyState;
wheeltouchPolicyState @10 :WheeltouchPolicyState;
enum AlertLevel {
# ordinal must match the name to prevent bugs
# comparing against the raw ordinal value
none @0;
one @1;
two @2;
three @3;
}
enum MonitoringPolicy {
wheeltouch @0;
vision @1;
}
struct VisionPolicyState {
awarenessPercent @0 :Int8;
awarenessStep @1 :Float32;
isDistracted @2 :Bool;
distractedTypes @3 :DistractedTypes;
faceDetected @4 :Bool;
pose @5 :Pose;
wheeltouchFallbackPercent @6 :Int8;
uncertainOffroadAlertPercent @7 :Int8;
struct DistractedTypes {
pose @0: Bool;
eye @1: Bool;
phone @2: Bool;
}
struct Pose {
pitch @0 :Float32;
yaw @1 :Float32;
pitchCalib @2 :CalibrationState;
yawCalib @3 :CalibrationState;
calibrated @4 :Bool;
uncertainty @5 :Float32;
}
}
struct WheeltouchPolicyState {
awarenessPercent @0 :Int8;
awarenessStep @1 :Float32;
driverInteracting @2 :Bool;
}
struct CalibrationState {
calibratedPercent @0 :Int8;
offset @1 :Float32;
}
deprecated :group {
alertCountLockoutPercent @1 :Int8;
alertTimeLockoutPercent @2 :Int8;
}
}
struct Boot { struct Boot {
wallTimeNanos @0 :UInt64; wallTimeNanos @0 :UInt64;
pstore @4 :Map(Text, Data); pstore @4 :Map(Text, Data);
@@ -2306,7 +2174,6 @@ struct LiveDelayData {
lateralDelayEstimateStd @5 :Float32; lateralDelayEstimateStd @5 :Float32;
points @4 :List(Float32); points @4 :List(Float32);
calPerc @6 :Int8; calPerc @6 :Int8;
version @7 :Int32;
enum Status { enum Status {
unestimated @0; unestimated @0;
@@ -2360,8 +2227,7 @@ struct Sentinel {
} }
struct UIDebug { struct UIDebug {
cpuTimeMillis @0 :Float32; drawTimeMillis @0 :Float32;
frameTimeMillis @1 :Float32;
} }
struct ManagerState { struct ManagerState {
@@ -2509,6 +2375,7 @@ struct Event {
boot @60 :Boot; boot @60 :Boot;
# ********** openpilot daemon msgs ********** # ********** openpilot daemon msgs **********
gpsNMEA @3 :GPSNMEAData;
can @5 :List(CanData); can @5 :List(CanData);
controlsState @7 :ControlsState; controlsState @7 :ControlsState;
selfdriveState @130 :SelfdriveState; selfdriveState @130 :SelfdriveState;
@@ -2533,6 +2400,7 @@ struct Event {
qcomGnss @31 :QcomGnss; qcomGnss @31 :QcomGnss;
gpsLocationExternal @48 :GpsLocationData; gpsLocationExternal @48 :GpsLocationData;
gpsLocation @21 :GpsLocationData; gpsLocation @21 :GpsLocationData;
gnssMeasurements @91 :GnssMeasurements;
liveParameters @61 :LiveParametersData; liveParameters @61 :LiveParametersData;
liveTorqueParameters @94 :LiveTorqueParametersData; liveTorqueParameters @94 :LiveTorqueParametersData;
liveDelay @146 : LiveDelayData; liveDelay @146 : LiveDelayData;
@@ -2540,7 +2408,7 @@ struct Event {
thumbnail @66: Thumbnail; thumbnail @66: Thumbnail;
onroadEvents @134: List(OnroadEvent); onroadEvents @134: List(OnroadEvent);
carParams @69: Car.CarParams; carParams @69: Car.CarParams;
driverMonitoringState @151 :DriverMonitoringState; driverMonitoringState @71: DriverMonitoringState;
livePose @129 :LivePose; livePose @129 :LivePose;
modelV2 @75 :ModelDataV2; modelV2 @75 :ModelDataV2;
drivingModelData @128 :DrivingModelData; drivingModelData @128 :DrivingModelData;
@@ -2564,8 +2432,9 @@ struct Event {
rawAudioData @147 :AudioData; rawAudioData @147 :AudioData;
# systems stuff # systems stuff
operatingSystemLog @20 :OperatingSystemLogEntry; androidLog @20 :AndroidLogEntry;
managerState @78 :ManagerState; managerState @78 :ManagerState;
uploaderState @79 :UploaderState;
procLog @33 :ProcLog; procLog @33 :ProcLog;
clocks @35 :Clocks; clocks @35 :Clocks;
deviceState @6 :DeviceState; deviceState @6 :DeviceState;
@@ -2575,6 +2444,12 @@ struct Event {
# touch frame # touch frame
touch @135 :List(Touch); touch @135 :List(Touch);
# navigation
navInstruction @82 :NavInstruction;
navRoute @83 :NavRoute;
navThumbnail @84: Thumbnail;
mapRenderState @105: MapRenderState;
# UI services # UI services
uiDebug @102 :UIDebug; uiDebug @102 :UIDebug;
@@ -2676,13 +2551,5 @@ struct Event {
gyroscope2DEPRECATED @100 :SensorEventData; gyroscope2DEPRECATED @100 :SensorEventData;
accelerometer2DEPRECATED @101 :SensorEventData; accelerometer2DEPRECATED @101 :SensorEventData;
temperatureSensor2DEPRECATED @123 :SensorEventData; temperatureSensor2DEPRECATED @123 :SensorEventData;
driverMonitoringStateDEPRECATED @71 :DriverMonitoringStateDEPRECATED;
gpsNMEADEPRECATED @3 :GPSNMEAData;
uploaderStateDEPRECATED @79 :UploaderState;
navInstructionDEPRECATED @82 :NavInstruction;
navRouteDEPRECATED @83 :NavRoute;
navThumbnailDEPRECATED @84 :Thumbnail;
gnssMeasurementsDEPRECATED @91 :GnssMeasurements;
mapRenderStateDEPRECATED @105: MapRenderState;
} }
} }
@@ -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"
+256
View File
@@ -0,0 +1,256 @@
#!/usr/bin/env python3
"""Schema-level cereal compat check between sunnypilot and upstream openpilot.
Rules (per struct matched across sides by typeId):
R1 shared ordinal must reference the same type.
R2 sunnypilot-only ordinal in a union -> FAIL (unknown discriminant upstream).
R3 sunnypilot-only ordinal on a regular field -> OK (additive struct evolution).
R4 upstream-only ordinal -> OK.
R5 sunnypilot-only struct referenced via an upstream-shared field -> FAIL.
"""
from __future__ import annotations
import argparse
import json
import os
import sys
from typing import Any
NO_DISCRIMINANT = 0xFFFF
def hex_id(value: int) -> str:
return f"0x{value:016x}"
def encode_type(type_node: Any) -> dict:
which = type_node.which()
if which == "struct":
return {"kind": "struct", "typeId": hex_id(type_node.struct.typeId)}
if which == "enum":
return {"kind": "enum", "typeId": hex_id(type_node.enum.typeId)}
if which == "interface":
return {"kind": "interface", "typeId": hex_id(type_node.interface.typeId)}
if which == "list":
return {"kind": "list", "element": encode_type(type_node.list.elementType)}
if which == "anyPointer":
return {"kind": "anyPointer"}
return {"kind": which}
def encode_field(name: str, field: Any) -> dict:
proto = field.proto
ordinal = proto.ordinal.explicit if proto.ordinal.which() == "explicit" else None
discriminant = proto.discriminantValue if proto.discriminantValue != NO_DISCRIMINANT else None
if proto.which() == "group":
type_desc = {"kind": "group", "typeId": hex_id(proto.group.typeId)}
else:
type_desc = encode_type(proto.slot.type)
return {
"name": name,
"ordinal": ordinal,
"discriminant": discriminant,
"type": type_desc,
}
def encode_struct(schema: Any) -> dict:
node = schema.node
return {
"typeId": hex_id(node.id),
"displayName": node.displayName,
"hasUnion": node.struct.discriminantCount > 0,
"fields": [encode_field(name, field) for name, field in schema.fields.items()],
}
def _child_struct_schema(field: Any) -> Any:
proto = field.proto
if proto.which() == "group":
return field.schema
type_node = proto.slot.type
which = type_node.which()
if which == "struct":
return field.schema
if which == "list":
container = field.schema
element_type = type_node.list.elementType
while element_type.which() == "list":
container = container.elementType
element_type = element_type.list.elementType
if element_type.which() == "struct":
return container.elementType
return None
def collect_schema(root: Any) -> dict[str, dict]:
structs: dict[str, dict] = {}
stack = [root]
while stack:
schema = stack.pop()
type_id = hex_id(schema.node.id)
if type_id in structs:
continue
structs[type_id] = encode_struct(schema)
for _name, field in schema.fields.items():
try:
child = _child_struct_schema(field)
except Exception:
child = None
if child is not None:
stack.append(child)
return structs
def load_log(cereal_dir: str) -> Any:
import capnp
cereal_dir = os.path.abspath(cereal_dir)
capnp.remove_import_hook()
return capnp.load(os.path.join(cereal_dir, "log.capnp"), imports=[cereal_dir])
def dump_schema(cereal_dir: str, path: str) -> None:
log = load_log(cereal_dir)
payload = {
"root": hex_id(log.Event.schema.node.id),
"structs": collect_schema(log.Event.schema),
}
with open(path, "w", encoding="utf-8") as handle:
json.dump(payload, handle, indent=2, sort_keys=True)
print(f"wrote schema dump with {len(payload['structs'])} structs to {path}")
def types_equal(a: dict, b: dict) -> bool:
if a.get("kind") != b.get("kind"):
return False
kind = a["kind"]
if kind in ("struct", "enum", "interface", "group"):
return a.get("typeId") == b.get("typeId")
if kind == "list":
return types_equal(a["element"], b["element"])
return True
def type_repr(t: dict) -> str:
kind = t.get("kind", "?")
if kind in ("struct", "enum", "interface", "group"):
return f"{kind}({t.get('typeId')})"
if kind == "list":
return f"list<{type_repr(t['element'])}>"
return kind
def field_is_union_variant(field: dict) -> bool:
return field.get("discriminant") is not None
def index_fields_by_ordinal(struct: dict) -> dict[int, dict]:
indexed: dict[int, dict] = {}
for field in struct["fields"]:
ordinal = field.get("ordinal")
if ordinal is None:
continue
indexed[ordinal] = field
return indexed
def compare(sunnypilot_dump: dict, upstream_dump: dict) -> list[str]:
violations: list[str] = []
sunnypilot_structs: dict[str, dict] = sunnypilot_dump["structs"]
upstream_structs: dict[str, dict] = upstream_dump["structs"]
sunnypilot_struct_referenced_from_shared: set[str] = set()
for type_id, sunnypilot_struct in sunnypilot_structs.items():
upstream_struct = upstream_structs.get(type_id)
if upstream_struct is None:
continue
sunnypilot_fields = index_fields_by_ordinal(sunnypilot_struct)
upstream_fields = index_fields_by_ordinal(upstream_struct)
display = sunnypilot_struct["displayName"]
for ordinal, sunnypilot_field in sunnypilot_fields.items():
upstream_field = upstream_fields.get(ordinal)
if upstream_field is None:
if field_is_union_variant(sunnypilot_field):
violations.append(
f"[R2] {display} @{ordinal} ('{sunnypilot_field['name']}', {type_repr(sunnypilot_field['type'])}): "
f"union variant not present upstream. upstream cannot parse this discriminant."
)
continue
if not types_equal(sunnypilot_field["type"], upstream_field["type"]):
violations.append(
f"[R1] {display} @{ordinal}: type mismatch. "
f"sunnypilot='{sunnypilot_field['name']}' {type_repr(sunnypilot_field['type'])} vs "
f"upstream='{upstream_field['name']}' {type_repr(upstream_field['type'])}."
)
continue
cursor = sunnypilot_field["type"]
while cursor.get("kind") == "list":
cursor = cursor["element"]
if cursor.get("kind") in ("struct", "group", "interface") and cursor.get("typeId"):
sunnypilot_struct_referenced_from_shared.add(cursor["typeId"])
for type_id, sunnypilot_struct in sunnypilot_structs.items():
if type_id in upstream_structs:
continue
if type_id in sunnypilot_struct_referenced_from_shared:
violations.append(
f"[R5] struct {sunnypilot_struct['displayName']} ({type_id}) exists only on sunnypilot "
f"but is referenced from an upstream-shared field. upstream cannot resolve this type."
)
return violations
def load_peer(path: str) -> dict:
with open(path, "r", encoding="utf-8") as handle:
return json.load(handle)
def run_read(cereal_dir: str, peer_path: str) -> int:
log = load_log(cereal_dir)
peer_dump = load_peer(peer_path)
local_dump = {
"root": hex_id(log.Event.schema.node.id),
"structs": collect_schema(log.Event.schema),
}
violations = compare(sunnypilot_dump=peer_dump, upstream_dump=local_dump)
if not violations:
print("cereal compat OK: upstream openpilot can parse sunnypilot routes "
"(no leaked structs, no ordinal collisions).")
return 0
print(f"cereal compat FAIL: upstream openpilot would misparse sunnypilot routes "
f"({len(violations)} violation(s)):")
for v in violations:
print(f" {v}")
return 1
def main() -> int:
parser = argparse.ArgumentParser(
description="sunnypilot <-> upstream cereal compatibility validator (schema-level)."
)
mode = parser.add_mutually_exclusive_group(required=True)
mode.add_argument("-g", "--generate", action="store_true", help="dump local schema to JSON")
mode.add_argument("-r", "--read", action="store_true", help="load peer JSON and diff against local")
parser.add_argument("-f", "--file", default="schema.json", help="JSON file path (default: schema.json)")
parser.add_argument("--cereal-dir", required=True, help="path to cereal directory containing log.capnp")
args = parser.parse_args()
if args.generate:
dump_schema(args.cereal_dir, args.file)
return 0
return run_read(args.cereal_dir, args.file)
if __name__ == "__main__":
sys.exit(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
@@ -41,7 +44,7 @@ _services: dict[str, tuple] = {
"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),
@@ -53,6 +56,7 @@ _services: dict[str, tuple] = {
"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 +75,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 +114,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())}
@@ -5,7 +5,6 @@ common_libs = [
'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)
@@ -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,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,8 +80,6 @@ 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}}, {"LateralManeuverMode", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
@@ -93,6 +92,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 +103,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,11 +132,11 @@ 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 --- //
{"AccelPersonality", {PERSISTENT | BACKUP, INT, std::to_string(static_cast<int>(cereal::LongitudinalPlanSP::AccelerationPersonality::NORMAL))}},
{"AccelPersonalityEnabled", {PERSISTENT | BACKUP, BOOL, "0"}},
{"ApiCache_DriveStats", {PERSISTENT, JSON}}, {"ApiCache_DriveStats", {PERSISTENT, JSON}},
{"AutoLaneChangeBsmDelay", {PERSISTENT | BACKUP, BOOL, "0"}}, {"AutoLaneChangeBsmDelay", {PERSISTENT | BACKUP, BOOL, "0"}},
{"AutoLaneChangeTimer", {PERSISTENT | BACKUP, INT, "0"}}, {"AutoLaneChangeTimer", {PERSISTENT | BACKUP, INT, "0"}},
@@ -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