mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-06-08 17:14:36 +08:00
Compare commits
22 Commits
paramwatch
...
ev9-new
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
664059c88e | ||
|
|
7ad7a9f4c0 | ||
|
|
74ba3fe110 | ||
|
|
09673eaa31 | ||
|
|
2c4013aacb | ||
|
|
719dce34cf | ||
|
|
39b53904ee | ||
|
|
a7165c0e4e | ||
|
|
1700540395 | ||
|
|
2b7b1d101c | ||
|
|
f1cfe0025d | ||
|
|
90631a1a50 | ||
|
|
4bf32c01b5 | ||
|
|
359425ce58 | ||
|
|
19a143ef81 | ||
|
|
eed4322fab | ||
|
|
9bf088b7e6 | ||
|
|
d2e93227db | ||
|
|
ab45ba682b | ||
|
|
af207483ba | ||
|
|
2569ff9802 | ||
|
|
122a5185b0 |
19
.clang-tidy
Normal file
19
.clang-tidy
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
Checks: '
|
||||
bugprone-*,
|
||||
-bugprone-integer-division,
|
||||
-bugprone-narrowing-conversions,
|
||||
performance-*,
|
||||
clang-analyzer-*,
|
||||
misc-*,
|
||||
-misc-unused-parameters,
|
||||
modernize-*,
|
||||
-modernize-avoid-c-arrays,
|
||||
-modernize-deprecated-headers,
|
||||
-modernize-use-auto,
|
||||
-modernize-use-using,
|
||||
-modernize-use-nullptr,
|
||||
-modernize-use-trailing-return-type,
|
||||
'
|
||||
CheckOptions:
|
||||
...
|
||||
@@ -1,6 +0,0 @@
|
||||
Wen
|
||||
REGIST
|
||||
PullRequest
|
||||
cancelled
|
||||
FOF
|
||||
NoO
|
||||
@@ -13,6 +13,27 @@
|
||||
*.o-*
|
||||
*.os
|
||||
*.os-*
|
||||
*.so
|
||||
*.a
|
||||
|
||||
venv/
|
||||
.venv/
|
||||
|
||||
notebooks
|
||||
phone
|
||||
massivemap
|
||||
neos
|
||||
installer
|
||||
chffr/app2
|
||||
chffr/backend/env
|
||||
selfdrive/nav
|
||||
selfdrive/baseui
|
||||
selfdrive/test/simulator2
|
||||
**/cache_data
|
||||
xx/plus
|
||||
xx/community
|
||||
xx/projects
|
||||
!xx/projects/eon_testing_master
|
||||
!xx/projects/map3d
|
||||
xx/ops
|
||||
xx/junk
|
||||
|
||||
@@ -5,7 +5,7 @@ end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{py,pyx,pxd}]
|
||||
[{*.py, *.pyx, *.pxd}]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
6
.gitattributes
vendored
6
.gitattributes
vendored
@@ -7,12 +7,12 @@
|
||||
*.png filter=lfs diff=lfs merge=lfs -text
|
||||
*.gif filter=lfs diff=lfs merge=lfs -text
|
||||
*.ttf filter=lfs diff=lfs merge=lfs -text
|
||||
*.otf filter=lfs diff=lfs merge=lfs -text
|
||||
*.wav filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
selfdrive/car/tests/test_models_segs.txt 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
|
||||
system/hardware/tici/updater filter=lfs diff=lfs merge=lfs -text
|
||||
selfdrive/ui/qt/spinner_larch64 filter=lfs diff=lfs merge=lfs -text
|
||||
selfdrive/ui/qt/text_larch64 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
|
||||
|
||||
11
.github/CODEOWNERS
vendored
11
.github/CODEOWNERS
vendored
@@ -1,11 +0,0 @@
|
||||
* @sunnypilot/dev-internal
|
||||
/.github/ @devtekve @sunnyhaibin
|
||||
/release/ci/ @devtekve @sunnyhaibin
|
||||
/tinygrad_repo @devtekve @Discountchubbs
|
||||
/tinygrad/ @devtekve @Discountchubbs
|
||||
/selfdrive/controls/lib/longitudinal_planner.py @devtekve @Discountchubbs
|
||||
/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py @devtekve @Discountchubbs
|
||||
/selfdrive/modeld/ @devtekve @Discountchubbs
|
||||
/sunnypilot/model* @devtekve @Discountchubbs
|
||||
/sunnypilot/sunnylink/ @devtekve
|
||||
/system/athena/ @devtekve
|
||||
12
.github/labeler.yaml
vendored
12
.github/labeler.yaml
vendored
@@ -1,10 +1,6 @@
|
||||
ci:
|
||||
CI / testing:
|
||||
- changed-files:
|
||||
- any-glob-to-all-files: "{.github/**,**/test_*,**/test/**,Jenkinsfile}"
|
||||
|
||||
chore:
|
||||
- changed-files:
|
||||
- any-glob-to-all-files: "{.github/**}"
|
||||
- any-glob-to-all-files: "{.github/**,**/test_*,Jenkinsfile}"
|
||||
|
||||
car:
|
||||
- changed-files:
|
||||
@@ -16,7 +12,7 @@ simulation:
|
||||
|
||||
ui:
|
||||
- changed-files:
|
||||
- any-glob-to-all-files: '{selfdrive/assets/**,selfdrive/ui/**,system/ui/**}'
|
||||
- any-glob-to-all-files: 'selfdrive/ui/**'
|
||||
|
||||
tools:
|
||||
- changed-files:
|
||||
@@ -28,4 +24,4 @@ multilanguage:
|
||||
|
||||
autonomy:
|
||||
- changed-files:
|
||||
- any-glob-to-all-files: "{selfdrive/modeld/models/**,selfdrive/test/process_replay/model_replay_ref_commit,sunnypilot/modeld*/models/**}"
|
||||
- any-glob-to-all-files: "{selfdrive/modeld/models/**,selfdrive/test/process_replay/model_replay_ref_commit}"
|
||||
|
||||
43
.github/release-drafter.yml
vendored
43
.github/release-drafter.yml
vendored
@@ -1,43 +0,0 @@
|
||||
exclude-labels:
|
||||
- 'no-changelog'
|
||||
categories:
|
||||
- title: '🚀 Features'
|
||||
labels:
|
||||
- 'feature'
|
||||
- 'enhancement'
|
||||
- title: '🐛 Bug Fixes'
|
||||
collapse-after: 5
|
||||
labels:
|
||||
- 'fix'
|
||||
- 'bugfix'
|
||||
- 'bug'
|
||||
- title: '🧰 Maintenance'
|
||||
collapse-after: 5
|
||||
label: 'chore'
|
||||
change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
|
||||
change-title-escapes: '\<*_&'
|
||||
replacers:
|
||||
- search: '/[Ss][Uu][Nn][Nn][Yy][Pp][Ii][Ll][Oo][Tt]/g'
|
||||
replace: 'sunnypilot'
|
||||
- search: '/\b[Ss][Pp]\b/g'
|
||||
replace: 'SP'
|
||||
version-resolver:
|
||||
major:
|
||||
labels:
|
||||
- 'major'
|
||||
minor:
|
||||
labels:
|
||||
- 'minor'
|
||||
patch:
|
||||
labels:
|
||||
- 'patch'
|
||||
default: patch
|
||||
name-template: 'v$RESOLVED_VERSION 🚀'
|
||||
tag-template: 'v$RESOLVED_VERSION'
|
||||
version-template: "0.$MAJOR.$MINOR.$PATCH" # The day OP becomes v1, we need to bump this
|
||||
tag-prefix: "v0." # The day OP becomes v1, we need to bump this
|
||||
prerelease-identifier: "staging"
|
||||
template: |
|
||||
## Changes
|
||||
|
||||
$CHANGES
|
||||
2
.github/workflows/auto-cache/action.yaml
vendored
2
.github/workflows/auto-cache/action.yaml
vendored
@@ -12,7 +12,7 @@ inputs:
|
||||
required: true
|
||||
save:
|
||||
description: 'whether to save the cache'
|
||||
default: 'true'
|
||||
default: 'false'
|
||||
required: false
|
||||
outputs:
|
||||
cache-hit:
|
||||
|
||||
78
.github/workflows/auto_pr_review.yaml
vendored
78
.github/workflows/auto_pr_review.yaml
vendored
@@ -1,7 +1,7 @@
|
||||
name: "PR review"
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [ opened, reopened, synchronize, edited ]
|
||||
types: [opened, reopened, synchronize, edited, edited]
|
||||
|
||||
jobs:
|
||||
labeler:
|
||||
@@ -9,7 +9,6 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -25,67 +24,30 @@ jobs:
|
||||
# Check PR target branch
|
||||
- name: check branch
|
||||
uses: Vankka/pr-target-branch-action@def32ec9d93514138d6ac0132ee62e120a72aed5
|
||||
if: github.repository == 'sunnypilot/sunnypilot'
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
target: /^(?!master$).*/
|
||||
exclude: /sunnypilot:.*/
|
||||
exclude: /commaai:.*/
|
||||
change-to: ${{ github.base_ref }}
|
||||
already-exists-action: close_this
|
||||
already-exists-comment: "Your PR should be made against the `master` branch"
|
||||
|
||||
update-pr-labels:
|
||||
name: Update fork's PR Labels
|
||||
runs-on: ubuntu-latest
|
||||
if: (github.event.pull_request.head.repo.fork && (contains(github.event_name, 'pull_request') && github.event.action == 'synchronize'))
|
||||
env:
|
||||
PR_LABEL: 'dev'
|
||||
TRUST_FORK_PR_LABEL: 'trust-fork-pr'
|
||||
steps:
|
||||
- name: Check if PR has dev label
|
||||
id: check-labels
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const prNumber = context.payload.pull_request.number;
|
||||
const { data: labels } = await github.rest.issues.listLabelsOnIssue({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber
|
||||
});
|
||||
|
||||
const hasDevC3Label = labels.some(label => label.name === process.env.PR_LABEL);
|
||||
const hasTrustLabel = labels.some(label => label.name === process.env.TRUST_FORK_PR_LABEL);
|
||||
|
||||
console.log(`PR #${prNumber} has ${process.env.PR_LABEL} label: ${hasDevC3Label}`);
|
||||
console.log(`PR #${prNumber} has ${process.env.TRUST_FORK_PR_LABEL} label: ${hasTrustLabel}`);
|
||||
|
||||
core.setOutput('has-dev', hasDevC3Label ? 'true' : 'false');
|
||||
core.setOutput('has-trust', hasTrustLabel ? 'true' : 'false');
|
||||
|
||||
- name: Remove trust-fork-pr label if present
|
||||
if: steps.check-labels.outputs.has-dev == 'true' && steps.check-labels.outputs.has-trust == 'true'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const prNumber = context.payload.pull_request.number;
|
||||
|
||||
await github.rest.issues.removeLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
name: process.env.TRUST_FORK_PR_LABEL
|
||||
});
|
||||
|
||||
console.log(`Removed '${process.env.TRUST_FORK_PR_LABEL}' label from PR #${prNumber} as it received new commits`);
|
||||
|
||||
// Add a comment to the PR
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
body: `The \`${process.env.TRUST_FORK_PR_LABEL}\` label has been automatically removed because new commits were pushed to this PR. This PR will need to be re-reviewed before the label can be applied again.`
|
||||
});
|
||||
# Welcome comment
|
||||
- name: "First timers PR"
|
||||
uses: actions/first-interaction@v1
|
||||
if: github.event.pull_request.head.repo.full_name != 'commaai/openpilot'
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
pr-message: |
|
||||
<!-- _(run_id **${{ github.run_id }}**)_ -->
|
||||
Thanks for contributing to openpilot! In order for us to review your PR as quickly as possible, check the following:
|
||||
* Convert your PR to a draft unless it's ready to review
|
||||
* Read the [contributing docs](https://github.com/commaai/openpilot/blob/master/docs/CONTRIBUTING.md)
|
||||
* Before marking as "ready for review", ensure:
|
||||
* the goal is clearly stated in the description
|
||||
* all the tests are passing
|
||||
* the change is [something we merge](https://github.com/commaai/openpilot/blob/master/docs/CONTRIBUTING.md#what-gets-merged)
|
||||
* include a route or your device' dongle ID if relevant
|
||||
|
||||
|
||||
10
.github/workflows/badges.yaml
vendored
10
.github/workflows/badges.yaml
vendored
@@ -5,15 +5,15 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
BASE_IMAGE: sunnypilot-base
|
||||
DOCKER_REGISTRY: ghcr.io/sunnypilot
|
||||
BASE_IMAGE: openpilot-base
|
||||
DOCKER_REGISTRY: ghcr.io/commaai
|
||||
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $DOCKER_REGISTRY/$BASE_IMAGE:latest /bin/bash -c
|
||||
|
||||
jobs:
|
||||
badges:
|
||||
name: create badges
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'sunnypilot/sunnypilot'
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
@@ -23,13 +23,13 @@ jobs:
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
- name: Push badges
|
||||
run: |
|
||||
${{ env.RUN }} "python3 selfdrive/ui/translations/create_badges.py"
|
||||
${{ env.RUN }} "scons -j$(nproc) && python3 selfdrive/ui/translations/create_badges.py"
|
||||
|
||||
rm .gitattributes
|
||||
|
||||
git checkout --orphan badges
|
||||
git rm -rf --cached .
|
||||
git config user.email "badge-researcher@sunnypilot.ai"
|
||||
git config user.email "badge-researcher@comma.ai"
|
||||
git config user.name "Badge Researcher"
|
||||
|
||||
git add translation_badge.svg
|
||||
|
||||
303
.github/workflows/build-all-tinygrad-models.yaml
vendored
303
.github/workflows/build-all-tinygrad-models.yaml
vendored
@@ -1,303 +0,0 @@
|
||||
name: Build and push all tinygrad models
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
set_min_version:
|
||||
description: 'Minimum selector version required for the models (see helpers.py or readme.md)'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
json_version: ${{ steps.get-json.outputs.json_version }}
|
||||
recompiled_dir: ${{ steps.create-recompiled-dir.outputs.recompiled_dir }}
|
||||
json_file: ${{ steps.get-json.outputs.json_file }}
|
||||
model_matrix: ${{ steps.set-matrix.outputs.model_matrix }}
|
||||
tinygrad_ref: ${{ steps.get-tinygrad-ref.outputs.tinygrad_ref }}
|
||||
steps:
|
||||
- name: Checkout sunnypilot repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: sunnypilot/sunnypilot
|
||||
path: sunnypilot
|
||||
submodules: recursive
|
||||
|
||||
- name: Get tinygrad_repo ref
|
||||
id: get-tinygrad-ref
|
||||
run: |
|
||||
cd sunnypilot
|
||||
export PYTHONPATH=$(pwd)
|
||||
ref=$(python3 sunnypilot/models/tinygrad_ref.py)
|
||||
echo "tinygrad_ref=$ref" >> $GITHUB_OUTPUT
|
||||
echo "tinygrad_ref is $ref"
|
||||
|
||||
- name: Checkout docs repo (sunnypilot-docs, gh-pages)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: sunnypilot/sunnypilot-docs
|
||||
ref: gh-pages
|
||||
path: docs
|
||||
ssh-key: ${{ secrets.CI_SUNNYPILOT_DOCS_PRIVATE_KEY }}
|
||||
|
||||
- name: Get next JSON version to use (from GitHub docs repo)
|
||||
id: get-json
|
||||
run: |
|
||||
cd docs/docs
|
||||
latest=$(ls driving_models_v*.json | sed -E 's/.*_v([0-9]+)\.json/\1/' | sort -n | tail -1)
|
||||
next=$((latest+1))
|
||||
json_file="driving_models_v${next}.json"
|
||||
cp "driving_models_v${latest}.json" "$json_file"
|
||||
echo "json_file=docs/docs/$json_file" >> $GITHUB_OUTPUT
|
||||
echo "json_version=$((next+0))" >> $GITHUB_OUTPUT
|
||||
echo "SRC_JSON_FILE=docs/docs/driving_models_v${latest}.json" >> $GITHUB_ENV
|
||||
|
||||
- name: Extract tinygrad models
|
||||
id: set-matrix
|
||||
working-directory: docs/docs
|
||||
run: |
|
||||
jq -c '[.bundles[] | select(.runner=="tinygrad") | {ref, display_name: (.display_name | gsub(" \\([^)]*\\)"; "")), is_20hz}]' "$(basename "${SRC_JSON_FILE}")" > matrix.json
|
||||
echo "model_matrix=$(cat matrix.json)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Set up SSH
|
||||
uses: webfactory/ssh-agent@v0.9.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.GITLAB_SSH_PRIVATE_KEY }}
|
||||
- run: |
|
||||
mkdir -p ~/.ssh
|
||||
ssh-keyscan -H gitlab.com >> ~/.ssh/known_hosts
|
||||
|
||||
- name: Clone GitLab docs repo and create new recompiled dir
|
||||
id: create-recompiled-dir
|
||||
env:
|
||||
GIT_SSH_COMMAND: 'ssh -o UserKnownHostsFile=~/.ssh/known_hosts'
|
||||
run: |
|
||||
git clone --depth 1 --filter=tree:0 --sparse git@gitlab.com:sunnypilot/public/${{ vars.MODELS_GITLAB }} gitlab_docs
|
||||
cd gitlab_docs
|
||||
git checkout main
|
||||
git sparse-checkout set --no-cone models/
|
||||
cd models
|
||||
latest_dir=$(ls -d recompiled* 2>/dev/null | sed -E 's/recompiled([0-9]+)/\1/' | sort -n | tail -1)
|
||||
if [[ -z "$latest_dir" ]]; then
|
||||
next_dir=1
|
||||
else
|
||||
next_dir=$((latest_dir+1))
|
||||
fi
|
||||
recompiled_dir="${next_dir}"
|
||||
mkdir -p "recompiled${recompiled_dir}"
|
||||
touch "recompiled${recompiled_dir}/.gitkeep"
|
||||
cd ../..
|
||||
echo "recompiled_dir=$recompiled_dir" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Push empty recompiled dir to GitLab
|
||||
run: |
|
||||
cd gitlab_docs
|
||||
git add models/recompiled${{ steps.create-recompiled-dir.outputs.recompiled_dir }}
|
||||
git config --global user.name "GitHub Action"
|
||||
git config --global user.email "action@github.com"
|
||||
git commit -m "Add recompiled${{ steps.create-recompiled-dir.outputs.recompiled_dir }} for build-all" || echo "No changes to commit"
|
||||
git push origin main
|
||||
|
||||
- name: Push new JSON to GitHub docs repo
|
||||
run: |
|
||||
cd docs
|
||||
git pull origin gh-pages
|
||||
git add docs/"$(basename ${{ steps.get-json.outputs.json_file }})"
|
||||
git config --global user.name "GitHub Action"
|
||||
git config --global user.email "action@github.com"
|
||||
git commit -m "Add new ${{ steps.get-json.outputs.json_file }} for build-all" || echo "No changes to commit"
|
||||
git push origin gh-pages
|
||||
|
||||
get_and_build:
|
||||
needs: [setup]
|
||||
strategy:
|
||||
matrix:
|
||||
model: ${{ fromJson(needs.setup.outputs.model_matrix) }}
|
||||
fail-fast: false
|
||||
uses: ./.github/workflows/build-single-tinygrad-model.yaml
|
||||
with:
|
||||
upstream_branch: ${{ matrix.model.ref }}
|
||||
custom_name: ${{ matrix.model.display_name }}
|
||||
recompiled_dir: ${{ needs.setup.outputs.recompiled_dir }}
|
||||
json_version: ${{ needs.setup.outputs.json_version }}
|
||||
secrets: inherit
|
||||
|
||||
retry_failed_models:
|
||||
needs: [setup, get_and_build]
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ needs.setup.result != 'failure' && !cancelled() }}
|
||||
outputs:
|
||||
retry_matrix: ${{ steps.set-retry-matrix.outputs.retry_matrix }}
|
||||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: model-*
|
||||
path: output
|
||||
|
||||
- id: set-retry-matrix
|
||||
run: |
|
||||
echo '${{ needs.setup.outputs.model_matrix }}' > matrix.json
|
||||
built=(); while IFS= read -r line; do built+=("$line"); done < <(
|
||||
ls output | sed -E 's/^model-//' | sed -E 's/-[0-9]+$//' | sed -E 's/ \([^)]*\)//' | awk '{gsub(/^ +| +$/, ""); print}'
|
||||
)
|
||||
jq -c --argjson built "$(printf '%s\n' "${built[@]}" | jq -R . | jq -s .)" \
|
||||
'map(select(.display_name as $n | ($built | index($n | gsub("^ +| +$"; "")) | not)))' matrix.json > retry_matrix.json
|
||||
echo "retry_matrix=$(cat retry_matrix.json)" >> $GITHUB_OUTPUT
|
||||
|
||||
retry_get_and_build:
|
||||
needs: [setup, get_and_build, retry_failed_models]
|
||||
if: ${{ needs.get_and_build.result == 'failure' || (needs.retry_failed_models.outputs.retry_matrix != '[]' && needs.retry_failed_models.outputs.retry_matrix != '') }}
|
||||
strategy:
|
||||
matrix:
|
||||
model: ${{ fromJson(needs.retry_failed_models.outputs.retry_matrix) }}
|
||||
fail-fast: false
|
||||
uses: ./.github/workflows/build-single-tinygrad-model.yaml
|
||||
with:
|
||||
upstream_branch: ${{ matrix.model.ref }}
|
||||
custom_name: ${{ matrix.model.display_name }}
|
||||
recompiled_dir: ${{ needs.setup.outputs.recompiled_dir }}
|
||||
json_version: ${{ needs.setup.outputs.json_version }}
|
||||
artifact_suffix: -retry
|
||||
secrets: inherit
|
||||
|
||||
publish_models:
|
||||
name: Publish models sequentially
|
||||
needs: [setup, get_and_build, retry_failed_models, retry_get_and_build]
|
||||
if: ${{ !cancelled() && (needs.get_and_build.result != 'failure' || needs.retry_get_and_build.result == 'success' || (needs.retry_failed_models.outputs.retry_matrix != '[]' && needs.retry_failed_models.outputs.retry_matrix != '')) }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
max-parallel: 1
|
||||
matrix:
|
||||
model: ${{ fromJson(needs.setup.outputs.model_matrix) }}
|
||||
env:
|
||||
RECOMPILED_DIR: recompiled${{ needs.setup.outputs.recompiled_dir }}
|
||||
JSON_FILE: ${{ needs.setup.outputs.json_file }}
|
||||
ARTIFACT_NAME_INPUT: ${{ matrix.model.display_name }}
|
||||
steps:
|
||||
- name: Set up SSH
|
||||
uses: webfactory/ssh-agent@v0.9.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.GITLAB_SSH_PRIVATE_KEY }}
|
||||
|
||||
- name: Add GitLab.com SSH key to known_hosts
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
ssh-keyscan -H gitlab.com >> ~/.ssh/known_hosts
|
||||
|
||||
- name: Clone GitLab docs repo
|
||||
env:
|
||||
GIT_SSH_COMMAND: 'ssh -o UserKnownHostsFile=~/.ssh/known_hosts'
|
||||
run: |
|
||||
echo "Cloning GitLab"
|
||||
git clone --depth 1 --filter=tree:0 --sparse git@gitlab.com:sunnypilot/public/${{ vars.MODELS_GITLAB }} gitlab_docs
|
||||
cd gitlab_docs
|
||||
echo "checkout models/${RECOMPILED_DIR}"
|
||||
git sparse-checkout set --no-cone models/${RECOMPILED_DIR}
|
||||
git checkout main
|
||||
cd ..
|
||||
|
||||
- name: Checkout docs repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: sunnypilot/sunnypilot-docs
|
||||
ref: gh-pages
|
||||
path: docs
|
||||
ssh-key: ${{ secrets.CI_SUNNYPILOT_DOCS_PRIVATE_KEY }}
|
||||
|
||||
- name: Validate recompiled dir and JSON version
|
||||
run: |
|
||||
if [ ! -d "gitlab_docs/models/$RECOMPILED_DIR" ]; then
|
||||
echo "Recompiled dir $RECOMPILED_DIR does not exist in GitLab repo"
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -f "$JSON_FILE" ]; then
|
||||
echo "JSON file $JSON_FILE does not exist!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Download artifact name file
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: artifact-name-${{ env.ARTIFACT_NAME_INPUT }}
|
||||
path: artifact_name
|
||||
|
||||
- name: Read artifact name
|
||||
id: read-artifact-name
|
||||
run: |
|
||||
ARTIFACT_NAME=$(cat artifact_name/artifact_name.txt)
|
||||
echo "artifact_name=$ARTIFACT_NAME" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Download model artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ${{ steps.read-artifact-name.outputs.artifact_name }}
|
||||
path: output
|
||||
|
||||
- name: Remove onnx files bc not needed for recompiled dir since they already exist from single build
|
||||
run: |
|
||||
find output -type f -name '*.onnx' -delete
|
||||
find output -type f -name 'big_*.pkl' -delete
|
||||
find output -type f -name 'dmonitoring_model_tinygrad.pkl' -delete
|
||||
|
||||
- name: Copy model artifacts to gitlab
|
||||
env:
|
||||
ARTIFACT_NAME: ${{ steps.read-artifact-name.outputs.artifact_name }}
|
||||
run: |
|
||||
ARTIFACT_DIR="gitlab_docs/models/${RECOMPILED_DIR}/${ARTIFACT_NAME}"
|
||||
mkdir -p "$ARTIFACT_DIR"
|
||||
for path in output/*; do
|
||||
if [ "$(basename "$path")" = "artifact_name.txt" ]; then
|
||||
continue
|
||||
fi
|
||||
name="$(basename "$path")"
|
||||
if [ -d "$path" ]; then
|
||||
mkdir -p "$ARTIFACT_DIR/$name"
|
||||
cp -r "$path"/* "$ARTIFACT_DIR/$name/"
|
||||
echo "Copied dir $name -> $ARTIFACT_DIR/$name"
|
||||
else
|
||||
cp "$path" "$ARTIFACT_DIR/"
|
||||
echo "Copied file $name -> $ARTIFACT_DIR/"
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Push recompiled dir to GitLab
|
||||
env:
|
||||
GITLAB_SSH_PRIVATE_KEY: ${{ secrets.GITLAB_SSH_PRIVATE_KEY }}
|
||||
run: |
|
||||
cd gitlab_docs
|
||||
git checkout main
|
||||
git pull origin main
|
||||
for d in models/"$RECOMPILED_DIR"/*/; do
|
||||
git sparse-checkout add "$d"
|
||||
done
|
||||
git add models/"$RECOMPILED_DIR"
|
||||
git config --global user.name "GitHub Action"
|
||||
git config --global user.email "action@github.com"
|
||||
git commit -m "Update $RECOMPILED_DIR with model from build-all-tinygrad-models" || echo "No changes to commit"
|
||||
git push origin main
|
||||
- run: |
|
||||
cd docs
|
||||
git pull origin gh-pages
|
||||
|
||||
- name: update json
|
||||
run: |
|
||||
ARGS=""
|
||||
[ -n "${{ inputs.set_min_version }}" ] && ARGS="$ARGS --set-min-version \"${{ inputs.set_min_version }}\""
|
||||
ARGS="$ARGS --sort-by-date"
|
||||
ARGS="$ARGS --tinygrad-ref \"${{ needs.setup.outputs.tinygrad_ref }}\""
|
||||
eval python3 docs/json_parser.py \
|
||||
--json-path "$JSON_FILE" \
|
||||
--recompiled-dir "gitlab_docs/models/$RECOMPILED_DIR" \
|
||||
$ARGS
|
||||
|
||||
- name: Push updated json to GitHub
|
||||
run: |
|
||||
cd docs
|
||||
git config --global user.name "GitHub Action"
|
||||
git config --global user.email "action@github.com"
|
||||
git checkout gh-pages
|
||||
git add docs/"$(basename $JSON_FILE)"
|
||||
git commit -m "Update $(basename $JSON_FILE) after recompiling model" || echo "No changes to commit"
|
||||
git push origin gh-pages
|
||||
228
.github/workflows/build-single-tinygrad-model.yaml
vendored
228
.github/workflows/build-single-tinygrad-model.yaml
vendored
@@ -1,228 +0,0 @@
|
||||
name: Build Single Tinygrad Model and Push
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
upstream_branch:
|
||||
description: 'Upstream commit to build from'
|
||||
required: true
|
||||
type: string
|
||||
custom_name:
|
||||
description: 'Custom name for the model (no date, only name)'
|
||||
required: false
|
||||
type: string
|
||||
recompiled_dir:
|
||||
description: 'Existing recompiled directory number (e.g. 3 for recompiled3)'
|
||||
required: true
|
||||
type: string
|
||||
json_version:
|
||||
description: 'driving_models version number to update (e.g. 5 for driving_models_v5.json)'
|
||||
required: true
|
||||
type: string
|
||||
artifact_suffix:
|
||||
description: 'Suffix for artifact name'
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
bypass_push:
|
||||
description: 'Bypass pushing to GitLab for build-all'
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
upstream_branch:
|
||||
description: 'Upstream commit to build from'
|
||||
required: true
|
||||
type: string
|
||||
custom_name:
|
||||
description: 'Custom name for the model (no date, only name)'
|
||||
required: false
|
||||
type: string
|
||||
recompiled_dir:
|
||||
description: 'Existing recompiled directory number (e.g. 3 for recompiled3)'
|
||||
required: true
|
||||
type: string
|
||||
json_version:
|
||||
description: 'driving_models version number to update (e.g. 5 for driving_models_v5.json)'
|
||||
required: true
|
||||
type: string
|
||||
model_folder:
|
||||
description: 'Model folder'
|
||||
type: choice
|
||||
default: 'None'
|
||||
options:
|
||||
- None
|
||||
- Simple Plan Models
|
||||
- Space Lab Models
|
||||
- TR Models
|
||||
- DTR Models
|
||||
- Custom Merge Models
|
||||
- FOF series models
|
||||
- Other
|
||||
custom_model_folder:
|
||||
description: 'Custom model folder name (if "Other" selected)'
|
||||
required: false
|
||||
type: string
|
||||
generation:
|
||||
description: 'Model generation'
|
||||
required: false
|
||||
type: string
|
||||
version:
|
||||
description: 'Minimum selector version'
|
||||
required: false
|
||||
type: string
|
||||
env:
|
||||
RECOMPILED_DIR: recompiled${{ inputs.recompiled_dir }}
|
||||
JSON_FILE: docs/docs/driving_models_v${{ inputs.json_version }}.json
|
||||
|
||||
jobs:
|
||||
build_model:
|
||||
uses: ./.github/workflows/sunnypilot-build-model.yaml
|
||||
with:
|
||||
upstream_branch: ${{ inputs.upstream_branch }}
|
||||
custom_name: ${{ inputs.custom_name || inputs.upstream_branch }}
|
||||
is_20hz: true
|
||||
artifact_suffix: ${{ inputs.artifact_suffix }}
|
||||
secrets: inherit
|
||||
|
||||
publish_model:
|
||||
if: ${{ !inputs.bypass_push && !cancelled() }}
|
||||
concurrency:
|
||||
group: gitlab-push-${{ inputs.recompiled_dir }}
|
||||
cancel-in-progress: false
|
||||
needs: build_model
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up SSH
|
||||
uses: webfactory/ssh-agent@v0.9.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.GITLAB_SSH_PRIVATE_KEY }}
|
||||
|
||||
- name: Add GitLab.com SSH key to known_hosts
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
ssh-keyscan -H gitlab.com >> ~/.ssh/known_hosts
|
||||
|
||||
- name: Clone GitLab docs repo
|
||||
env:
|
||||
GIT_SSH_COMMAND: 'ssh -o UserKnownHostsFile=~/.ssh/known_hosts'
|
||||
run: |
|
||||
echo "Cloning GitLab"
|
||||
git clone --depth 1 --filter=tree:0 --sparse git@gitlab.com:sunnypilot/public/${{ vars.MODELS_GITLAB }} gitlab_docs
|
||||
cd gitlab_docs
|
||||
echo "checkout models/${RECOMPILED_DIR}"
|
||||
git sparse-checkout set --no-cone models/${RECOMPILED_DIR}
|
||||
git checkout main
|
||||
cd ..
|
||||
|
||||
- name: Checkout docs repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: sunnypilot/sunnypilot-docs
|
||||
ref: gh-pages
|
||||
path: docs
|
||||
ssh-key: ${{ secrets.CI_SUNNYPILOT_DOCS_PRIVATE_KEY }}
|
||||
|
||||
- name: Validate recompiled dir and JSON version
|
||||
run: |
|
||||
if [ ! -d "gitlab_docs/models/$RECOMPILED_DIR" ]; then
|
||||
echo "Recompiled dir $RECOMPILED_DIR does not exist in GitLab repo"
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -f "$JSON_FILE" ]; then
|
||||
echo "JSON file $JSON_FILE does not exist!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Download artifact name file
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: artifact-name-${{ inputs.custom_name || inputs.upstream_branch }}
|
||||
path: artifact_name
|
||||
|
||||
- name: Read artifact name
|
||||
id: read-artifact-name
|
||||
run: |
|
||||
ARTIFACT_NAME=$(cat artifact_name/artifact_name.txt)
|
||||
echo "artifact_name=$ARTIFACT_NAME" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Download and extract model artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ${{ steps.read-artifact-name.outputs.artifact_name }}
|
||||
path: output
|
||||
|
||||
- name: Remove unwanted files
|
||||
run: |
|
||||
find output -type f -name 'dmonitoring_model_tinygrad.pkl' -delete
|
||||
find output -type f -name 'dmonitoring_model.onnx' -delete
|
||||
|
||||
- name: Copy model artifact(s) to GitLab recompiled dir
|
||||
env:
|
||||
ARTIFACT_NAME: ${{ steps.read-artifact-name.outputs.artifact_name }}
|
||||
run: |
|
||||
ARTIFACT_DIR="gitlab_docs/models/${RECOMPILED_DIR}/${ARTIFACT_NAME}"
|
||||
mkdir -p "$ARTIFACT_DIR"
|
||||
for path in output/*; do
|
||||
if [ "$(basename "$path")" = "artifact_name.txt" ]; then
|
||||
continue
|
||||
fi
|
||||
name="$(basename "$path")"
|
||||
if [ -d "$path" ]; then
|
||||
mkdir -p "$ARTIFACT_DIR/$name"
|
||||
cp -r "$path"/* "$ARTIFACT_DIR/$name/"
|
||||
echo "Copied dir $name -> $ARTIFACT_DIR/$name"
|
||||
else
|
||||
cp "$path" "$ARTIFACT_DIR/"
|
||||
echo "Copied file $name -> $ARTIFACT_DIR/"
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Push recompiled dir to GitLab
|
||||
env:
|
||||
GITLAB_SSH_PRIVATE_KEY: ${{ secrets.GITLAB_SSH_PRIVATE_KEY }}
|
||||
run: |
|
||||
cd gitlab_docs
|
||||
git checkout main
|
||||
git pull origin main
|
||||
for d in models/"$RECOMPILED_DIR"/*/; do
|
||||
git sparse-checkout add "$d"
|
||||
done
|
||||
git add models/"$RECOMPILED_DIR"
|
||||
git config --global user.name "GitHub Action"
|
||||
git config --global user.email "action@github.com"
|
||||
git commit -m "Create/Update $RECOMPILED_DIR with new/updated model from build-single-tinygrad-model" || echo "No changes to commit"
|
||||
git push origin main
|
||||
|
||||
- run: |
|
||||
cd docs
|
||||
git pull origin gh-pages
|
||||
|
||||
- name: Run json_parser.py to update JSON
|
||||
run: |
|
||||
FOLDER="${{ inputs.model_folder }}"
|
||||
if [ "$FOLDER" = "Other" ]; then
|
||||
FOLDER="${{ inputs.custom_model_folder }}"
|
||||
fi
|
||||
ARGS=""
|
||||
if [ "$FOLDER" != "None" ] && [ -n "$FOLDER" ]; then
|
||||
ARGS="$ARGS --model-folder \"$FOLDER\""
|
||||
fi
|
||||
[ -n "${{ inputs.generation }}" ] && ARGS="$ARGS --generation \"${{ inputs.generation }}\""
|
||||
[ -n "${{ inputs.version }}" ] && ARGS="$ARGS --version \"${{ inputs.version }}\""
|
||||
eval python3 docs/json_parser.py \
|
||||
--json-path "$JSON_FILE" \
|
||||
--recompiled-dir "gitlab_docs/models/$RECOMPILED_DIR" \
|
||||
--sort-by-date \
|
||||
$ARGS
|
||||
|
||||
- name: Push updated JSON to GitHub docs repo
|
||||
run: |
|
||||
cd docs
|
||||
git config --global user.name "GitHub Action"
|
||||
git config --global user.email "action@github.com"
|
||||
git checkout gh-pages
|
||||
git add docs/"$(basename $JSON_FILE)"
|
||||
git commit -m "Update $(basename $JSON_FILE) after recompiling model" || echo "No changes to commit"
|
||||
git push origin gh-pages
|
||||
76
.github/workflows/cereal_validation.yaml
vendored
76
.github/workflows/cereal_validation.yaml
vendored
@@ -1,76 +0,0 @@
|
||||
name: cereal validation
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
paths:
|
||||
- 'cereal/**'
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
inputs:
|
||||
run_number:
|
||||
default: '1'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
concurrency:
|
||||
group: cereal-validation-ci-run-${{ inputs.run_number }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
PYTHONWARNINGS: error
|
||||
BASE_IMAGE: openpilot-base
|
||||
BUILD: selfdrive/test/docker_build.sh base
|
||||
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c
|
||||
|
||||
jobs:
|
||||
generate_cereal_artifact:
|
||||
name: Generate cereal validation artifacts
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
- name: Build openpilot
|
||||
run: ${{ env.RUN }} "scons -j$(nproc) cereal"
|
||||
- name: Generate the log file
|
||||
run: |
|
||||
${{ env.RUN }} "cereal/messaging/tests/validate_sp_cereal_upstream.py -g -f schema_instances.bin" && \
|
||||
ls -la
|
||||
ls -la cereal/messaging/tests
|
||||
- name: 'Prepare artifact'
|
||||
run: |
|
||||
mkdir -p "cereal/messaging/tests/cereal_validations"
|
||||
cp cereal/messaging/tests/validate_sp_cereal_upstream.py "cereal/messaging/tests/cereal_validations/validate_sp_cereal_upstream.py"
|
||||
cp schema_instances.bin "cereal/messaging/tests/cereal_validations/schema_instances.bin"
|
||||
- name: 'Upload Artifact'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cereal_validations
|
||||
path: cereal/messaging/tests/cereal_validations
|
||||
|
||||
validate_cereal_with_upstream:
|
||||
name: Validate cereal with Upstream
|
||||
runs-on: ubuntu-24.04
|
||||
needs: generate_cereal_artifact
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'commaai/openpilot'
|
||||
submodules: true
|
||||
ref: "refs/heads/master"
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
- name: Build openpilot
|
||||
run: ${{ env.RUN }} "scons -j$(nproc) cereal"
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: cereal_validations
|
||||
path: cereal/messaging/tests/cereal_validations
|
||||
- name: 'Run the validation'
|
||||
run: |
|
||||
chmod +x cereal/messaging/tests/cereal_validations/validate_sp_cereal_upstream.py
|
||||
${{ env.RUN }} "cereal/messaging/tests/cereal_validations/validate_sp_cereal_upstream.py -r -f cereal/messaging/tests/cereal_validations/schema_instances.bin"
|
||||
6
.github/workflows/ci_weekly_report.yaml
vendored
6
.github/workflows/ci_weekly_report.yaml
vendored
@@ -15,7 +15,7 @@ env:
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
if: github.repository == 'sunnypilot/sunnypilot'
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
ci_runs: ${{ steps.ci_runs_setup.outputs.matrix }}
|
||||
@@ -31,14 +31,14 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{fromJSON(needs.setup.outputs.ci_runs)}}
|
||||
uses: sunnypilot/sunnypilot/.github/workflows/ci_weekly_run.yaml@master
|
||||
uses: commaai/openpilot/.github/workflows/ci_weekly_run.yaml@master
|
||||
with:
|
||||
run_number: ${{ matrix.run_number }}
|
||||
|
||||
report:
|
||||
needs: [ci_matrix_run]
|
||||
runs-on: ubuntu-latest
|
||||
if: always() && github.repository == 'commaai/openpilot'
|
||||
if: always()
|
||||
steps:
|
||||
- name: Get job results
|
||||
uses: actions/github-script@v7
|
||||
|
||||
4
.github/workflows/ci_weekly_run.yaml
vendored
4
.github/workflows/ci_weekly_run.yaml
vendored
@@ -11,7 +11,7 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
uses: sunnypilot/sunnypilot/.github/workflows/tests.yaml@master
|
||||
selfdrive_tests:
|
||||
uses: commaai/openpilot/.github/workflows/selfdrive_tests.yaml@master
|
||||
with:
|
||||
run_number: ${{ inputs.run_number }}
|
||||
|
||||
6
.github/workflows/docs.yaml
vendored
6
.github/workflows/docs.yaml
vendored
@@ -35,13 +35,13 @@ jobs:
|
||||
|
||||
# Push to docs.comma.ai
|
||||
- uses: actions/checkout@v4
|
||||
if: github.ref == 'refs/heads/master' && github.repository == 'sunnypilot/sunnypilot'
|
||||
if: github.ref == 'refs/heads/master' && github.repository == 'commaai/openpilot'
|
||||
with:
|
||||
path: openpilot-docs
|
||||
ssh-key: ${{ secrets.OPENPILOT_DOCS_KEY }}
|
||||
repository: sunnypilot/sunnypilot-docs
|
||||
repository: commaai/openpilot-docs
|
||||
- name: Push
|
||||
if: github.ref == 'refs/heads/master' && github.repository == 'sunnypilot/sunnypilot'
|
||||
if: github.ref == 'refs/heads/master' && github.repository == 'commaai/openpilot'
|
||||
run: |
|
||||
set -x
|
||||
|
||||
|
||||
14
.github/workflows/jenkins-pr-trigger.yaml
vendored
14
.github/workflows/jenkins-pr-trigger.yaml
vendored
@@ -9,9 +9,6 @@ jobs:
|
||||
scan-comments:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.issue.pull_request }}
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
steps:
|
||||
- name: Check for trigger phrase
|
||||
id: check_comment
|
||||
@@ -46,14 +43,3 @@ jobs:
|
||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git checkout -b tmp-jenkins-${{ github.event.issue.number }}
|
||||
GIT_LFS_SKIP_PUSH=1 git push -f origin tmp-jenkins-${{ github.event.issue.number }}
|
||||
|
||||
- name: Delete trigger comment
|
||||
if: steps.check_comment.outputs.result == 'true' && always()
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
await github.rest.issues.deleteComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: context.payload.comment.id,
|
||||
});
|
||||
|
||||
72
.github/workflows/lfs-maintenance.yaml
vendored
72
.github/workflows/lfs-maintenance.yaml
vendored
@@ -1,72 +0,0 @@
|
||||
name: Sync comma's LFS
|
||||
|
||||
env:
|
||||
LFS_URL: 'https://gitlab.com/sunnypilot/public/sunnypilot-new-lfs.git/info/lfs'
|
||||
LFS_PUSH_URL: 'ssh://git@gitlab.com/sunnypilot/public/sunnypilot-new-lfs.git'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *' # Runs at 00:00 UTC every day
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'master'
|
||||
workflow_dispatch: # enables manual triggering
|
||||
inputs:
|
||||
upstream_branch:
|
||||
default: 'master'
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
# Skip if PR is in draft mode
|
||||
if: (github.event_name != 'pull_request' || (github.event_name == 'pull_request' && github.event.pull_request.draft == false)) && !github.event.pull_request.head.repo.fork
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'commaai/openpilot'
|
||||
ref: ${{ inputs.upstream_branch }}
|
||||
|
||||
- name: LFS Fetch
|
||||
run: |
|
||||
git lfs fetch
|
||||
|
||||
- name: Set up Git
|
||||
run: |
|
||||
git config --global user.name 'GitHub Action'
|
||||
git config --global user.email 'action@github.com'
|
||||
|
||||
- name: Set up SSH
|
||||
uses: webfactory/ssh-agent@v0.9.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||
|
||||
- name: Add GitLab public keys
|
||||
run: |
|
||||
ssh-keyscan -H gitlab.com >> ~/.ssh/known_hosts
|
||||
|
||||
- name: Ensure branch
|
||||
run: |
|
||||
if git symbolic-ref -q HEAD >/dev/null; then
|
||||
echo "Already on a branch, proceeding with push"
|
||||
else
|
||||
echo "Detached HEAD state detected, creating temporary branch"
|
||||
git checkout -b temp_branch
|
||||
fi
|
||||
|
||||
- name: Update LFS Config
|
||||
run: |
|
||||
echo '[lfs]' > .lfsconfig
|
||||
echo ' url = ${{ env.LFS_URL }}' >> .lfsconfig
|
||||
echo ' pushurl = ${{ env.LFS_PUSH_URL }}' >> .lfsconfig
|
||||
echo ' locksverify = false' >> .lfsconfig
|
||||
|
||||
- name: Push LFS
|
||||
id: sync-and-commit
|
||||
run: |
|
||||
git lfs ls-files -l
|
||||
git lfs push --all origin
|
||||
151
.github/workflows/mici_raylib_ui_preview.yaml
vendored
151
.github/workflows/mici_raylib_ui_preview.yaml
vendored
@@ -1,151 +0,0 @@
|
||||
name: "mici raylib ui preview"
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request_target:
|
||||
types: [assigned, opened, synchronize, reopened, edited]
|
||||
branches:
|
||||
- 'master'
|
||||
paths:
|
||||
- 'selfdrive/assets/**'
|
||||
- 'selfdrive/ui/**'
|
||||
- 'system/ui/**'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
UI_JOB_NAME: "Create mici raylib UI Report"
|
||||
REPORT_NAME: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
||||
SHA: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.sha || github.event.pull_request.head.sha }}
|
||||
BRANCH_NAME: "openpilot/pr-${{ github.event.number }}-mici-raylib-ui"
|
||||
MASTER_BRANCH_NAME: "openpilot_master_ui_mici_raylib"
|
||||
# All report files are pushed here
|
||||
REPORT_FILES_BRANCH_NAME: "mici-raylib-ui-reports"
|
||||
|
||||
jobs:
|
||||
preview:
|
||||
if: github.repository == 'sunnypilot/sunnypilot'
|
||||
name: preview
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
actions: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Waiting for ui generation to end
|
||||
uses: lewagon/wait-on-check-action@v1.3.4
|
||||
with:
|
||||
ref: ${{ env.SHA }}
|
||||
check-name: ${{ env.UI_JOB_NAME }}
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
allowed-conclusions: success
|
||||
wait-interval: 20
|
||||
|
||||
- name: Getting workflow run ID
|
||||
id: get_run_id
|
||||
run: |
|
||||
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 # filename: pr_ui/mici_ui_replay.mp4
|
||||
id: download-artifact
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run_id: ${{ steps.get_run_id.outputs.run_id }}
|
||||
search_artifacts: true
|
||||
name: mici-raylib-report-1-${{ env.REPORT_NAME }}
|
||||
path: ${{ github.workspace }}/pr_ui
|
||||
|
||||
- name: Getting master ui # filename: master_ui_raylib/mici_ui_replay.mp4
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: sunnypilot/ci-artifacts
|
||||
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
||||
path: ${{ github.workspace }}/master_ui_raylib
|
||||
ref: ${{ env.MASTER_BRANCH_NAME }}
|
||||
|
||||
- name: Saving new master ui
|
||||
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
|
||||
working-directory: ${{ github.workspace }}/master_ui_raylib
|
||||
run: |
|
||||
git checkout --orphan=new_master_ui_mici_raylib
|
||||
git rm -rf *
|
||||
git branch -D ${{ env.MASTER_BRANCH_NAME }}
|
||||
git branch -m ${{ env.MASTER_BRANCH_NAME }}
|
||||
git config user.name "GitHub Actions Bot"
|
||||
git config user.email "<>"
|
||||
mv ${{ github.workspace }}/pr_ui/* .
|
||||
git add .
|
||||
git commit -m "mici raylib video for commit ${{ env.SHA }}"
|
||||
git push origin ${{ env.MASTER_BRANCH_NAME }} --force
|
||||
|
||||
- name: Setup FFmpeg
|
||||
uses: AnimMouse/setup-ffmpeg@ae28d57dabbb148eff63170b6bf7f2b60062cbae
|
||||
|
||||
- name: Finding diff
|
||||
if: github.event_name == 'pull_request_target'
|
||||
id: find_diff
|
||||
run: |
|
||||
# Find the video file from PR
|
||||
pr_video="${{ github.workspace }}/pr_ui/mici_ui_replay_proposed.mp4"
|
||||
mv "${{ github.workspace }}/pr_ui/mici_ui_replay.mp4" "$pr_video"
|
||||
|
||||
master_video="${{ github.workspace }}/pr_ui/mici_ui_replay_master.mp4"
|
||||
mv "${{ github.workspace }}/master_ui_raylib/mici_ui_replay.mp4" "$master_video"
|
||||
|
||||
# Run report
|
||||
export PYTHONPATH=${{ github.workspace }}
|
||||
baseurl="https://github.com/sunnypilot/ci-artifacts/raw/refs/heads/${{ env.BRANCH_NAME }}"
|
||||
diff_exit_code=0
|
||||
python3 ${{ github.workspace }}/selfdrive/ui/tests/diff/diff.py "${{ github.workspace }}/pr_ui/mici_ui_replay_master.mp4" "${{ github.workspace }}/pr_ui/mici_ui_replay_proposed.mp4" "diff.html" --basedir "$baseurl" --no-open || diff_exit_code=$?
|
||||
|
||||
# Copy diff report files
|
||||
cp ${{ github.workspace }}/selfdrive/ui/tests/diff/report/diff.html ${{ github.workspace }}/pr_ui/
|
||||
cp ${{ github.workspace }}/selfdrive/ui/tests/diff/report/diff.mp4 ${{ github.workspace }}/pr_ui/
|
||||
|
||||
REPORT_URL="https://sunnypilot.github.io/ci-artifacts/diff_pr_${{ github.event.number }}.html"
|
||||
if [ $diff_exit_code -eq 0 ]; then
|
||||
DIFF="✅ Videos are identical! [View Diff Report]($REPORT_URL)"
|
||||
else
|
||||
DIFF="❌ <strong>Videos differ!</strong> [View Diff Report]($REPORT_URL)"
|
||||
fi
|
||||
echo "DIFF=$DIFF" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Saving proposed ui
|
||||
if: github.event_name == 'pull_request_target'
|
||||
working-directory: ${{ github.workspace }}/master_ui_raylib
|
||||
run: |
|
||||
# Overwrite PR branch w/ proposed ui, and master ui at this point in time for future reference
|
||||
git config user.name "GitHub Actions Bot"
|
||||
git config user.email "<>"
|
||||
git checkout --orphan=${{ env.BRANCH_NAME }}
|
||||
git rm -rf *
|
||||
mv ${{ github.workspace }}/pr_ui/* .
|
||||
git add .
|
||||
git commit -m "mici raylib video for PR #${{ github.event.number }}"
|
||||
git push origin ${{ env.BRANCH_NAME }} --force
|
||||
|
||||
# Append diff report to report files branch
|
||||
git fetch origin ${{ env.REPORT_FILES_BRANCH_NAME }}
|
||||
git checkout ${{ env.REPORT_FILES_BRANCH_NAME }}
|
||||
cp ${{ github.workspace }}/selfdrive/ui/tests/diff/report/diff.html diff_pr_${{ github.event.number }}.html
|
||||
git add diff_pr_${{ github.event.number }}.html
|
||||
git commit -m "mici raylib ui diff report for PR #${{ github.event.number }}" || echo "No changes to commit"
|
||||
git push origin ${{ env.REPORT_FILES_BRANCH_NAME }}
|
||||
|
||||
- name: Comment Video on PR
|
||||
if: github.event_name == 'pull_request_target'
|
||||
uses: thollander/actions-comment-pull-request@v2
|
||||
with:
|
||||
message: |
|
||||
<!-- _(run_id_video_mici_raylib **${{ github.run_id }}**)_ -->
|
||||
## mici raylib UI Preview
|
||||
${{ steps.find_diff.outputs.DIFF }}
|
||||
comment_tag: run_id_video_mici_raylib
|
||||
pr_number: ${{ github.event.number }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
42
.github/workflows/model_review.yaml
vendored
42
.github/workflows/model_review.yaml
vendored
@@ -1,42 +0,0 @@
|
||||
name: "model review"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
paths:
|
||||
- 'selfdrive/modeld/models/*.onnx'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
comment:
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Checkout master
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: master
|
||||
path: base
|
||||
- run: git lfs pull
|
||||
- run: cd base && git lfs pull
|
||||
|
||||
- run: pip install onnx
|
||||
|
||||
- name: scripts/reporter.py
|
||||
id: report
|
||||
run: |
|
||||
echo "content<<EOF" >> $GITHUB_OUTPUT
|
||||
echo "## Model Review" >> $GITHUB_OUTPUT
|
||||
MASTER_PATH=${{ github.workspace }}/base python scripts/reporter.py >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Post model report comment
|
||||
uses: marocchino/sticky-pull-request-comment@baa7203ed60924babbe5dcd0ac8eae3b66ec5e16
|
||||
with:
|
||||
header: model-review
|
||||
message: ${{ steps.report.outputs.content }}
|
||||
105
.github/workflows/post-to-discourse/action.yml
vendored
105
.github/workflows/post-to-discourse/action.yml
vendored
@@ -1,105 +0,0 @@
|
||||
name: 'Post to Discourse'
|
||||
description: 'Posts a message to a Discourse topic (existing or new)'
|
||||
|
||||
inputs:
|
||||
discourse-url:
|
||||
description: 'Discourse instance URL (e.g., https://discourse.example.com)'
|
||||
required: true
|
||||
api-key:
|
||||
description: 'Discourse API key'
|
||||
required: true
|
||||
api-username:
|
||||
description: 'Discourse API username'
|
||||
required: true
|
||||
topic-id:
|
||||
description: 'Discourse topic ID to post to (use this OR category-id + title)'
|
||||
required: false
|
||||
category-id:
|
||||
description: 'Category ID for new topic (required if topic-id not provided)'
|
||||
required: false
|
||||
title:
|
||||
description: 'Title for new topic (required if topic-id not provided)'
|
||||
required: false
|
||||
message:
|
||||
description: 'Message content (markdown supported)'
|
||||
required: true
|
||||
|
||||
outputs:
|
||||
post-number:
|
||||
description: 'The post number in the topic'
|
||||
value: ${{ steps.post.outputs.post_number }}
|
||||
post-url:
|
||||
description: 'Direct URL to the post'
|
||||
value: ${{ steps.post.outputs.post_url }}
|
||||
topic-id:
|
||||
description: 'The topic ID (useful when creating a new topic)'
|
||||
value: ${{ steps.post.outputs.topic_id }}
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Post to Discourse
|
||||
id: post
|
||||
shell: bash
|
||||
run: |
|
||||
# Validate inputs
|
||||
if [ -z "${{ inputs.topic-id }}" ] && ([ -z "${{ inputs.category-id }}" ] || [ -z "${{ inputs.title }}" ]); then
|
||||
echo "❌ Error: Must provide either topic-id OR both category-id and title"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -n "${{ inputs.topic-id }}" ] && ([ -n "${{ inputs.category-id }}" ] || [ -n "${{ inputs.title }}" ]); then
|
||||
echo "⚠️ Warning: Both topic-id and category-id/title provided. Will post to existing topic."
|
||||
fi
|
||||
|
||||
# Determine if creating new topic or posting to existing
|
||||
if [ -n "${{ inputs.topic-id }}" ]; then
|
||||
echo "📝 Posting to existing topic ID: ${{ inputs.topic-id }}"
|
||||
|
||||
# Create JSON payload for posting to existing topic
|
||||
PAYLOAD=$(jq -n \
|
||||
--arg content '${{ inputs.message }}' \
|
||||
--arg topic_id "${{ inputs.topic-id }}" \
|
||||
'{topic_id: $topic_id, raw: $content}')
|
||||
else
|
||||
echo "✨ Creating new topic: ${{ inputs.title }}"
|
||||
|
||||
# Create JSON payload for new topic
|
||||
PAYLOAD=$(jq -n \
|
||||
--arg content '${{ inputs.message }}' \
|
||||
--arg title "${{ inputs.title }}" \
|
||||
--arg category "${{ inputs.category-id }}" \
|
||||
'{title: $title, category: ($category | tonumber), raw: $content}')
|
||||
fi
|
||||
|
||||
# Post to Discourse
|
||||
RESPONSE=$(curl -s -w "\n%{http_code}" \
|
||||
-X POST "${{ inputs.discourse-url }}/posts.json" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Api-Key: ${{ inputs.api-key }}" \
|
||||
-H "Api-Username: ${{ inputs.api-username }}" \
|
||||
-d "$PAYLOAD")
|
||||
|
||||
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
|
||||
BODY=$(echo "$RESPONSE" | sed '$d')
|
||||
|
||||
if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 300 ]; then
|
||||
echo "✅ Successfully posted to Discourse!"
|
||||
|
||||
POST_NUMBER=$(echo "$BODY" | jq -r '.post_number // "unknown"')
|
||||
TOPIC_ID=$(echo "$BODY" | jq -r '.topic_id // "${{ inputs.topic-id }}"')
|
||||
POST_URL="${{ inputs.discourse-url }}/t/${TOPIC_ID}/${POST_NUMBER}"
|
||||
|
||||
echo "post_number=${POST_NUMBER}" >> $GITHUB_OUTPUT
|
||||
echo "post_url=${POST_URL}" >> $GITHUB_OUTPUT
|
||||
echo "topic_id=${TOPIC_ID}" >> $GITHUB_OUTPUT
|
||||
|
||||
echo "Topic ID: ${TOPIC_ID}"
|
||||
echo "Post number: ${POST_NUMBER}"
|
||||
echo "URL: ${POST_URL}"
|
||||
else
|
||||
echo "❌ Failed to post to Discourse"
|
||||
echo "HTTP Code: ${HTTP_CODE}"
|
||||
echo "Response: ${BODY}"
|
||||
exit 1
|
||||
fi
|
||||
4
.github/workflows/prebuilt.yaml
vendored
4
.github/workflows/prebuilt.yaml
vendored
@@ -6,13 +6,13 @@ on:
|
||||
|
||||
env:
|
||||
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
|
||||
BUILD: release/ci/docker_build_sp.sh prebuilt
|
||||
BUILD: selfdrive/test/docker_build.sh prebuilt
|
||||
|
||||
jobs:
|
||||
build_prebuilt:
|
||||
name: build prebuilt
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'sunnypilot/sunnypilot'
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
env:
|
||||
PUSH_IMAGE: true
|
||||
permissions:
|
||||
|
||||
28
.github/workflows/release-drafter.yml
vendored
28
.github/workflows/release-drafter.yml
vendored
@@ -1,28 +0,0 @@
|
||||
name: Release Drafter
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request_target:
|
||||
types: [opened, reopened, synchronize]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
update_release_draft:
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: release-drafter/release-drafter@v6
|
||||
with:
|
||||
config-name: release-drafter.yml
|
||||
prerelease: ${{ !startsWith(github.ref, 'refs/tags/v') }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
30
.github/workflows/release.yaml
vendored
30
.github/workflows/release.yaml
vendored
@@ -5,14 +5,15 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build___nightly:
|
||||
name: build __nightly
|
||||
build_masterci:
|
||||
name: build master-ci
|
||||
env:
|
||||
ImageOS: ubuntu24
|
||||
TARGET_DIR: /tmp/openpilot
|
||||
ImageOS: ubuntu20
|
||||
container:
|
||||
image: ghcr.io/sunnypilot/sunnypilot-base:latest
|
||||
image: ghcr.io/commaai/openpilot-base:latest
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'sunnypilot/sunnypilot'
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
permissions:
|
||||
checks: read
|
||||
contents: write
|
||||
@@ -22,12 +23,12 @@ jobs:
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libyaml-dev
|
||||
- name: Wait for green check mark
|
||||
if: ${{ github.event_name == 'schedule' }}
|
||||
if: ${{ github.event_name != 'workflow_dispatch' }}
|
||||
uses: lewagon/wait-on-check-action@ccfb013c15c8afb7bf2b7c028fb74dc5a068cccc
|
||||
with:
|
||||
ref: master
|
||||
wait-interval: 30
|
||||
running-workflow-name: 'build __nightly'
|
||||
running-workflow-name: 'build master-ci'
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
check-regexp: ^((?!.*(build prebuilt).*).)*$
|
||||
- uses: actions/checkout@v4
|
||||
@@ -38,5 +39,16 @@ jobs:
|
||||
run: |
|
||||
git config --global --add safe.directory '*'
|
||||
git lfs pull
|
||||
- name: Push __nightly
|
||||
run: BRANCH=__nightly release/build_stripped.sh
|
||||
- name: Build master-ci
|
||||
run: |
|
||||
release/build_devel.sh
|
||||
- name: Run tests
|
||||
run: |
|
||||
export PYTHONPATH=$TARGET_DIR
|
||||
cd $TARGET_DIR
|
||||
scons -j$(nproc)
|
||||
pytest -n logical selfdrive/car/tests/test_car_interfaces.py
|
||||
- name: Push master-ci
|
||||
run: |
|
||||
unset TARGET_DIR
|
||||
BRANCH=__nightly release/build_devel.sh
|
||||
|
||||
23
.github/workflows/repo-maintenance.yaml
vendored
23
.github/workflows/repo-maintenance.yaml
vendored
@@ -6,14 +6,14 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
BASE_IMAGE: sunnypilot-base
|
||||
BUILD: release/ci/docker_build_sp.sh base
|
||||
BASE_IMAGE: openpilot-base
|
||||
BUILD: selfdrive/test/docker_build.sh base
|
||||
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c
|
||||
|
||||
jobs:
|
||||
update_translations:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'sunnypilot/sunnypilot'
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@9153d834b60caba6d51c9b9510b087acf9f33f83
|
||||
with:
|
||||
author: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
|
||||
author: Vehicle Researcher <user@comma.ai>
|
||||
commit-message: "Update translations"
|
||||
title: "[bot] Update translations"
|
||||
body: "Automatic PR from repo-maintenance -> update_translations"
|
||||
@@ -36,14 +36,13 @@ jobs:
|
||||
name: package_updates
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/sunnypilot/sunnypilot-base:latest
|
||||
if: github.repository == 'sunnypilot/sunnypilot'
|
||||
image: ghcr.io/commaai/openpilot-base:latest
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: uv lock
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
run: |
|
||||
python3 -m ensurepip --upgrade
|
||||
pip3 install uv
|
||||
@@ -51,20 +50,18 @@ jobs:
|
||||
- name: bump submodules
|
||||
run: |
|
||||
git config --global --add safe.directory '*'
|
||||
git config submodule.tinygrad.update none
|
||||
git submodule update --remote
|
||||
git -c submodule."tinygrad".update=none submodule update --remote
|
||||
git add .
|
||||
- name: update car docs
|
||||
run: |
|
||||
export PYTHONPATH="$PWD"
|
||||
scons -j$(nproc) --minimal opendbc_repo
|
||||
python selfdrive/car/docs.py
|
||||
PYTHONPATH=. python selfdrive/car/docs.py
|
||||
git add docs/CARS.md
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@9153d834b60caba6d51c9b9510b087acf9f33f83
|
||||
with:
|
||||
author: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
|
||||
token: ${{ github.repository == 'commaai/openpilot' && secrets.ACTIONS_CREATE_PR_PAT || secrets.GITHUB_TOKEN }}
|
||||
author: Vehicle Researcher <user@comma.ai>
|
||||
token: ${{ secrets.ACTIONS_CREATE_PR_PAT }}
|
||||
commit-message: Update Python packages
|
||||
title: '[bot] Update Python packages'
|
||||
branch: auto-package-updates
|
||||
|
||||
350
.github/workflows/selfdrive_tests.yaml
vendored
Normal file
350
.github/workflows/selfdrive_tests.yaml
vendored
Normal file
@@ -0,0 +1,350 @@
|
||||
name: selfdrive
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
inputs:
|
||||
run_number:
|
||||
default: '1'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
concurrency:
|
||||
group: selfdrive-tests-ci-run-${{ inputs.run_number }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
PYTHONWARNINGS: error
|
||||
BASE_IMAGE: openpilot-base
|
||||
AZURE_TOKEN: ${{ secrets.AZURE_COMMADATACI_OPENPILOTCI_TOKEN }}
|
||||
|
||||
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
|
||||
BUILD: selfdrive/test/docker_build.sh base
|
||||
|
||||
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c
|
||||
|
||||
PYTEST: pytest --continue-on-collection-errors --cov --cov-report=xml --cov-append --durations=0 --durations-min=5 --hypothesis-seed 0 -n logical
|
||||
|
||||
jobs:
|
||||
build_release:
|
||||
name: build release
|
||||
runs-on:
|
||||
- ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-amd64-8x16' || 'ubuntu-24.04' }}
|
||||
- ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-experiments:docker.builds.local-cache=separate' || 'ubuntu-24.04' }}
|
||||
env:
|
||||
STRIPPED_DIR: /tmp/releasepilot
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Getting LFS files
|
||||
uses: nick-fields/retry@7152eba30c6575329ac0576536151aca5a72780e
|
||||
with:
|
||||
timeout_minutes: 2
|
||||
max_attempts: 3
|
||||
command: git lfs pull
|
||||
- name: Build devel
|
||||
timeout-minutes: 1
|
||||
run: TARGET_DIR=$STRIPPED_DIR release/build_devel.sh
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
- name: Check submodules
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
timeout-minutes: 3
|
||||
run: release/check-submodules.sh
|
||||
- name: Build openpilot and run checks
|
||||
timeout-minutes: ${{ ((steps.restore-scons-cache.outputs.cache-hit == 'true') && 10 || 30) }} # allow more time when we missed the scons cache
|
||||
run: |
|
||||
cd $STRIPPED_DIR
|
||||
${{ env.RUN }} "python3 system/manager/build.py"
|
||||
- name: Run tests
|
||||
timeout-minutes: 1
|
||||
run: |
|
||||
cd $STRIPPED_DIR
|
||||
${{ env.RUN }} "release/check-dirty.sh"
|
||||
|
||||
build:
|
||||
runs-on:
|
||||
- ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-amd64-8x16' || 'ubuntu-24.04' }}
|
||||
- ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-experiments:docker.builds.local-cache=separate' || 'ubuntu-24.04' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Setup docker push
|
||||
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'commaai/openpilot'
|
||||
run: |
|
||||
echo "PUSH_IMAGE=true" >> "$GITHUB_ENV"
|
||||
$DOCKER_LOGIN
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
- uses: ./.github/workflows/compile-openpilot
|
||||
timeout-minutes: 30
|
||||
|
||||
build_mac:
|
||||
name: build macOS
|
||||
runs-on: ${{ github.repository == 'commaai/openpilot' && 'namespace-profile-macos-8x14' || 'macos-latest' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Homebrew cache
|
||||
uses: ./.github/workflows/auto-cache
|
||||
with:
|
||||
path: ~/Library/Caches/Homebrew
|
||||
- name: Install dependencies
|
||||
run: ./tools/mac_setup.sh
|
||||
env:
|
||||
# package install has DeprecationWarnings
|
||||
PYTHONWARNINGS: default
|
||||
- run: git lfs pull
|
||||
- run: echo "CACHE_COMMIT_DATE=$(git log -1 --pretty='format:%cd' --date=format:'%Y-%m-%d-%H:%M')" >> $GITHUB_ENV
|
||||
- name: Getting scons cache
|
||||
uses: ./.github/workflows/auto-cache
|
||||
with:
|
||||
path: /tmp/scons_cache
|
||||
- name: Building openpilot
|
||||
run: . .venv/bin/activate && scons -j$(nproc)
|
||||
|
||||
static_analysis:
|
||||
name: static analysis
|
||||
runs-on:
|
||||
- ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-amd64-8x16' || 'ubuntu-24.04' }}
|
||||
- ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-experiments:docker.builds.local-cache=separate' || 'ubuntu-24.04' }}
|
||||
env:
|
||||
PYTHONWARNINGS: default
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
- name: Static analysis
|
||||
timeout-minutes: 1
|
||||
run: ${{ env.RUN }} "scripts/lint/lint.sh"
|
||||
|
||||
unit_tests:
|
||||
name: unit tests
|
||||
runs-on:
|
||||
- ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-amd64-8x16' || 'ubuntu-24.04' }}
|
||||
- ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-experiments:docker.builds.local-cache=separate' || 'ubuntu-24.04' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
- name: Build openpilot
|
||||
run: ${{ env.RUN }} "scons -j$(nproc)"
|
||||
- name: Run unit tests
|
||||
timeout-minutes: ${{ contains(runner.name, 'nsc') && 1 || 20 }}
|
||||
run: |
|
||||
${{ env.RUN }} "$PYTEST --collect-only -m 'not slow' &> /dev/null && \
|
||||
MAX_EXAMPLES=1 $PYTEST -m 'not slow' && \
|
||||
./selfdrive/ui/tests/create_test_translations.sh && \
|
||||
QT_QPA_PLATFORM=offscreen ./selfdrive/ui/tests/test_translations && \
|
||||
chmod -R 777 /tmp/comma_download_cache"
|
||||
- name: "Upload coverage to Codecov"
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
name: ${{ github.job }}
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
process_replay:
|
||||
name: process replay
|
||||
runs-on:
|
||||
- ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-amd64-8x16' || 'ubuntu-24.04' }}
|
||||
- ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-experiments:docker.builds.local-cache=separate' || 'ubuntu-24.04' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
- name: Cache test routes
|
||||
id: dependency-cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: .ci_cache/comma_download_cache
|
||||
key: proc-replay-${{ hashFiles('selfdrive/test/process_replay/ref_commit', 'selfdrive/test/process_replay/test_processes.py') }}
|
||||
- name: Build openpilot
|
||||
run: |
|
||||
${{ env.RUN }} "scons -j$(nproc)"
|
||||
- name: Run replay
|
||||
timeout-minutes: ${{ contains(runner.name, 'nsc') && (steps.dependency-cache.outputs.cache-hit == 'true') && 1 || 20 }}
|
||||
run: |
|
||||
${{ env.RUN }} "coverage run selfdrive/test/process_replay/test_processes.py -j$(nproc) && \
|
||||
chmod -R 777 /tmp/comma_download_cache && \
|
||||
coverage combine && \
|
||||
coverage xml"
|
||||
- name: Print diff
|
||||
id: print-diff
|
||||
if: always()
|
||||
run: cat selfdrive/test/process_replay/diff.txt
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
continue-on-error: true
|
||||
with:
|
||||
name: process_replay_diff.txt
|
||||
path: selfdrive/test/process_replay/diff.txt
|
||||
- name: Upload reference logs
|
||||
if: ${{ failure() && steps.print-diff.outcome == 'success' && github.repository == 'commaai/openpilot' && env.AZURE_TOKEN != '' }}
|
||||
run: |
|
||||
${{ env.RUN }} "unset PYTHONWARNINGS && AZURE_TOKEN='$AZURE_TOKEN' python3 selfdrive/test/process_replay/test_processes.py -j$(nproc) --upload-only"
|
||||
- name: Run regen
|
||||
if: false
|
||||
timeout-minutes: 4
|
||||
run: |
|
||||
${{ env.RUN }} "ONNXCPU=1 $PYTEST selfdrive/test/process_replay/test_regen.py && \
|
||||
chmod -R 777 /tmp/comma_download_cache"
|
||||
- name: "Upload coverage to Codecov"
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
name: ${{ github.job }}
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
test_cars:
|
||||
name: cars
|
||||
runs-on:
|
||||
- ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-amd64-8x16' || 'ubuntu-24.04' }}
|
||||
- ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-experiments:docker.builds.local-cache=separate' || 'ubuntu-24.04' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
job: [0, 1, 2, 3]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
- name: Cache test routes
|
||||
id: routes-cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: .ci_cache/comma_download_cache
|
||||
key: car_models-${{ hashFiles('selfdrive/car/tests/test_models.py', 'opendbc/car/tests/routes.py') }}-${{ matrix.job }}
|
||||
- name: Build openpilot
|
||||
run: ${{ env.RUN }} "scons -j$(nproc)"
|
||||
- name: Test car models
|
||||
timeout-minutes: ${{ contains(runner.name, 'nsc') && (steps.routes-cache.outputs.cache-hit == 'true') && 1 || 6 }}
|
||||
run: |
|
||||
${{ env.RUN }} "MAX_EXAMPLES=1 $PYTEST selfdrive/car/tests/test_models.py && \
|
||||
chmod -R 777 /tmp/comma_download_cache"
|
||||
env:
|
||||
NUM_JOBS: 4
|
||||
JOB_ID: ${{ matrix.job }}
|
||||
- name: "Upload coverage to Codecov"
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
name: ${{ github.job }}-${{ matrix.job }}
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
car_docs_diff:
|
||||
name: PR comments
|
||||
runs-on: ubuntu-latest
|
||||
#if: github.event_name == 'pull_request'
|
||||
if: false # TODO: run this in opendbc?
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
ref: ${{ github.event.pull_request.base.ref }}
|
||||
- run: git lfs pull
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
- name: Get base car info
|
||||
run: |
|
||||
${{ env.RUN }} "scons -j$(nproc) && python3 selfdrive/debug/dump_car_docs.py --path /tmp/openpilot_cache/base_car_docs"
|
||||
sudo chown -R $USER:$USER ${{ github.workspace }}
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
path: current
|
||||
- run: cd current && git lfs pull
|
||||
- name: Save car docs diff
|
||||
id: save_diff
|
||||
run: |
|
||||
cd current
|
||||
${{ env.RUN }} "scons -j$(nproc)"
|
||||
output=$(${{ env.RUN }} "python3 selfdrive/debug/print_docs_diff.py --path /tmp/openpilot_cache/base_car_docs")
|
||||
output="${output//$'\n'/'%0A'}"
|
||||
echo "::set-output name=diff::$output"
|
||||
- name: Find comment
|
||||
if: ${{ env.AZURE_TOKEN != '' }}
|
||||
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e
|
||||
id: fc
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
body-includes: This PR makes changes to
|
||||
- name: Update comment
|
||||
if: ${{ steps.save_diff.outputs.diff != '' && env.AZURE_TOKEN != '' }}
|
||||
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043
|
||||
with:
|
||||
comment-id: ${{ steps.fc.outputs.comment-id }}
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
body: "${{ steps.save_diff.outputs.diff }}"
|
||||
edit-mode: replace
|
||||
- name: Delete comment
|
||||
if: ${{ steps.fc.outputs.comment-id != '' && steps.save_diff.outputs.diff == '' && env.AZURE_TOKEN != '' }}
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
github.rest.issues.deleteComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: ${{ steps.fc.outputs.comment-id }}
|
||||
})
|
||||
|
||||
simulator_driving:
|
||||
name: simulator driving
|
||||
runs-on:
|
||||
- ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-amd64-8x16' || 'ubuntu-24.04' }}
|
||||
- ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-experiments:docker.builds.local-cache=separate' || 'ubuntu-24.04' }}
|
||||
if: (github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
- name: Build openpilot
|
||||
run: |
|
||||
${{ env.RUN }} "scons -j$(nproc)"
|
||||
- name: Driving test
|
||||
timeout-minutes: 1
|
||||
run: |
|
||||
${{ env.RUN }} "source selfdrive/test/setup_xvfb.sh && \
|
||||
source selfdrive/test/setup_vsound.sh && \
|
||||
CI=1 pytest -s tools/sim/tests/test_metadrive_bridge.py"
|
||||
|
||||
create_ui_report:
|
||||
# This job name needs to be the same as UI_JOB_NAME in ui_preview.yaml
|
||||
name: Create UI Report
|
||||
runs-on:
|
||||
- ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-amd64-8x16' || 'ubuntu-24.04' }}
|
||||
- ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-experiments:docker.builds.local-cache=separate' || 'ubuntu-24.04' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
- name: caching frames
|
||||
id: frames-cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: .ci_cache/comma_download_cache
|
||||
key: ui_screenshots_test_${{ hashFiles('selfdrive/ui/tests/test_ui/run.py') }}
|
||||
- name: Build openpilot
|
||||
run: ${{ env.RUN }} "scons -j$(nproc)"
|
||||
- name: Create Test Report
|
||||
timeout-minutes: ${{ ((steps.frames-cache.outputs.cache-hit == 'true') && 1 || 3) }}
|
||||
run: >
|
||||
${{ env.RUN }} "PYTHONWARNINGS=ignore &&
|
||||
source selfdrive/test/setup_xvfb.sh &&
|
||||
CACHE_ROOT=/tmp/comma_download_cache python3 selfdrive/ui/tests/test_ui/run.py &&
|
||||
chmod -R 777 /tmp/comma_download_cache"
|
||||
- name: Upload Test Report
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
||||
path: selfdrive/ui/tests/test_ui/report_1/screenshots
|
||||
15
.github/workflows/setup-with-retry/action.yaml
vendored
15
.github/workflows/setup-with-retry/action.yaml
vendored
@@ -10,17 +10,9 @@ inputs:
|
||||
required: false
|
||||
default: 30
|
||||
|
||||
outputs:
|
||||
duration:
|
||||
description: 'Duration of the setup process in seconds'
|
||||
value: ${{ steps.get_duration.outputs.duration }}
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- id: start_time
|
||||
shell: bash
|
||||
run: echo "START_TIME=$(date +%s)" >> $GITHUB_ENV
|
||||
- id: setup1
|
||||
uses: ./.github/workflows/setup
|
||||
continue-on-error: true
|
||||
@@ -43,10 +35,3 @@ runs:
|
||||
uses: ./.github/workflows/setup
|
||||
with:
|
||||
is_retried: true
|
||||
- id: get_duration
|
||||
shell: bash
|
||||
run: |
|
||||
END_TIME=$(date +%s)
|
||||
DURATION=$((END_TIME - START_TIME))
|
||||
echo "Total duration: $DURATION seconds"
|
||||
echo "duration=$DURATION" >> $GITHUB_OUTPUT
|
||||
|
||||
2
.github/workflows/setup/action.yaml
vendored
2
.github/workflows/setup/action.yaml
vendored
@@ -19,7 +19,7 @@ runs:
|
||||
- shell: bash
|
||||
name: No retries!
|
||||
run: |
|
||||
if [ "${{ github.run_attempt }}" -gt ${{ github.event.pull_request.head.repo.fork && github.event.pull_request.author_association == 'NONE' && 2 || 1}} ]; then
|
||||
if [ "${{ github.run_attempt }}" -gt 1 ]; then
|
||||
echo -e "\033[0;31m##################################################"
|
||||
echo -e "\033[0;31m Retries not allowed! Fix the flaky test! "
|
||||
echo -e "\033[0;31m##################################################\033[0m"
|
||||
|
||||
29
.github/workflows/stale.yaml
vendored
29
.github/workflows/stale.yaml
vendored
@@ -5,9 +5,8 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
DAYS_BEFORE_PR_CLOSE: 7
|
||||
DAYS_BEFORE_PR_STALE: 24
|
||||
DAYS_BEFORE_PR_STALE_DRAFT: 30
|
||||
DAYS_BEFORE_PR_CLOSE: 2
|
||||
DAYS_BEFORE_PR_STALE: 9
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
@@ -21,32 +20,10 @@ jobs:
|
||||
stale-pr-message: 'This PR has had no activity for ${{ env.DAYS_BEFORE_PR_STALE }} days. It will be automatically closed in ${{ env.DAYS_BEFORE_PR_CLOSE }} days if there is no activity.'
|
||||
close-pr-message: 'This PR has been automatically closed due to inactivity. Feel free to re-open once activity resumes.'
|
||||
stale-pr-label: stale
|
||||
delete-branch: ${{ github.event.pull_request.head.repo.full_name == 'sunnypilot/sunnypilot' }} # only delete branches on the main repo
|
||||
delete-branch: ${{ github.event.pull_request.head.repo.full_name == 'commaai/openpilot' }} # only delete branches on the main repo
|
||||
exempt-pr-labels: "ignore stale,needs testing" # if wip or it needs testing from the community, don't mark as stale
|
||||
days-before-pr-stale: ${{ env.DAYS_BEFORE_PR_STALE }}
|
||||
days-before-pr-close: ${{ env.DAYS_BEFORE_PR_CLOSE }}
|
||||
exempt-draft-pr: false
|
||||
|
||||
# issue config
|
||||
days-before-issue-stale: -1 # ignore issues for now
|
||||
|
||||
# same as above, but give draft PRs more time
|
||||
stale_drafts:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
exempt-all-milestones: true
|
||||
|
||||
# pull request config
|
||||
stale-pr-message: 'This PR has had no activity for ${{ env.DAYS_BEFORE_PR_STALE_DRAFT }} days. It will be automatically closed in ${{ env.DAYS_BEFORE_PR_CLOSE }} days if there is no activity.'
|
||||
close-pr-message: 'This PR has been automatically closed due to inactivity. Feel free to re-open once activity resumes.'
|
||||
stale-pr-label: stale
|
||||
delete-branch: ${{ github.event.pull_request.head.repo.full_name == 'commaai/openpilot' }} # only delete branches on the main repo
|
||||
exempt-pr-labels: "ignore stale,needs testing" # if wip or it needs testing from the community, don't mark as stale
|
||||
days-before-pr-stale: ${{ env.DAYS_BEFORE_PR_STALE_DRAFT }}
|
||||
days-before-pr-close: ${{ env.DAYS_BEFORE_PR_CLOSE }}
|
||||
exempt-draft-pr: true
|
||||
|
||||
# issue config
|
||||
days-before-issue-stale: -1 # ignore issues for now
|
||||
|
||||
221
.github/workflows/sunnypilot-build-model.yaml
vendored
221
.github/workflows/sunnypilot-build-model.yaml
vendored
@@ -1,221 +0,0 @@
|
||||
name: Build Model from Upstream
|
||||
|
||||
env:
|
||||
BUILD_DIR: "/data/openpilot"
|
||||
OUTPUT_DIR: ${{ github.workspace }}/output
|
||||
SCONS_CACHE_DIR: ${{ github.workspace }}/release/ci/scons_cache
|
||||
UPSTREAM_REPO: "commaai/openpilot"
|
||||
TINYGRAD_PATH: ${{ github.workspace }}/tinygrad_repo
|
||||
MODELS_DIR: ${{ github.workspace }}/selfdrive/modeld/models
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
upstream_branch:
|
||||
description: 'Upstream branch to build from'
|
||||
required: true
|
||||
default: 'master'
|
||||
type: string
|
||||
custom_name:
|
||||
description: 'Custom name for the model (no date, only name)'
|
||||
required: false
|
||||
type: string
|
||||
is_20hz:
|
||||
description: 'Is this a 20Hz model'
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
artifact_suffix:
|
||||
description: 'Suffix for artifact name'
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
upstream_branch:
|
||||
description: 'Upstream branch to build from'
|
||||
required: true
|
||||
default: 'master'
|
||||
type: string
|
||||
custom_name:
|
||||
description: 'Custom name for the model (no date, only name)'
|
||||
required: false
|
||||
type: string
|
||||
is_20hz:
|
||||
description: 'Is this a 20Hz model'
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
|
||||
run-name: Build model [${{ inputs.custom_name || inputs.upstream_branch }}] from ref [${{ inputs.upstream_branch }}]
|
||||
|
||||
jobs:
|
||||
get_model:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
REF: ${{ inputs.upstream_branch }}
|
||||
outputs:
|
||||
model_date: ${{ steps.commit-date.outputs.model_date }}
|
||||
steps:
|
||||
# Note: To allow dynamic models from both openpilot and sunnypilot (merges/mashups), we try commaai as default,
|
||||
# and fallback to sunnypilot if the ref checkout fails.
|
||||
- name: Checkout commaai/openpilot
|
||||
id: checkout_upstream
|
||||
continue-on-error: true
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: commaai/openpilot
|
||||
ref: ${{ inputs.upstream_branch }}
|
||||
submodules: recursive
|
||||
path: openpilot
|
||||
|
||||
- name: Fallback to sunnypilot/sunnypilot
|
||||
if: steps.checkout_upstream.outcome == 'failure'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: sunnypilot/sunnypilot
|
||||
ref: ${{ inputs.upstream_branch }}
|
||||
submodules: recursive
|
||||
path: openpilot
|
||||
- name: Get commit date
|
||||
id: commit-date
|
||||
run: |
|
||||
cd ${{ github.workspace }}/openpilot
|
||||
commit_date=$(git log -1 --format=%cd --date=format:'%B %d, %Y')
|
||||
echo "model_date=${commit_date}" >> $GITHUB_OUTPUT
|
||||
cat $GITHUB_OUTPUT
|
||||
- run: |
|
||||
cd ${{ github.workspace }}/openpilot
|
||||
git lfs pull
|
||||
- name: 'Upload Artifact'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: models-${{ env.REF }}${{ inputs.artifact_suffix }}
|
||||
path: ${{ github.workspace }}/openpilot/selfdrive/modeld/models/*.onnx
|
||||
|
||||
build_model:
|
||||
runs-on: [self-hosted, tici]
|
||||
needs: get_model
|
||||
env:
|
||||
MODEL_NAME: ${{ inputs.custom_name || inputs.upstream_branch }} (${{ needs.get_model.outputs.model_date }})
|
||||
REF: ${{ inputs.upstream_branch }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- run: git lfs pull
|
||||
- name: Cache SCons
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{env.SCONS_CACHE_DIR}}
|
||||
key: scons-${{ runner.os }}-${{ runner.arch }}-${{ github.head_ref || github.ref_name }}-model-${{ github.sha }}
|
||||
# Note: GitHub Actions enforces cache isolation between different build sources (PR builds, workflow dispatches, etc.)
|
||||
# for security. Only caches from the default branch are shared across all builds. This is by design and cannot be overridden.
|
||||
restore-keys: |
|
||||
scons-${{ runner.os }}-${{ runner.arch }}-${{ github.head_ref || github.ref_name }}-model
|
||||
scons-${{ runner.os }}-${{ runner.arch }}-${{ github.head_ref || github.ref_name }}
|
||||
scons-${{ runner.os }}-${{ runner.arch }}-${{ env.MASTER_NEW_BRANCH }}-model
|
||||
scons-${{ runner.os }}-${{ runner.arch }}-${{ env.MASTER_BRANCH }}-model
|
||||
scons-${{ runner.os }}-${{ runner.arch }}-${{ env.MASTER_NEW_BRANCH }}
|
||||
scons-${{ runner.os }}-${{ runner.arch }}-${{ env.MASTER_BRANCH }}
|
||||
scons-${{ runner.os }}-${{ runner.arch }}
|
||||
|
||||
- name: Set environment variables
|
||||
id: set-env
|
||||
run: |
|
||||
# Set up common environment
|
||||
source /etc/profile;
|
||||
export UV_PROJECT_ENVIRONMENT=${HOME}/venv
|
||||
export VIRTUAL_ENV=$UV_PROJECT_ENVIRONMENT
|
||||
printenv >> $GITHUB_ENV
|
||||
if [[ "${{ runner.debug }}" == "1" ]]; then
|
||||
cat $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Setup build environment
|
||||
run: |
|
||||
mkdir -p "${BUILD_DIR}/"
|
||||
sudo find $BUILD_DIR/ -mindepth 1 -delete
|
||||
echo "Starting build stage..."
|
||||
echo "BUILD_DIR: ${BUILD_DIR}"
|
||||
echo "CI_DIR: ${CI_DIR}"
|
||||
echo "VERSION: ${{ steps.set-env.outputs.version }}"
|
||||
echo "UV_PROJECT_ENVIRONMENT: ${UV_PROJECT_ENVIRONMENT}"
|
||||
echo "VIRTUAL_ENV: ${VIRTUAL_ENV}"
|
||||
echo "-------"
|
||||
if [[ "${{ runner.debug }}" == "1" ]]; then
|
||||
printenv
|
||||
fi
|
||||
PYTHONPATH=$PYTHONPATH:${{ github.workspace }}/ ${{ github.workspace }}/scripts/manage-powersave.py --disable
|
||||
rm -rf ${{ env.MODELS_DIR }}/*.onnx
|
||||
|
||||
- name: Download model artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: models-${{ env.REF }}${{ inputs.artifact_suffix }}
|
||||
path: ${{ github.workspace }}/selfdrive/modeld/models
|
||||
- run: |
|
||||
rm -f ${{ github.workspace }}/selfdrive/modeld/models/{dmonitoring_model,big_driving_policy,big_driving_vision}.onnx
|
||||
|
||||
- name: Build Model
|
||||
run: |
|
||||
source /etc/profile
|
||||
export UV_PROJECT_ENVIRONMENT=${HOME}/venv
|
||||
export VIRTUAL_ENV=$UV_PROJECT_ENVIRONMENT
|
||||
export PYTHONPATH="${PYTHONPATH}:${{ env.TINYGRAD_PATH }}"
|
||||
|
||||
# Loop through all .onnx files
|
||||
find "${{ env.MODELS_DIR }}" -maxdepth 1 -name '*.onnx' | while IFS= read -r onnx_file; do
|
||||
base_name=$(basename "$onnx_file" .onnx)
|
||||
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"
|
||||
QCOM=1 python3 "${{ env.MODELS_DIR }}/../get_model_metadata.py" "$onnx_file" || true
|
||||
done
|
||||
|
||||
- name: Prepare Output
|
||||
run: |
|
||||
sudo rm -rf ${{ env.OUTPUT_DIR }}
|
||||
mkdir -p ${{ env.OUTPUT_DIR }}
|
||||
|
||||
# Copy the model files
|
||||
rsync -avm \
|
||||
--include='*.dlc' \
|
||||
--include='*.thneed' \
|
||||
--include='*.pkl' \
|
||||
--include='*.onnx' \
|
||||
--exclude='*' \
|
||||
--delete-excluded \
|
||||
--chown=comma:comma \
|
||||
${{ env.MODELS_DIR }}/ ${{ env.OUTPUT_DIR }}/
|
||||
|
||||
python3 "${{ github.workspace }}/release/ci/model_generator.py" \
|
||||
--model-dir "${{ env.MODELS_DIR }}" \
|
||||
--output-dir "${{ env.OUTPUT_DIR }}" \
|
||||
--custom-name "${{ env.MODEL_NAME }}" \
|
||||
--upstream-branch "${{ inputs.upstream_branch }}" \
|
||||
${{ inputs.is_20hz && '--is-20hz' || '' }}
|
||||
|
||||
- name: Write artifact name to file
|
||||
run: echo "model-${{ env.MODEL_NAME }}${{ inputs.artifact_suffix }}-${{ github.run_number }}" > ${{ env.OUTPUT_DIR }}/artifact_name.txt
|
||||
|
||||
- name: Upload Build Artifacts
|
||||
id: upload-artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: model-${{ env.MODEL_NAME }}${{ inputs.artifact_suffix }}-${{ github.run_number }}
|
||||
path: ${{ env.OUTPUT_DIR }}
|
||||
|
||||
- name: Upload artifact name file
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: artifact-name-${{ inputs.custom_name || inputs.upstream_branch }}
|
||||
path: ${{ env.OUTPUT_DIR }}/artifact_name.txt
|
||||
|
||||
- name: Re-enable powersave
|
||||
if: always()
|
||||
run: |
|
||||
PYTHONPATH=$PYTHONPATH:${{ github.workspace }}/ ${{ github.workspace }}/scripts/manage-powersave.py --enable
|
||||
372
.github/workflows/sunnypilot-build-prebuilt.yaml
vendored
372
.github/workflows/sunnypilot-build-prebuilt.yaml
vendored
@@ -1,372 +0,0 @@
|
||||
name: sunnypilot prebuilt action
|
||||
|
||||
env:
|
||||
BUILD_DIR: "/data/openpilot"
|
||||
OUTPUT_DIR: ${{ github.workspace }}/output
|
||||
CI_DIR: ${{ github.workspace }}/release/ci
|
||||
SCONS_CACHE_DIR: ${{ github.workspace }}/release/ci/scons_cache
|
||||
PUBLIC_REPO_URL: "https://github.com/sunnypilot/sunnypilot"
|
||||
|
||||
# Branch configurations
|
||||
STAGING_SOURCE_BRANCH: 'master'
|
||||
|
||||
# Runtime configuration
|
||||
SOURCE_BRANCH: "${{ github.head_ref || github.ref_name }}"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, master-dev ]
|
||||
tags: [ 'release/*' ]
|
||||
pull_request_target:
|
||||
types: [ labeled ]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
wait_for_tests:
|
||||
description: 'Wait for tests to finish'
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
prepare_strategy:
|
||||
runs-on: ubuntu-24.04
|
||||
if: (!contains(github.event_name, 'pull_request') || (github.event.action == 'labeled' && github.event.label.name == 'prebuilt'))
|
||||
outputs:
|
||||
environment: ${{ steps.strategy.outputs.environment }}
|
||||
new_branch: ${{ steps.strategy.outputs.new_branch }}
|
||||
extra_version_identifier: ${{ steps.strategy.outputs.extra_version_identifier }}
|
||||
version: ${{ steps.strategy.outputs.version }}
|
||||
cancel_publish_in_progress: ${{ steps.strategy.outputs.cancel_publish_in_progress }}
|
||||
publish_concurrency_group: ${{ steps.strategy.outputs.publish_concurrency_group }}
|
||||
is_stable_branch: ${{ steps.strategy.outputs.is_stable_branch }}
|
||||
build: ${{ steps.strategy.outputs.build }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Extract deploy strategy
|
||||
id: strategy
|
||||
run: |
|
||||
echo '::group::Strategy Extraction'
|
||||
BRANCH="${{ github.head_ref || github.ref_name }}"
|
||||
echo "Current branch: $BRANCH"
|
||||
|
||||
STRATEGY_JSON='${{ vars.DEPLOY_STRATEGY }}'
|
||||
CONFIG=$(echo "$STRATEGY_JSON" | jq -r --arg branch "$BRANCH" '
|
||||
.configs[] | select(.branch == $branch)
|
||||
')
|
||||
|
||||
BUILD="$(date '+%Y.%m.%d')-${{ github.run_number }}"
|
||||
if [[ -z "$CONFIG" || "$CONFIG" == "null" ]]; then
|
||||
echo "No exact strategy match found. Falling back to feature/fork logic."
|
||||
IS_FORK="${{ github.event.pull_request.head.repo.fork && 'true' || 'false' }}"
|
||||
FORK_SUFFIX=$( [[ "$IS_FORK" == "true" ]] && echo "-fork" || echo "" )
|
||||
NEW_BRANCH="${BRANCH}${FORK_SUFFIX}-prebuilt"
|
||||
|
||||
echo "new_branch=$NEW_BRANCH" >> $GITHUB_OUTPUT
|
||||
echo "version=$BUILD" >> $GITHUB_OUTPUT
|
||||
echo "cancel_publish_in_progress=true" >> $GITHUB_OUTPUT
|
||||
echo "publish_concurrency_group=publish-${BRANCH}" >> $GITHUB_OUTPUT
|
||||
echo "environment=feature-branch" >> $GITHUB_OUTPUT
|
||||
echo "extra_version_identifier=feature-branch" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "Matched config: $CONFIG"
|
||||
environment=$(echo "$CONFIG" | jq -r '.environment')
|
||||
echo "environment=$environment" >> $GITHUB_OUTPUT
|
||||
echo "new_branch=$(echo "$CONFIG" | jq -r '.target_branch')" >> $GITHUB_OUTPUT
|
||||
cancel="$(echo "$CONFIG" | jq -r '.cancel_publish_in_progress')";
|
||||
echo "cancel_publish_in_progress=$( [ "$cancel" = "null" ] && echo "true" || echo $cancel)" >> $GITHUB_OUTPUT
|
||||
echo "publish_concurrency_group=publish-${BRANCH}$( [ "$cancel" = "null" ] || [ "$cancel" = "true" ] || echo "${{ github.sha }}" )" >> $GITHUB_OUTPUT
|
||||
|
||||
is_stable_branch="$(echo "$CONFIG" | jq -r '.stable_branch // false')";
|
||||
echo "is_stable_branch=$is_stable_branch" >> $GITHUB_OUTPUT
|
||||
|
||||
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 "extra_version_identifier=${environment}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
echo "build=$BUILD" >> $GITHUB_OUTPUT
|
||||
cat $GITHUB_OUTPUT
|
||||
|
||||
validate_tests:
|
||||
runs-on: ubuntu-24.04
|
||||
needs: [ prepare_strategy ]
|
||||
if: ${{
|
||||
((github.event_name == 'workflow_dispatch' && inputs.wait_for_tests) ||
|
||||
(github.event_name == 'push' && needs.prepare_strategy.outputs.is_stable_branch == 'true') ||
|
||||
contains(github.event_name, 'pull_request') && (github.event.action == 'labeled' && github.event.label.name == 'prebuilt'))
|
||||
}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Wait for Tests
|
||||
uses: ./.github/workflows/wait-for-action # Path to where you place the action
|
||||
with:
|
||||
workflow: tests.yaml # The workflow file to monitor
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
should-wait-for-start: ${{ github.event_name == 'push' && 'true' || 'false' }}
|
||||
|
||||
build:
|
||||
needs: [ validate_tests, prepare_strategy ]
|
||||
concurrency:
|
||||
group: build-${{ github.head_ref || github.ref_name }}
|
||||
cancel-in-progress: false
|
||||
runs-on: [self-hosted, tici]
|
||||
outputs:
|
||||
new_branch: ${{ needs.prepare_strategy.outputs.new_branch }}
|
||||
version: ${{ needs.prepare_strategy.outputs.version }}
|
||||
extra_version_identifier: ${{ needs.prepare_strategy.outputs.extra_version_identifier }}
|
||||
commit_sha: ${{ github.sha }}
|
||||
if: ${{
|
||||
(always() && !cancelled() && !failure()) &&
|
||||
needs.prepare_strategy.result == 'success' &&
|
||||
(needs.validate_tests.result == 'success' || needs.validate_tests.result == 'skipped') &&
|
||||
(!contains(github.event_name, 'pull_request') ||
|
||||
(github.event.action == 'labeled' && github.event.label.name == 'prebuilt'))
|
||||
}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
ref: ${{ env.SOURCE_BRANCH }}
|
||||
repository: ${{ github.event.pull_request.head.repo.fork && github.event.pull_request.head.repo.full_name || github.repository }}
|
||||
- run: git lfs pull
|
||||
|
||||
- name: Cache SCons
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{env.SCONS_CACHE_DIR}}
|
||||
key: scons-${{ runner.os }}-${{ runner.arch }}-${{ env.SOURCE_BRANCH }}-${{ github.sha }}
|
||||
# Note: GitHub Actions enforces cache isolation between different build sources (PR builds, workflow dispatches, etc.)
|
||||
# for security. Only caches from the default branch are shared across all builds. This is by design and cannot be overridden.
|
||||
restore-keys: |
|
||||
scons-${{ runner.os }}-${{ runner.arch }}-${{ env.SOURCE_BRANCH }}
|
||||
scons-${{ runner.os }}-${{ runner.arch }}-${{ env.STAGING_SOURCE_BRANCH }}
|
||||
scons-${{ runner.os }}-${{ runner.arch }}
|
||||
|
||||
- name: Set environment variables
|
||||
id: set-env
|
||||
run: |
|
||||
echo "new_branch=${{ needs.prepare_strategy.outputs.new_branch }}" >> $GITHUB_OUTPUT
|
||||
echo "version=${{ needs.prepare_strategy.outputs.version }}" >> $GITHUB_OUTPUT
|
||||
echo "extra_version_identifier=${{ needs.prepare_strategy.outputs.extra_version_identifier }}" >> $GITHUB_OUTPUT
|
||||
echo "commit_sha=${{ github.sha }}" >> $GITHUB_OUTPUT
|
||||
|
||||
# Set up common environment
|
||||
source /etc/profile;
|
||||
export UV_PROJECT_ENVIRONMENT=${HOME}/venv
|
||||
export VIRTUAL_ENV=$UV_PROJECT_ENVIRONMENT
|
||||
printenv >> $GITHUB_ENV
|
||||
if [[ "${{ runner.debug }}" == "1" ]]; then
|
||||
cat $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Setup build environment
|
||||
run: |
|
||||
mkdir -p "${BUILD_DIR}/"
|
||||
sudo find $BUILD_DIR/ -mindepth 1 -delete
|
||||
echo "Starting build stage..."
|
||||
echo "BUILD_DIR: ${BUILD_DIR}"
|
||||
echo "CI_DIR: ${CI_DIR}"
|
||||
echo "VERSION: ${{ steps.set-env.outputs.version }}"
|
||||
echo "UV_PROJECT_ENVIRONMENT: ${UV_PROJECT_ENVIRONMENT}"
|
||||
echo "VIRTUAL_ENV: ${VIRTUAL_ENV}"
|
||||
echo "-------"
|
||||
if [[ "${{ runner.debug }}" == "1" ]]; then
|
||||
printenv
|
||||
fi
|
||||
PYTHONPATH=$PYTHONPATH:${{ github.workspace }}/ ${{ github.workspace }}/scripts/manage-powersave.py --disable
|
||||
|
||||
- name: Build Main Project
|
||||
run: |
|
||||
export PYTHONPATH="$BUILD_DIR"
|
||||
./release/release_files.py | sort | uniq | rsync -rRl${RUNNER_DEBUG:+v} --files-from=- . $BUILD_DIR/
|
||||
cd $BUILD_DIR
|
||||
sed -i '/from .board.jungle import PandaJungle, PandaJungleDFU/s/^/#/' panda/__init__.py
|
||||
echo "Building sunnypilot's modeld..."
|
||||
scons -j$(nproc) cache_dir=${{env.SCONS_CACHE_DIR}} --minimal sunnypilot/modeld
|
||||
echo "Building sunnypilot's modeld_v2..."
|
||||
scons -j$(nproc) cache_dir=${{env.SCONS_CACHE_DIR}} --minimal sunnypilot/modeld_v2
|
||||
echo "Building sunnypilot's locationd..."
|
||||
scons -j2 cache_dir=${{env.SCONS_CACHE_DIR}} --minimal sunnypilot/selfdrive/locationd
|
||||
echo "Building openpilot's locationd..."
|
||||
scons -j$(nproc) cache_dir=${{env.SCONS_CACHE_DIR}} --minimal selfdrive/locationd
|
||||
echo "Building rest of sunnypilot"
|
||||
scons -j$(nproc) cache_dir=${{env.SCONS_CACHE_DIR}} --minimal
|
||||
touch ${BUILD_DIR}/prebuilt
|
||||
if [[ "${{ runner.debug }}" == "1" ]]; then
|
||||
ls -la ${BUILD_DIR}
|
||||
fi
|
||||
|
||||
- name: Prepare Output
|
||||
run: |
|
||||
sudo rm -rf ${OUTPUT_DIR}
|
||||
mkdir -p ${OUTPUT_DIR}
|
||||
rsync -am${RUNNER_DEBUG:+v} \
|
||||
--exclude='.sconsign.dblite' \
|
||||
--exclude='*.a' \
|
||||
--exclude='*.o' \
|
||||
--exclude='*.os' \
|
||||
--exclude='*.pyc' \
|
||||
--exclude='moc_*' \
|
||||
--exclude='__pycache__' \
|
||||
--exclude='Jenkinsfile' \
|
||||
--exclude='**/release/' \
|
||||
--exclude='**/.github/' \
|
||||
--exclude='**/selfdrive/ui/replay/' \
|
||||
--exclude='**/__pycache__/' \
|
||||
--exclude='${{env.SCONS_CACHE_DIR}}' \
|
||||
--exclude='**/.git/' \
|
||||
--exclude='**/SConstruct' \
|
||||
--exclude='**/SConscript' \
|
||||
--exclude='**/.venv/' \
|
||||
--exclude='selfdrive/modeld/models/driving_vision.onnx' \
|
||||
--exclude='selfdrive/modeld/models/driving_policy.onnx' \
|
||||
--exclude='sunnypilot/modeld*/models/supercombo.onnx' \
|
||||
--exclude='third_party/*x86*' \
|
||||
--exclude='third_party/*Darwin*' \
|
||||
--delete-excluded \
|
||||
--chown=comma:comma \
|
||||
${BUILD_DIR}/ ${OUTPUT_DIR}/
|
||||
|
||||
- name: 'Tar.gz files'
|
||||
run: |
|
||||
tar czf prebuilt.tar.gz -C ${{ env.OUTPUT_DIR }} .
|
||||
ls -la prebuilt.tar.gz
|
||||
|
||||
- name: 'Upload Artifact'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: prebuilt
|
||||
path: prebuilt.tar.gz
|
||||
|
||||
- name: Re-enable powersave
|
||||
if: always()
|
||||
run: |
|
||||
PYTHONPATH=$PYTHONPATH:${{ github.workspace }}/ ${{ github.workspace }}/scripts/manage-powersave.py --enable
|
||||
|
||||
|
||||
publish:
|
||||
concurrency:
|
||||
# We do a bit of a hack here to avoid canceling the publishing job if a new commit comes in while we're publishing by adding the sha to the group name.
|
||||
# This means that if multiple commits come in while we're publishing, they will be queued up and publish one after the other.
|
||||
# Otherwise, if a job is waiting to be published due to environment wait time, it would be canceled by a new commit and restart the wait time.
|
||||
group: ${{ needs.prepare_strategy.outputs.publish_concurrency_group }}
|
||||
cancel-in-progress: ${{ needs.prepare_strategy.outputs.cancel_publish_in_progress == 'true' }}
|
||||
if: ${{ (always() && !cancelled() && !failure()) && needs.build.result == 'success' && needs.prepare_strategy.result == 'success' && (!contains(github.event_name, 'pull_request') || (github.event.action == 'labeled' && github.event.label.name == 'prebuilt')) }}
|
||||
needs: [ build, prepare_strategy ]
|
||||
runs-on: ubuntu-24.04
|
||||
environment: ${{ needs.prepare_strategy.outputs.environment }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: prebuilt
|
||||
|
||||
- name: Untar prebuilt
|
||||
run: |
|
||||
mkdir -p ${{ env.OUTPUT_DIR }}
|
||||
tar xzf prebuilt.tar.gz -C ${{ env.OUTPUT_DIR }}
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --global user.name "github-actions[bot]"
|
||||
|
||||
- name: Publish to Public Repository
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
echo '${{ toJSON(needs.build.outputs) }}'
|
||||
ls -la ${{ env.OUTPUT_DIR }}
|
||||
|
||||
${{ env.CI_DIR }}/publish.sh \
|
||||
"${{ github.workspace }}" \
|
||||
"${{ env.OUTPUT_DIR }}" \
|
||||
"${{ needs.build.outputs.new_branch }}" \
|
||||
"${{ needs.build.outputs.version }}" \
|
||||
"https://x-access-token:${{github.token}}@github.com/sunnypilot/sunnypilot.git" \
|
||||
"${{ needs.build.outputs.extra_version_identifier }}"
|
||||
|
||||
echo ""
|
||||
echo "---- ℹ️ To update the list of branches that auto deploy prebuilts -----"
|
||||
echo ""
|
||||
echo "1. Go to: ${{ github.server_url }}/${{ github.repository }}/settings/variables/actions/AUTO_DEPLOY_PREBUILT_BRANCHES"
|
||||
echo "2. Current value: ${{ vars.AUTO_DEPLOY_PREBUILT_BRANCHES }}"
|
||||
echo "3. Update as needed (JSON array with no spaces)"
|
||||
|
||||
- name: Tag ${{ needs.prepare_strategy.outputs.environment }}
|
||||
if: ${{ needs.prepare_strategy.outputs.is_stable_branch == 'true' && (github.event_name != 'push' || !startsWith(github.ref, 'refs/tags/')) }}
|
||||
run: |
|
||||
TAG="${{ needs.prepare_strategy.outputs.environment }}/${{ needs.prepare_strategy.outputs.version }}/${{ needs.prepare_strategy.outputs.build }}"
|
||||
git tag -f -a ${TAG} -m "${{ needs.prepare_strategy.outputs.environment }} @ ${{ needs.prepare_strategy.outputs.version }} of build ${{ needs.build.outputs.build }}."
|
||||
git push -f origin ${TAG}
|
||||
|
||||
notify:
|
||||
needs:
|
||||
- prepare_strategy
|
||||
- build
|
||||
- publish
|
||||
runs-on: ubuntu-24.04
|
||||
if: ${{ (always() && !cancelled() && !failure())
|
||||
&& needs.publish.result == 'success'
|
||||
&& (!contains(github.event_name, 'pull_request') || (github.event.action == 'labeled' && github.event.label.name == 'prebuilt'))
|
||||
&& (fromJSON(vars.DEV_FEEDBACK_NOTIFICATION_BRANCHES_V2)[github.head_ref || github.ref_name] != null) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Prepare notification message
|
||||
id: message
|
||||
run: |
|
||||
TEMPLATE='${{ vars.DISCOURSE_GENERAL_UPDATE_NOTICE }}'
|
||||
export VERSION="${{ needs.prepare_strategy.outputs.version }}"
|
||||
export branch_name="${{ env.SOURCE_BRANCH }}"
|
||||
export new_branch="${{ needs.prepare_strategy.outputs.new_branch }}"
|
||||
export commit_sha="${{ github.sha }}"
|
||||
export commit_short_sha="${{ github.sha }}"
|
||||
export commit_short_sha="${commit_short_sha:0:7}"
|
||||
export extra_version_identifier="${{ needs.prepare_strategy.outputs.extra_version_identifier || github.run_number }}"
|
||||
export PUBLIC_REPO_URL="${{ env.PUBLIC_REPO_URL }}"
|
||||
|
||||
MESSAGE=$(cat << 'EOF' | envsubst
|
||||
${{ vars.DISCOURSE_GENERAL_UPDATE_NOTICE }}
|
||||
EOF
|
||||
)
|
||||
|
||||
{
|
||||
echo 'content<<EOFMARKER'
|
||||
echo "$MESSAGE"
|
||||
echo 'EOFMARKER'
|
||||
} >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
- name: Post to Discourse
|
||||
uses: ./.github/workflows/post-to-discourse
|
||||
with:
|
||||
discourse-url: ${{ vars.DISCOURSE_URL }}
|
||||
api-key: ${{ secrets.DISCOURSE_API_KEY }}
|
||||
api-username: "system"
|
||||
topic-id: ${{ fromJSON(vars.DEV_FEEDBACK_NOTIFICATION_BRANCHES_V2)[github.head_ref || github.ref_name].topic_id }}
|
||||
message: ${{ steps.message.outputs.content }}
|
||||
|
||||
manage-pr-labels:
|
||||
name: Remove prebuilt label
|
||||
runs-on: ubuntu-latest
|
||||
if: (always() && contains(github.event_name, 'pull_request') && (github.event.action == 'labeled' && github.event.label.name == 'prebuilt'))
|
||||
env:
|
||||
LABEL: prebuilt
|
||||
steps:
|
||||
- name: Remove trust-fork-pr label if present
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const prNumber = context.payload.pull_request.number;
|
||||
|
||||
await github.rest.issues.removeLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
name: process.env.LABEL
|
||||
});
|
||||
|
||||
console.log(`Removed '${process.env.LABEL}' label from PR #${prNumber}`);
|
||||
250
.github/workflows/sunnypilot-master-dev-prep.yaml
vendored
250
.github/workflows/sunnypilot-master-dev-prep.yaml
vendored
@@ -1,250 +0,0 @@
|
||||
name: Build dev
|
||||
|
||||
env:
|
||||
DEFAULT_SOURCE_BRANCH: "master"
|
||||
DEFAULT_TARGET_BRANCH: "master-dev"
|
||||
LFS_URL: 'https://gitlab.com/sunnypilot/public/sunnypilot-new-lfs.git/info/lfs'
|
||||
LFS_PUSH_URL: 'ssh://git@gitlab.com/sunnypilot/public/sunnypilot-new-lfs.git'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request_target:
|
||||
types: [ labeled ]
|
||||
branches:
|
||||
- 'master'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
source_branch:
|
||||
description: 'Source branch to reset from'
|
||||
required: true
|
||||
default: 'master'
|
||||
type: string
|
||||
target_branch:
|
||||
description: 'Target branch to reset and squash into'
|
||||
required: true
|
||||
default: 'master-dev'
|
||||
type: string
|
||||
cancel_in_progress:
|
||||
description: 'Cancel any in-progress runs of this workflow'
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}
|
||||
cancel-in-progress: ${{ inputs.cancel_in_progress || github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
|
||||
|
||||
jobs:
|
||||
reset-and-squash:
|
||||
runs-on: ubuntu-latest
|
||||
if: (
|
||||
(github.event_name == 'workflow_dispatch')
|
||||
|| (github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch))
|
||||
|| (contains(github.event_name, 'pull_request') && ((github.event.action == 'labeled' && (github.event.label.name == vars.PREBUILT_PR_LABEL || github.event.label.name == 'trust-fork-pr') && contains(github.event.pull_request.labels.*.name, vars.PREBUILT_PR_LABEL))))
|
||||
)
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Fetch all history for all branches
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
persist-credentials: false
|
||||
|
||||
- name: Wait for Tests
|
||||
uses: ./.github/workflows/wait-for-action # Path to where you place the action
|
||||
if: (
|
||||
(github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch))
|
||||
|| (contains(github.event_name, 'pull_request') && ((github.event.action == 'labeled' && (github.event.label.name == vars.PREBUILT_PR_LABEL || github.event.label.name == 'trust-fork-pr') && contains(github.event.pull_request.labels.*.name, vars.PREBUILT_PR_LABEL))))
|
||||
)
|
||||
with:
|
||||
workflow: tests.yaml # The workflow file to monitor
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
git config --global user.name 'github-actions[bot]'
|
||||
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
|
||||
|
||||
- name: Set up SSH
|
||||
uses: webfactory/ssh-agent@v0.9.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||
|
||||
- name: Add GitLab public keys
|
||||
run: |
|
||||
ssh-keyscan -H gitlab.com >> ~/.ssh/known_hosts
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install PyGithub
|
||||
|
||||
- name: Check branches exist
|
||||
run: |
|
||||
# Check if source branch exists
|
||||
if ! git ls-remote --heads origin ${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }} | grep -q "${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }}"; then
|
||||
echo "Source branch ${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }} does not exist!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Make sure we have the latest source branch
|
||||
git fetch origin ${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }}
|
||||
|
||||
# Check if target branch exists
|
||||
if ! git ls-remote --heads origin ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} | grep -q "${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}"; then
|
||||
echo "Target branch ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} does not exist, creating it from ${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }}"
|
||||
git checkout -b ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} origin/${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }}
|
||||
git push origin ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}
|
||||
else
|
||||
# Fetch target branch if it exists
|
||||
git fetch origin ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}
|
||||
fi
|
||||
|
||||
- name: Reset target branch
|
||||
run: |
|
||||
echo "Resetting ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} to match ${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }}"
|
||||
# Delete if exists and recreate pointing to source
|
||||
git branch -D ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} || true
|
||||
git branch ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} origin/${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }}
|
||||
|
||||
- name: Get PRs to squash
|
||||
id: get-prs
|
||||
run: |
|
||||
# Use GitHub API to get PRs with specific label, ordered by creation date
|
||||
PR_LIST=$(gh api graphql -f query='
|
||||
query($search_query:String!) {
|
||||
search(query: $search_query, type:ISSUE, first:40) {
|
||||
nodes {
|
||||
... on PullRequest {
|
||||
number
|
||||
headRefName
|
||||
title
|
||||
createdAt
|
||||
labels(last:10) {
|
||||
nodes {
|
||||
name
|
||||
}
|
||||
}
|
||||
headRepository {
|
||||
name
|
||||
nameWithOwner
|
||||
url
|
||||
isFork
|
||||
}
|
||||
commits(last: 1) {
|
||||
nodes {
|
||||
commit {
|
||||
statusCheckRollup {
|
||||
state
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}' -F search_query="repo:${{ github.repository }} is:pr is:open label:${{ vars.PREBUILT_PR_LABEL }},${{ vars.PREBUILT_PR_LABEL }}-c3 draft:false sort:created-asc")
|
||||
|
||||
PR_LIST=${PR_LIST//\'/}
|
||||
echo "PR_LIST=${PR_LIST}" >> $GITHUB_OUTPUT
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Process PRs
|
||||
run: |
|
||||
cp ${{ github.workspace }}/release/ci/squash_and_merge.py /tmp/squash_and_merge.py && \
|
||||
chmod +x /tmp/squash_and_merge.py && \
|
||||
python3 ${{ github.workspace }}/release/ci/squash_and_merge_prs.py \
|
||||
--pr-data '${{ steps.get-prs.outputs.PR_LIST }}' \
|
||||
--target-branch ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} \
|
||||
--squash-script-path '/tmp/squash_and_merge.py'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Update LFS Config
|
||||
run: |
|
||||
echo '[lfs]' > .lfsconfig
|
||||
echo ' url = ${{ env.LFS_URL }}' >> .lfsconfig
|
||||
echo ' pushurl = ${{ env.LFS_PUSH_URL }}' >> .lfsconfig
|
||||
echo ' locksverify = false' >> .lfsconfig
|
||||
|
||||
- name: Restore workflows from source
|
||||
run: |
|
||||
TARGET_BRANCH="${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}"
|
||||
SOURCE_BRANCH="${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }}"
|
||||
|
||||
# Ensure we are on the target branch
|
||||
git checkout $TARGET_BRANCH
|
||||
|
||||
echo "Restoring .github/workflows from $SOURCE_BRANCH"
|
||||
git checkout origin/$SOURCE_BRANCH -- .github/workflows
|
||||
|
||||
if ! git diff --cached --quiet; then
|
||||
echo "Workflows differ. Committing restoration."
|
||||
git commit -m "chore: restore .github/workflows from $SOURCE_BRANCH"
|
||||
else
|
||||
echo "Workflows match $SOURCE_BRANCH."
|
||||
fi
|
||||
|
||||
- uses: actions/create-github-app-token@v2
|
||||
id: ci-token
|
||||
with:
|
||||
app-id: ${{ secrets.CI_GITHUB_ACTIONS_TOKEN_APP_ID }}
|
||||
private-key: ${{ secrets.CI_GITHUB_ACTIONS_TOKEN_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Push changes if there are diffs
|
||||
id: push-changes
|
||||
run: |
|
||||
TARGET_BRANCH="${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}"
|
||||
|
||||
# Use the App Token to set the remote URL with authentication
|
||||
git remote set-url origin "https://x-access-token:${{ steps.ci-token.outputs.token }}@github.com/${{ github.repository }}.git"
|
||||
|
||||
# Fetch the latest from remote
|
||||
git fetch origin $TARGET_BRANCH
|
||||
|
||||
# Check for diffs between local and remote
|
||||
if git diff $TARGET_BRANCH origin/$TARGET_BRANCH --quiet; then
|
||||
echo "No changes to push - local and remote branches are identical"
|
||||
echo "has_changes=false" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Push with the authenticated origin
|
||||
if ! git push origin $TARGET_BRANCH --force; then
|
||||
echo "Failed to push changes to $TARGET_BRANCH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Branch $TARGET_BRANCH has been reset and updated with squashed PRs"
|
||||
echo "has_changes=true" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Trigger and wait for selfdrive tests
|
||||
if: steps.push-changes.outputs.has_changes == 'true'
|
||||
run: |
|
||||
echo "Triggering selfdrive tests..."
|
||||
gh workflow run tests.yaml --ref "${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}"
|
||||
|
||||
echo "Sleeping for 120s to give plenty of time for the action to start and then we wait"
|
||||
sleep 120
|
||||
|
||||
echo "Getting latest run ID..."
|
||||
RUN_ID=$(gh run list --workflow=tests.yaml --branch="${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}" --limit=1 --json databaseId --jq '.[0].databaseId')
|
||||
|
||||
echo "Watching run ID: $RUN_ID"
|
||||
gh run watch "$RUN_ID"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Trigger prebuilt workflow
|
||||
if: success() && steps.push-changes.outputs.has_changes == 'true'
|
||||
run: |
|
||||
gh workflow run sunnypilot-build-prebuilt.yaml --ref "${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
78
.github/workflows/test-discourse.yaml.yml
vendored
78
.github/workflows/test-discourse.yaml.yml
vendored
@@ -1,78 +0,0 @@
|
||||
name: Debug Discourse Posting
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
test-discourse-post:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Post test message to Discourse
|
||||
uses: ./.github/workflows/post-to-discourse
|
||||
with:
|
||||
discourse-url: ${{ vars.DISCOURSE_URL }}
|
||||
api-key: ${{ secrets.DISCOURSE_API_KEY }}
|
||||
api-username: ${{ secrets.DISCOURSE_API_USERNAME }}
|
||||
topic-id: ${{ vars.DISCOURSE_UPDATES_TOPIC_ID }}
|
||||
message: |
|
||||
## 🧪 Test Post from GitHub Actions
|
||||
|
||||
**This is a test post to verify Discourse integration**
|
||||
|
||||
- **Workflow**: ${{ github.workflow }}
|
||||
- **Run Number**: #${{ github.run_number }}
|
||||
- **Branch**: `${{ github.ref_name }}`
|
||||
- **Commit**: ${{ github.sha }}
|
||||
- **Actor**: @${{ github.actor }}
|
||||
- **Timestamp**: ${{ github.event.head_commit.timestamp }}
|
||||
|
||||
---
|
||||
|
||||
### Fake Build Info (for testing)
|
||||
- **Version**: 0.9.8-test
|
||||
- **Build**: #42
|
||||
- **Branch**: release-test
|
||||
|
||||
[View workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
||||
|
||||
*This is an automated test message. Drive safe! 🚗💨*
|
||||
|
||||
|
||||
- name: Create topic on Discourse
|
||||
uses: ./.github/workflows/post-to-discourse
|
||||
with:
|
||||
discourse-url: ${{ vars.DISCOURSE_URL }}
|
||||
api-key: ${{ secrets.DISCOURSE_API_KEY }}
|
||||
api-username: ${{ secrets.DISCOURSE_API_USERNAME }}
|
||||
#topic-id: ${{ vars.DISCOURSE_UPDATES_TOPIC_ID }}
|
||||
category-id: 4
|
||||
title: "This is a test of a new topic instead of a reply"
|
||||
message: |
|
||||
## 🧪 Test Post from GitHub Actions
|
||||
|
||||
**This is a test post to verify Discourse integration**
|
||||
|
||||
- **Workflow**: ${{ github.workflow }}
|
||||
- **Run Number**: #${{ github.run_number }}
|
||||
- **Branch**: `${{ github.ref_name }}`
|
||||
- **Commit**: ${{ github.sha }}
|
||||
- **Actor**: @${{ github.actor }}
|
||||
- **Timestamp**: ${{ github.event.head_commit.timestamp }}
|
||||
|
||||
---
|
||||
|
||||
### Fake Build Info (for testing)
|
||||
- **Version**: 0.9.8-test
|
||||
- **Build**: #42
|
||||
- **Branch**: release-test
|
||||
|
||||
[View workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
||||
|
||||
*This is an automated test message. Drive safe! 🚗💨*
|
||||
- name: Display results
|
||||
if: always()
|
||||
run: |
|
||||
echo "::notice::Discourse post test completed"
|
||||
echo "Check your Discourse topic to verify the post appeared correctly"
|
||||
325
.github/workflows/tests.yaml
vendored
325
.github/workflows/tests.yaml
vendored
@@ -1,325 +0,0 @@
|
||||
name: tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
inputs:
|
||||
run_number:
|
||||
default: '1'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
concurrency:
|
||||
group: tests-ci-run-${{ inputs.run_number }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
PYTHONWARNINGS: error
|
||||
BASE_IMAGE: sunnypilot-base
|
||||
AZURE_TOKEN: ${{ secrets.AZURE_COMMADATACI_OPENPILOTCI_TOKEN }}
|
||||
|
||||
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
|
||||
BUILD: release/ci/docker_build_sp.sh base
|
||||
|
||||
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c
|
||||
|
||||
PYTEST: pytest --continue-on-collection-errors --durations=0 -n logical
|
||||
|
||||
jobs:
|
||||
build_release:
|
||||
name: build release
|
||||
runs-on: ${{
|
||||
(github.repository == 'commaai/openpilot') &&
|
||||
((github.event_name != 'pull_request') ||
|
||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||
|| fromJSON('["ubuntu-24.04"]') }}
|
||||
env:
|
||||
STRIPPED_DIR: /tmp/releasepilot
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Getting LFS files
|
||||
uses: nick-fields/retry@7152eba30c6575329ac0576536151aca5a72780e
|
||||
with:
|
||||
timeout_minutes: 2
|
||||
max_attempts: 3
|
||||
command: git lfs pull
|
||||
- name: Build devel
|
||||
timeout-minutes: 1
|
||||
run: TARGET_DIR=$STRIPPED_DIR release/build_stripped.sh
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
- name: Build openpilot and run checks
|
||||
timeout-minutes: ${{ ((steps.restore-scons-cache.outputs.cache-hit == 'true') && 10 || 30) }} # allow more time when we missed the scons cache
|
||||
run: |
|
||||
cd $STRIPPED_DIR
|
||||
${{ env.RUN }} "python3 system/manager/build.py"
|
||||
- name: Run tests
|
||||
timeout-minutes: 1
|
||||
run: |
|
||||
cd $STRIPPED_DIR
|
||||
${{ env.RUN }} "release/check-dirty.sh"
|
||||
- name: Check submodules
|
||||
if: github.repository == 'sunnypilot/sunnypilot'
|
||||
timeout-minutes: 3
|
||||
run: |
|
||||
if [ "${{ github.ref }}" != "refs/heads/master" ]; then
|
||||
git fetch origin master:refs/remotes/origin/master
|
||||
|
||||
SUBMODULE_PATHS=$(git diff origin/master HEAD --name-only | grep -E '^[^/]+$' | while read path; do
|
||||
if git ls-files --stage "$path" | grep -q "^160000"; then
|
||||
echo "$path"
|
||||
fi
|
||||
done | tr '\n' ' ')
|
||||
|
||||
if [ -n "$SUBMODULE_PATHS" ]; then
|
||||
echo "Changed submodule paths: $SUBMODULE_PATHS"
|
||||
export SUBMODULE_PATHS="$SUBMODULE_PATHS"
|
||||
export CHECK_PR_REFS=true
|
||||
fi
|
||||
fi
|
||||
release/check-submodules.sh
|
||||
|
||||
build:
|
||||
runs-on: ${{
|
||||
(github.repository == 'commaai/openpilot') &&
|
||||
((github.event_name != 'pull_request') ||
|
||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||
|| fromJSON('["ubuntu-24.04"]') }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Setup docker push
|
||||
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'sunnypilot/sunnypilot'
|
||||
run: |
|
||||
echo "PUSH_IMAGE=true" >> "$GITHUB_ENV"
|
||||
$DOCKER_LOGIN
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
- uses: ./.github/workflows/compile-openpilot
|
||||
timeout-minutes: 30
|
||||
|
||||
build_mac:
|
||||
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' }}
|
||||
if: false # There'll be one day that this works. That day is not today.
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- run: echo "CACHE_COMMIT_DATE=$(git log -1 --pretty='format:%cd' --date=format:'%Y-%m-%d-%H:%M')" >> $GITHUB_ENV
|
||||
- name: Homebrew cache
|
||||
uses: ./.github/workflows/auto-cache
|
||||
with:
|
||||
save: false # No need save here if we manually save it later conditionally
|
||||
path: ~/Library/Caches/Homebrew
|
||||
key: brew-macos-${{ hashFiles('tools/Brewfile') }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
brew-macos-${{ hashFiles('tools/Brewfile') }}
|
||||
brew-macos-
|
||||
- name: Install dependencies
|
||||
run: ./tools/mac_setup.sh
|
||||
env:
|
||||
PYTHONWARNINGS: default # package install has DeprecationWarnings
|
||||
HOMEBREW_DISPLAY_INSTALL_TIMES: 1
|
||||
- name: Save Homebrew cache
|
||||
uses: actions/cache/save@v4
|
||||
if: github.ref == 'refs/heads/master'
|
||||
with:
|
||||
path: ~/Library/Caches/Homebrew
|
||||
key: brew-macos-${{ hashFiles('tools/Brewfile') }}-${{ github.sha }}
|
||||
- run: git lfs pull
|
||||
- name: Getting scons cache
|
||||
uses: ./.github/workflows/auto-cache
|
||||
with:
|
||||
save: false # No need save here if we manually save it later conditionally
|
||||
path: /tmp/scons_cache
|
||||
key: scons-${{ runner.arch }}-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
scons-${{ runner.arch }}-macos-${{ env.CACHE_COMMIT_DATE }}
|
||||
scons-${{ runner.arch }}-macos
|
||||
- name: Building openpilot
|
||||
run: . .venv/bin/activate && scons -j$(nproc)
|
||||
- name: Save scons cache
|
||||
uses: actions/cache/save@v4
|
||||
if: github.ref == 'refs/heads/master'
|
||||
with:
|
||||
path: /tmp/scons_cache
|
||||
key: scons-${{ runner.arch }}-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
|
||||
|
||||
static_analysis:
|
||||
name: static analysis
|
||||
runs-on: ${{
|
||||
(github.repository == 'commaai/openpilot') &&
|
||||
((github.event_name != 'pull_request') ||
|
||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||
|| fromJSON('["ubuntu-24.04"]') }}
|
||||
env:
|
||||
PYTHONWARNINGS: default
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
- name: Static analysis
|
||||
timeout-minutes: 1
|
||||
run: ${{ env.RUN }} "scripts/lint/lint.sh"
|
||||
|
||||
unit_tests:
|
||||
name: unit tests
|
||||
runs-on: ${{
|
||||
(github.repository == 'commaai/openpilot') &&
|
||||
((github.event_name != 'pull_request') ||
|
||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||
|| fromJSON('["ubuntu-24.04"]') }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
id: setup-step
|
||||
- name: Build openpilot
|
||||
run: ${{ env.RUN }} "scons -j$(nproc)"
|
||||
- name: Run unit tests
|
||||
timeout-minutes: ${{ contains(runner.name, 'nsc') && ((steps.setup-step.outputs.duration < 18) && 1 || 2) || 999 }}
|
||||
run: |
|
||||
${{ env.RUN }} "source selfdrive/test/setup_xvfb.sh && \
|
||||
# Pre-compile Python bytecode so each pytest worker doesn't need to
|
||||
$PYTEST --collect-only -m 'not slow' -qq && \
|
||||
MAX_EXAMPLES=1 $PYTEST -m 'not slow' && \
|
||||
chmod -R 777 /tmp/comma_download_cache"
|
||||
|
||||
process_replay:
|
||||
name: process replay
|
||||
if: false # disable process_replay for forks
|
||||
runs-on: ${{
|
||||
(github.repository == 'commaai/openpilot') &&
|
||||
((github.event_name != 'pull_request') ||
|
||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||
|| fromJSON('["ubuntu-24.04"]') }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
id: setup-step
|
||||
- name: Cache test routes
|
||||
id: dependency-cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: .ci_cache/comma_download_cache
|
||||
key: proc-replay-${{ hashFiles('selfdrive/test/process_replay/ref_commit', 'selfdrive/test/process_replay/test_processes.py') }}
|
||||
- name: Build openpilot
|
||||
run: |
|
||||
${{ env.RUN }} "scons -j$(nproc)"
|
||||
- name: Run replay
|
||||
timeout-minutes: ${{ contains(runner.name, 'nsc') && (steps.dependency-cache.outputs.cache-hit == 'true') && ((steps.setup-step.outputs.duration < 18) && 1 || 2) || 20 }}
|
||||
run: |
|
||||
${{ env.RUN }} "selfdrive/test/process_replay/test_processes.py -j$(nproc) && \
|
||||
chmod -R 777 /tmp/comma_download_cache"
|
||||
- name: Print diff
|
||||
id: print-diff
|
||||
if: always()
|
||||
run: cat selfdrive/test/process_replay/diff.txt
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
continue-on-error: true
|
||||
with:
|
||||
name: process_replay_diff.txt
|
||||
path: selfdrive/test/process_replay/diff.txt
|
||||
- name: Upload reference logs
|
||||
if: false # TODO: move this to github instead of azure
|
||||
run: |
|
||||
${{ env.RUN }} "unset PYTHONWARNINGS && AZURE_TOKEN='$AZURE_TOKEN' python3 selfdrive/test/process_replay/test_processes.py -j$(nproc) --upload-only"
|
||||
- name: Run regen
|
||||
if: false
|
||||
timeout-minutes: 4
|
||||
run: |
|
||||
${{ env.RUN }} "ONNXCPU=1 $PYTEST selfdrive/test/process_replay/test_regen.py && \
|
||||
chmod -R 777 /tmp/comma_download_cache"
|
||||
|
||||
simulator_driving:
|
||||
name: simulator driving
|
||||
runs-on: ${{
|
||||
(github.repository == 'commaai/openpilot') &&
|
||||
((github.event_name != 'pull_request') ||
|
||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||
|| fromJSON('["ubuntu-24.04"]') }}
|
||||
if: false # FIXME: Started to timeout recently
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
id: setup-step
|
||||
- name: Build openpilot
|
||||
run: |
|
||||
${{ env.RUN }} "scons -j$(nproc)"
|
||||
- name: Driving test
|
||||
timeout-minutes: ${{ (steps.setup-step.outputs.duration < 18) && 1 || 2 }}
|
||||
run: |
|
||||
${{ env.RUN }} "source selfdrive/test/setup_xvfb.sh && \
|
||||
source selfdrive/test/setup_vsound.sh && \
|
||||
CI=1 pytest -s tools/sim/tests/test_metadrive_bridge.py"
|
||||
|
||||
create_raylib_ui_report:
|
||||
name: Create raylib UI Report
|
||||
runs-on: ${{
|
||||
(github.repository == 'commaai/openpilot') &&
|
||||
((github.event_name != 'pull_request') ||
|
||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||
|| fromJSON('["ubuntu-24.04"]') }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
- name: Build openpilot
|
||||
run: ${{ env.RUN }} "scons -j$(nproc)"
|
||||
- name: Create raylib UI Report
|
||||
run: >
|
||||
${{ env.RUN }} "PYTHONWARNINGS=ignore &&
|
||||
source selfdrive/test/setup_xvfb.sh &&
|
||||
python3 selfdrive/ui/tests/test_ui/raylib_screenshots.py"
|
||||
- name: Upload Raylib UI Report
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: raylib-report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
||||
path: selfdrive/ui/tests/test_ui/raylib_report/screenshots
|
||||
|
||||
create_mici_raylib_ui_report:
|
||||
name: Create mici raylib UI Report
|
||||
runs-on: ${{
|
||||
(github.repository == 'commaai/openpilot') &&
|
||||
((github.event_name != 'pull_request') ||
|
||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||
|| fromJSON('["ubuntu-24.04"]') }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
- name: Build openpilot
|
||||
run: ${{ env.RUN }} "scons -j$(nproc)"
|
||||
- name: Create mici raylib UI Report
|
||||
run: >
|
||||
${{ env.RUN }} "PYTHONWARNINGS=ignore &&
|
||||
source selfdrive/test/setup_xvfb.sh &&
|
||||
WINDOWED=1 python3 selfdrive/ui/tests/diff/replay.py"
|
||||
- name: Upload Raylib UI Report
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: mici-raylib-report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
||||
path: selfdrive/ui/tests/diff/report
|
||||
@@ -1,4 +1,4 @@
|
||||
name: "raylib ui preview"
|
||||
name: "ui preview"
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@@ -8,20 +8,18 @@ on:
|
||||
branches:
|
||||
- 'master'
|
||||
paths:
|
||||
- 'selfdrive/assets/**'
|
||||
- 'selfdrive/ui/**'
|
||||
- 'system/ui/**'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
UI_JOB_NAME: "Create raylib UI Report"
|
||||
UI_JOB_NAME: "Create UI Report"
|
||||
REPORT_NAME: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
||||
SHA: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.sha || github.event.pull_request.head.sha }}
|
||||
BRANCH_NAME: "openpilot/pr-${{ github.event.number }}-raylib-ui"
|
||||
BRANCH_NAME: "openpilot/pr-${{ github.event.number }}"
|
||||
|
||||
jobs:
|
||||
preview:
|
||||
if: github.repository == 'sunnypilot/sunnypilot'
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
name: preview
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
@@ -54,37 +52,37 @@ jobs:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run_id: ${{ steps.get_run_id.outputs.run_id }}
|
||||
search_artifacts: true
|
||||
name: raylib-report-1-${{ env.REPORT_NAME }}
|
||||
name: report-1-${{ env.REPORT_NAME }}
|
||||
path: ${{ github.workspace }}/pr_ui
|
||||
|
||||
- name: Getting master ui
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: sunnypilot/ci-artifacts
|
||||
repository: commaai/ci-artifacts
|
||||
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
||||
path: ${{ github.workspace }}/master_ui_raylib
|
||||
ref: openpilot_master_ui_raylib
|
||||
path: ${{ github.workspace }}/master_ui
|
||||
ref: openpilot_master_ui
|
||||
|
||||
- name: Saving new master ui
|
||||
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
|
||||
working-directory: ${{ github.workspace }}/master_ui_raylib
|
||||
working-directory: ${{ github.workspace }}/master_ui
|
||||
run: |
|
||||
git checkout --orphan=new_master_ui_raylib
|
||||
git checkout --orphan=new_master_ui
|
||||
git rm -rf *
|
||||
git branch -D openpilot_master_ui_raylib
|
||||
git branch -m openpilot_master_ui_raylib
|
||||
git branch -D openpilot_master_ui
|
||||
git branch -m openpilot_master_ui
|
||||
git config user.name "GitHub Actions Bot"
|
||||
git config user.email "<>"
|
||||
mv ${{ github.workspace }}/pr_ui/*.png .
|
||||
git add .
|
||||
git commit -m "raylib screenshots for commit ${{ env.SHA }}"
|
||||
git push origin openpilot_master_ui_raylib --force
|
||||
git commit -m "screenshots for commit ${{ env.SHA }}"
|
||||
git push origin openpilot_master_ui --force
|
||||
|
||||
- name: Finding diff
|
||||
if: github.event_name == 'pull_request_target'
|
||||
id: find_diff
|
||||
run: >-
|
||||
sudo apt-get update && sudo apt-get install -y imagemagick
|
||||
sudo apt-get install -y imagemagick
|
||||
|
||||
scenes=$(find ${{ github.workspace }}/pr_ui/*.png -type f -printf "%f\n" | cut -d '.' -f 1 | grep -v 'pair_device')
|
||||
A=($scenes)
|
||||
@@ -95,38 +93,26 @@ jobs:
|
||||
|
||||
for ((i=0; i<${#A[*]}; i=i+1));
|
||||
do
|
||||
# Check if the master file exists
|
||||
if [ ! -f "${{ github.workspace }}/master_ui_raylib/${A[$i]}.png" ]; then
|
||||
# This is a new file in PR UI that doesn't exist in master
|
||||
DIFF="${DIFF}<details open>"
|
||||
DIFF="${DIFF}<summary>${A[$i]} : \$\${\\color{cyan}\\text{NEW}}\$\$</summary>"
|
||||
DIFF="${DIFF}<table>"
|
||||
|
||||
DIFF="${DIFF}<tr>"
|
||||
DIFF="${DIFF} <td> <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}.png\"> </td>"
|
||||
DIFF="${DIFF}</tr>"
|
||||
|
||||
DIFF="${DIFF}</table>"
|
||||
DIFF="${DIFF}</details>"
|
||||
elif ! compare -fuzz 2% -highlight-color DeepSkyBlue1 -lowlight-color Black -compose Src ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png; then
|
||||
if ! compare -fuzz 2% -highlight-color DeepSkyBlue1 -lowlight-color Black -compose Src ${{ github.workspace }}/master_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png; then
|
||||
convert ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png -transparent black mask.png
|
||||
composite mask.png ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png
|
||||
convert -delay 100 ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png -loop 0 ${{ github.workspace }}/pr_ui/${A[$i]}_diff.gif
|
||||
composite mask.png ${{ github.workspace }}/master_ui/${A[$i]}.png composite_diff.png
|
||||
convert -delay 100 ${{ github.workspace }}/master_ui/${A[$i]}.png composite_diff.png -loop 0 ${{ github.workspace }}/pr_ui/${A[$i]}_diff.gif
|
||||
|
||||
mv ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_master_ref.png
|
||||
mv ${{ github.workspace }}/master_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_master_ref.png
|
||||
|
||||
DIFF="${DIFF}<details open>"
|
||||
DIFF="${DIFF}<summary>${A[$i]} : \$\${\\color{red}\\text{DIFFERENT}}\$\$</summary>"
|
||||
DIFF="${DIFF}<table>"
|
||||
|
||||
DIFF="${DIFF}<tr>"
|
||||
DIFF="${DIFF} <td> master <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}_master_ref.png\"> </td>"
|
||||
DIFF="${DIFF} <td> proposed <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}.png\"> </td>"
|
||||
DIFF="${DIFF} <td> master <img src=\"https://raw.githubusercontent.com/commaai/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}_master_ref.png\"> </td>"
|
||||
DIFF="${DIFF} <td> proposed <img src=\"https://raw.githubusercontent.com/commaai/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}.png\"> </td>"
|
||||
DIFF="${DIFF}</tr>"
|
||||
|
||||
DIFF="${DIFF}<tr>"
|
||||
DIFF="${DIFF} <td> diff <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}_diff.png\"> </td>"
|
||||
DIFF="${DIFF} <td> composite diff <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}_diff.gif\"> </td>"
|
||||
DIFF="${DIFF} <td> diff <img src=\"https://raw.githubusercontent.com/commaai/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}_diff.png\"> </td>"
|
||||
DIFF="${DIFF} <td> composite diff <img src=\"https://raw.githubusercontent.com/commaai/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}_diff.gif\"> </td>"
|
||||
DIFF="${DIFF}</tr>"
|
||||
|
||||
DIFF="${DIFF}</table>"
|
||||
@@ -139,7 +125,7 @@ jobs:
|
||||
if [[ $INDEX -eq 0 ]]; then
|
||||
TABLE="${TABLE}<tr>"
|
||||
fi
|
||||
TABLE="${TABLE} <td> <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}.png\"> </td>"
|
||||
TABLE="${TABLE} <td> <img src=\"https://raw.githubusercontent.com/commaai/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}.png\"> </td>"
|
||||
if [[ $INDEX -eq 1 || $(($i + 1)) -eq ${#A[*]} ]]; then
|
||||
TABLE="${TABLE}</tr>"
|
||||
fi
|
||||
@@ -151,7 +137,7 @@ jobs:
|
||||
|
||||
- name: Saving proposed ui
|
||||
if: github.event_name == 'pull_request_target'
|
||||
working-directory: ${{ github.workspace }}/master_ui_raylib
|
||||
working-directory: ${{ github.workspace }}/master_ui
|
||||
run: |
|
||||
git config user.name "GitHub Actions Bot"
|
||||
git config user.email "<>"
|
||||
@@ -159,7 +145,7 @@ jobs:
|
||||
git rm -rf *
|
||||
mv ${{ github.workspace }}/pr_ui/* .
|
||||
git add .
|
||||
git commit -m "raylib screenshots for PR #${{ github.event.number }}"
|
||||
git commit -m "screenshots for PR #${{ github.event.number }}"
|
||||
git push origin ${{ env.BRANCH_NAME }} --force
|
||||
|
||||
- name: Comment Screenshots on PR
|
||||
@@ -167,9 +153,9 @@ jobs:
|
||||
uses: thollander/actions-comment-pull-request@v2
|
||||
with:
|
||||
message: |
|
||||
<!-- _(run_id_screenshots_raylib **${{ github.run_id }}**)_ -->
|
||||
## raylib UI Preview
|
||||
<!-- _(run_id_screenshots **${{ github.run_id }}**)_ -->
|
||||
## UI Preview
|
||||
${{ steps.find_diff.outputs.DIFF }}
|
||||
comment_tag: run_id_screenshots_raylib
|
||||
comment_tag: run_id_screenshots
|
||||
pr_number: ${{ github.event.number }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
52
.github/workflows/wait-for-action/action.yaml
vendored
52
.github/workflows/wait-for-action/action.yaml
vendored
@@ -1,52 +0,0 @@
|
||||
name: 'Wait for Tests'
|
||||
description: 'Action to wait for workflow tests to start and complete'
|
||||
inputs:
|
||||
workflow:
|
||||
description: 'The workflow file name to monitor'
|
||||
required: true
|
||||
default: 'tests.yaml'
|
||||
branch:
|
||||
description: 'The branch to monitor (defaults to current branch)'
|
||||
required: false
|
||||
default: ''
|
||||
github-token:
|
||||
description: 'GitHub token for API access'
|
||||
required: true
|
||||
wait-time:
|
||||
description: 'Initial sleep time in seconds before monitoring starts'
|
||||
required: false
|
||||
default: '30'
|
||||
should-wait-for-start:
|
||||
description: 'Whether to wait for tests to start'
|
||||
required: false
|
||||
default: false
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Wait for tests to start
|
||||
if: inputs.should-wait-for-start == 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Sleeping for ${{ inputs.wait-time }} seconds to give some time for the action to start and then we'll wait"
|
||||
sleep ${{ inputs.wait-time }}
|
||||
|
||||
- name: Wait for tests to finish
|
||||
shell: bash
|
||||
run: |
|
||||
BRANCH="${{ inputs.branch || github.head_ref || github.ref_name }}"
|
||||
|
||||
echo "Looking for workflow runs of ${{ inputs.workflow }} on branch $BRANCH"
|
||||
RUN_ID=$(gh run list --workflow=${{ inputs.workflow }} --branch="$BRANCH" --limit=1 --json databaseId --jq '.[0].databaseId')
|
||||
echo "Watching run ID: $RUN_ID"
|
||||
gh run watch "$RUN_ID"
|
||||
|
||||
CONCLUSION=$(gh run view "$RUN_ID" --json conclusion --jq '.conclusion')
|
||||
echo "Run concluded with: $CONCLUSION"
|
||||
|
||||
if [[ "$CONCLUSION" != "success" ]]; then
|
||||
echo "❌ Workflow run failed with conclusion: $CONCLUSION"
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ inputs.github-token }}
|
||||
27
.gitignore
vendored
27
.gitignore
vendored
@@ -10,13 +10,12 @@ venv/
|
||||
.overlay_init
|
||||
.overlay_consistent
|
||||
.sconsign.dblite
|
||||
model2.png
|
||||
a.out
|
||||
.hypothesis
|
||||
.cache/
|
||||
|
||||
/docs_site/
|
||||
|
||||
*.mp4
|
||||
*.dylib
|
||||
*.DSYM
|
||||
*.d
|
||||
@@ -36,23 +35,32 @@ a.out
|
||||
*.class
|
||||
*.pyxbldc
|
||||
*.vcd
|
||||
*.mo
|
||||
*.qm
|
||||
*_pyx.cpp
|
||||
*.stats
|
||||
config.json
|
||||
clcache
|
||||
compile_commands.json
|
||||
compare_runtime*.html
|
||||
|
||||
persist
|
||||
selfdrive/pandad/pandad
|
||||
cereal/services.h
|
||||
cereal/gen
|
||||
cereal/messaging/bridge
|
||||
selfdrive/logcatd/logcatd
|
||||
selfdrive/mapd/default_speeds_by_region.json
|
||||
system/proclogd/proclogd
|
||||
selfdrive/ui/translations/alerts_generated.h
|
||||
selfdrive/ui/translations/tmp
|
||||
selfdrive/test/longitudinal_maneuvers/out
|
||||
selfdrive/car/tests/cars_dump
|
||||
system/camerad/camerad
|
||||
system/camerad/test/ae_gray_test
|
||||
|
||||
notebooks
|
||||
hyperthneed
|
||||
provisioning
|
||||
|
||||
.coverage*
|
||||
coverage.xml
|
||||
htmlcov
|
||||
@@ -64,12 +72,10 @@ flycheck_*
|
||||
cppcheck_report.txt
|
||||
comma*.sh
|
||||
|
||||
selfdrive/modeld/thneed/compile
|
||||
selfdrive/modeld/models/*.thneed
|
||||
selfdrive/modeld/models/*.pkl
|
||||
sunnypilot/modeld*/thneed/compile
|
||||
sunnypilot/modeld*/models/*.thneed
|
||||
sunnypilot/modeld*/models/*.pkl
|
||||
|
||||
# openpilot log files
|
||||
*.bz2
|
||||
*.zst
|
||||
|
||||
@@ -98,8 +104,3 @@ Pipfile
|
||||
# Ignore all local history of files
|
||||
.history
|
||||
.ionide
|
||||
|
||||
### JetBrains ###
|
||||
!.idea/customTargets.xml
|
||||
!.idea/tools/*
|
||||
!.run/*
|
||||
|
||||
13
.gitmodules
vendored
13
.gitmodules
vendored
@@ -1,21 +1,18 @@
|
||||
[submodule "panda"]
|
||||
path = panda
|
||||
url = https://github.com/sunnyhaibin/panda.git
|
||||
url = ../../commaai/panda.git
|
||||
[submodule "opendbc"]
|
||||
path = opendbc_repo
|
||||
url = https://github.com/sunnypilot/opendbc.git
|
||||
url = ../../commaai/opendbc.git
|
||||
[submodule "msgq"]
|
||||
path = msgq_repo
|
||||
url = https://github.com/commaai/msgq.git
|
||||
url = ../../commaai/msgq.git
|
||||
[submodule "rednose_repo"]
|
||||
path = rednose_repo
|
||||
url = https://github.com/commaai/rednose.git
|
||||
url = ../../commaai/rednose.git
|
||||
[submodule "teleoprtc_repo"]
|
||||
path = teleoprtc_repo
|
||||
url = https://github.com/commaai/teleoprtc
|
||||
url = ../../commaai/teleoprtc
|
||||
[submodule "tinygrad"]
|
||||
path = tinygrad_repo
|
||||
url = https://github.com/commaai/tinygrad.git
|
||||
[submodule "sunnypilot/neural_network_data"]
|
||||
path = sunnypilot/neural_network_data
|
||||
url = https://github.com/sunnypilot/neural-network-data.git
|
||||
|
||||
25
.idea/customTargets.xml
generated
25
.idea/customTargets.xml
generated
@@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CLionExternalBuildManager">
|
||||
<target id="a62f99e8-5ec4-434c-8122-49efed5af108" name="uv Scons Build Debug" defaultType="TOOL">
|
||||
<configuration id="b93ec964-16e5-4962-a12e-3ed360ce8f02" name="uv Scons Build Debug">
|
||||
<build type="TOOL">
|
||||
<tool actionId="Tool_External Tools_uv Scons Build Debug" />
|
||||
</build>
|
||||
<clean type="TOOL">
|
||||
<tool actionId="Tool_External Tools_uv Scons Clean" />
|
||||
</clean>
|
||||
</configuration>
|
||||
</target>
|
||||
<target id="edd8ad9d-183b-467c-a355-0d9a0ecab026" name="uv Scons Build Release" defaultType="TOOL">
|
||||
<configuration id="09523339-5ce3-4223-ab9e-904f38ad7752" name="uv Scons Build Release">
|
||||
<build type="TOOL">
|
||||
<tool actionId="Tool_External Tools_uv Scons Build Release" />
|
||||
</build>
|
||||
<clean type="TOOL">
|
||||
<tool actionId="Tool_External Tools_uv Scons Clean" />
|
||||
</clean>
|
||||
</configuration>
|
||||
</target>
|
||||
</component>
|
||||
</project>
|
||||
23
.idea/tools/External Tools.xml
generated
23
.idea/tools/External Tools.xml
generated
@@ -1,23 +0,0 @@
|
||||
<toolSet name="External Tools">
|
||||
<tool name="uv Scons Build Debug" showInMainMenu="false" showInEditor="false" showInProject="false" showInSearchPopup="false" disabled="false" useConsole="true" showConsoleOnStdOut="false" showConsoleOnStdErr="false" synchronizeAfterRun="true">
|
||||
<exec>
|
||||
<option name="COMMAND" value="bash" />
|
||||
<option name="PARAMETERS" value="-c "source .venv/bin/activate && scons -u -j$(nproc) --ccflags=\"-fno-inline\""" />
|
||||
<option name="WORKING_DIRECTORY" value="$ProjectFileDir$" />
|
||||
</exec>
|
||||
</tool>
|
||||
<tool name="uv Scons Clean" showInMainMenu="false" showInEditor="false" showInProject="false" showInSearchPopup="false" disabled="false" useConsole="true" showConsoleOnStdOut="false" showConsoleOnStdErr="false" synchronizeAfterRun="true">
|
||||
<exec>
|
||||
<option name="COMMAND" value="bash" />
|
||||
<option name="PARAMETERS" value="-c "source .venv/bin/activate && scons -c" " />
|
||||
<option name="WORKING_DIRECTORY" value="$ProjectFileDir$" />
|
||||
</exec>
|
||||
</tool>
|
||||
<tool name="uv Scons Build Release" showInMainMenu="false" showInEditor="false" showInProject="false" showInSearchPopup="false" disabled="false" useConsole="true" showConsoleOnStdOut="false" showConsoleOnStdErr="false" synchronizeAfterRun="true">
|
||||
<exec>
|
||||
<option name="COMMAND" value="bash" />
|
||||
<option name="PARAMETERS" value="-c "source .venv/bin/activate && scons -u -j$(nproc)" " />
|
||||
<option name="WORKING_DIRECTORY" value="$ProjectFileDir$" />
|
||||
</exec>
|
||||
</tool>
|
||||
</toolSet>
|
||||
@@ -1,4 +1,4 @@
|
||||
[lfs]
|
||||
url = https://gitlab.com/sunnypilot/public/sunnypilot-new-lfs.git/info/lfs
|
||||
pushurl = ssh://git@gitlab.com/sunnypilot/public/sunnypilot-new-lfs.git
|
||||
url = https://gitlab.com/commaai/openpilot-lfs.git/info/lfs
|
||||
pushurl = ssh://git@gitlab.com/commaai/openpilot-lfs.git
|
||||
locksverify = false
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
[lfs]
|
||||
url = https://gitlab.com/commaai/openpilot-lfs.git/info/lfs
|
||||
pushurl = ssh://git@gitlab.com/commaai/openpilot-lfs.git
|
||||
locksverify = false
|
||||
@@ -1,10 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Build Debug" type="CLionExternalRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" WORKING_DIR="file://$ProjectFileDir$/selfdrive/ui" PASS_PARENT_ENVS_2="true" PROJECT_NAME="sunnypilot" TARGET_NAME="uv Scons Build Debug" CONFIG_NAME="uv Scons Build Debug" RUN_PATH="ui">
|
||||
<envs>
|
||||
<env name="QT_DBL_CLICK_DIST" value="150" />
|
||||
</envs>
|
||||
<method v="2">
|
||||
<option name="CLION.EXTERNAL.BUILD" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,10 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Build Release" type="CLionExternalRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" WORKING_DIR="file://$ProjectFileDir$/selfdrive/ui" PASS_PARENT_ENVS_2="true" PROJECT_NAME="sunnypilot" TARGET_NAME="uv Scons Build Release" CONFIG_NAME="uv Scons Build Release" RUN_PATH="ui">
|
||||
<envs>
|
||||
<env name="QT_DBL_CLICK_DIST" value="150" />
|
||||
</envs>
|
||||
<method v="2">
|
||||
<option name="CLION.EXTERNAL.BUILD" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,26 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Build_BIG_UI" type="PythonConfigurationType" factoryName="Python">
|
||||
<module name="sunnypilot" />
|
||||
<option name="ENV_FILES" value="" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="PARENT_ENVS" value="true" />
|
||||
<envs>
|
||||
<env name="BIG" value="1" />
|
||||
</envs>
|
||||
<option name="SDK_HOME" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="$ProjectFileDir$/" />
|
||||
<option name="IS_MODULE_SDK" value="true" />
|
||||
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||
<option name="SCRIPT_NAME" value="$ProjectFileDir$/selfdrive/ui/ui.py" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="SHOW_COMMAND_LINE" value="false" />
|
||||
<option name="EMULATE_TERMINAL" value="false" />
|
||||
<option name="MODULE_MODE" value="false" />
|
||||
<option name="REDIRECT_INPUT" value="false" />
|
||||
<option name="INPUT_FILE" value="" />
|
||||
<method v="2">
|
||||
<option name="ToolBeforeRunTask" enabled="true" actionId="Tool_External Tools_uv Scons Build Debug" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,23 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Build_SMALL_UI" type="PythonConfigurationType" factoryName="Python">
|
||||
<module name="sunnypilot" />
|
||||
<option name="ENV_FILES" value="" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="PARENT_ENVS" value="true" />
|
||||
<option name="SDK_HOME" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="$ProjectFileDir$/" />
|
||||
<option name="IS_MODULE_SDK" value="true" />
|
||||
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||
<option name="SCRIPT_NAME" value="$ProjectFileDir$/selfdrive/ui/ui.py" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="SHOW_COMMAND_LINE" value="false" />
|
||||
<option name="EMULATE_TERMINAL" value="false" />
|
||||
<option name="MODULE_MODE" value="false" />
|
||||
<option name="REDIRECT_INPUT" value="false" />
|
||||
<option name="INPUT_FILE" value="" />
|
||||
<method v="2">
|
||||
<option name="ToolBeforeRunTask" enabled="true" actionId="Tool_External Tools_uv Scons Build Debug" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
41
.vscode/launch.json
vendored
41
.vscode/launch.json
vendored
@@ -23,11 +23,6 @@
|
||||
"id": "args",
|
||||
"description": "Arguments to pass to the process",
|
||||
"type": "promptString"
|
||||
},
|
||||
{
|
||||
"id": "replayArg",
|
||||
"type": "promptString",
|
||||
"description": "Enter route or segment to replay."
|
||||
}
|
||||
],
|
||||
"configurations": [
|
||||
@@ -45,41 +40,7 @@
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/${input:cpp_process}",
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"name": "Attach LLDB to Replay drive",
|
||||
"type": "lldb",
|
||||
"request": "attach",
|
||||
"pid": "${command:pickMyProcess}",
|
||||
"initCommands": [
|
||||
"script import time; time.sleep(3)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Replay drive",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/opendbc/safety/tests/safety_replay/replay_drive.py",
|
||||
"args": [
|
||||
"${input:replayArg}"
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": false,
|
||||
"env": {
|
||||
"PYTHONPATH": "${workspaceFolder}"
|
||||
},
|
||||
"subProcess": true,
|
||||
"stopOnEntry": false
|
||||
}
|
||||
],
|
||||
"compounds": [
|
||||
{
|
||||
"name": "Replay drive + Safety LLDB",
|
||||
"configurations": [
|
||||
"Replay drive",
|
||||
"Attach LLDB to Replay drive"
|
||||
]
|
||||
"cwd": "${workspaceFolder}",
|
||||
}
|
||||
]
|
||||
}
|
||||
1104
CHANGELOG.md
1104
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -3,12 +3,11 @@ FROM ghcr.io/commaai/openpilot-base:latest
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
||||
ENV OPENPILOT_PATH=/home/batman/openpilot
|
||||
ENV PYTHONPATH=${OPENPILOT_PATH}:${PYTHONPATH}
|
||||
|
||||
RUN mkdir -p ${OPENPILOT_PATH}
|
||||
WORKDIR ${OPENPILOT_PATH}
|
||||
|
||||
COPY . ${OPENPILOT_PATH}/
|
||||
|
||||
ENV UV_BIN="/home/batman/.local/bin/"
|
||||
ENV PATH="$UV_BIN:$PATH"
|
||||
RUN UV_PROJECT_ENVIRONMENT=$VIRTUAL_ENV uv run scons --cache-readonly -j$(nproc)
|
||||
RUN scons --cache-readonly -j$(nproc)
|
||||
|
||||
@@ -60,7 +60,6 @@ ENV NVIDIA_DRIVER_CAPABILITIES=graphics,utility,compute
|
||||
ENV QTWEBENGINE_DISABLE_SANDBOX=1
|
||||
|
||||
RUN dbus-uuidgen > /etc/machine-id
|
||||
RUN apt-get update && apt-get install -y fonts-noto-cjk fonts-noto-color-emoji
|
||||
|
||||
ARG USER=batman
|
||||
ARG USER_UID=1001
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
FROM ghcr.io/sunnypilot/sunnypilot-base:latest
|
||||
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
||||
ENV OPENPILOT_PATH=/home/batman/openpilot
|
||||
|
||||
RUN mkdir -p ${OPENPILOT_PATH}
|
||||
WORKDIR ${OPENPILOT_PATH}
|
||||
|
||||
COPY . ${OPENPILOT_PATH}/
|
||||
|
||||
RUN scons --cache-readonly -j$(nproc)
|
||||
@@ -1,83 +0,0 @@
|
||||
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 ssh pulseaudio xvfb x11-xserver-utils gnome-screenshot python3-tk python3-dev && \
|
||||
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
|
||||
|
||||
COPY tools/install_ubuntu_dependencies.sh /tmp/tools/
|
||||
RUN /tmp/tools/install_ubuntu_dependencies.sh && \
|
||||
rm -rf /var/lib/apt/lists/* /tmp/* && \
|
||||
cd /usr/lib/gcc/arm-none-eabi/* && \
|
||||
rm -rf arm/ thumb/nofp thumb/v6* thumb/v8* thumb/v7+fp thumb/v7-r+fp.sp
|
||||
|
||||
# Add OpenCL
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
apt-utils \
|
||||
alien \
|
||||
unzip \
|
||||
tar \
|
||||
curl \
|
||||
xz-utils \
|
||||
dbus \
|
||||
gcc-arm-none-eabi \
|
||||
tmux \
|
||||
vim \
|
||||
libx11-6 \
|
||||
wget \
|
||||
rsync \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN mkdir -p /tmp/opencl-driver-intel && \
|
||||
cd /tmp/opencl-driver-intel && \
|
||||
wget https://github.com/intel/llvm/releases/download/2024-WW14/oclcpuexp-2024.17.3.0.09_rel.tar.gz && \
|
||||
wget https://github.com/oneapi-src/oneTBB/releases/download/v2021.12.0/oneapi-tbb-2021.12.0-lin.tgz && \
|
||||
mkdir -p /opt/intel/oclcpuexp_2024.17.3.0.09_rel && \
|
||||
cd /opt/intel/oclcpuexp_2024.17.3.0.09_rel && \
|
||||
tar -zxvf /tmp/opencl-driver-intel/oclcpuexp-2024.17.3.0.09_rel.tar.gz && \
|
||||
mkdir -p /etc/OpenCL/vendors && \
|
||||
echo /opt/intel/oclcpuexp_2024.17.3.0.09_rel/x64/libintelocl.so > /etc/OpenCL/vendors/intel_expcpu.icd && \
|
||||
cd /opt/intel && \
|
||||
tar -zxvf /tmp/opencl-driver-intel/oneapi-tbb-2021.12.0-lin.tgz && \
|
||||
ln -s /opt/intel/oneapi-tbb-2021.12.0/lib/intel64/gcc4.8/libtbb.so /opt/intel/oclcpuexp_2024.17.3.0.09_rel/x64 && \
|
||||
ln -s /opt/intel/oneapi-tbb-2021.12.0/lib/intel64/gcc4.8/libtbbmalloc.so /opt/intel/oclcpuexp_2024.17.3.0.09_rel/x64 && \
|
||||
ln -s /opt/intel/oneapi-tbb-2021.12.0/lib/intel64/gcc4.8/libtbb.so.12 /opt/intel/oclcpuexp_2024.17.3.0.09_rel/x64 && \
|
||||
ln -s /opt/intel/oneapi-tbb-2021.12.0/lib/intel64/gcc4.8/libtbbmalloc.so.2 /opt/intel/oclcpuexp_2024.17.3.0.09_rel/x64 && \
|
||||
mkdir -p /etc/ld.so.conf.d && \
|
||||
echo /opt/intel/oclcpuexp_2024.17.3.0.09_rel/x64 > /etc/ld.so.conf.d/libintelopenclexp.conf && \
|
||||
ldconfig -f /etc/ld.so.conf.d/libintelopenclexp.conf && \
|
||||
cd / && \
|
||||
rm -rf /tmp/opencl-driver-intel
|
||||
|
||||
ENV NVIDIA_VISIBLE_DEVICES=all
|
||||
ENV NVIDIA_DRIVER_CAPABILITIES=graphics,utility,compute
|
||||
ENV QTWEBENGINE_DISABLE_SANDBOX=1
|
||||
|
||||
RUN dbus-uuidgen > /etc/machine-id
|
||||
RUN apt-get update && apt-get install -y fonts-noto-cjk fonts-noto-color-emoji
|
||||
|
||||
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
|
||||
|
||||
COPY --chown=$USER pyproject.toml uv.lock /home/$USER
|
||||
COPY --chown=$USER tools/install_python_dependencies.sh /home/$USER/tools/
|
||||
|
||||
ENV VIRTUAL_ENV=/home/$USER/.venv
|
||||
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
|
||||
RUN cd /home/$USER && \
|
||||
tools/install_python_dependencies.sh && \
|
||||
rm -rf tools/ pyproject.toml uv.lock .cache
|
||||
|
||||
USER root
|
||||
RUN sudo git config --global --add safe.directory /tmp/openpilot
|
||||
38
Jenkinsfile
vendored
38
Jenkinsfile
vendored
@@ -167,7 +167,7 @@ node {
|
||||
env.GIT_COMMIT = checkout(scm).GIT_COMMIT
|
||||
|
||||
def excludeBranches = ['__nightly', 'devel', 'devel-staging', 'release3', 'release3-staging',
|
||||
'release-tici', 'release-tizi', 'release-tizi-staging', 'testing-closet*', 'hotfix-*']
|
||||
'testing-closet*', 'hotfix-*']
|
||||
def excludeRegex = excludeBranches.join('|').replaceAll('\\*', '.*')
|
||||
|
||||
if (env.BRANCH_NAME != 'master' && !env.BRANCH_NAME.contains('__jenkins_loop_')) {
|
||||
@@ -178,20 +178,20 @@ node {
|
||||
|
||||
try {
|
||||
if (env.BRANCH_NAME == 'devel-staging') {
|
||||
deviceStage("build release-tizi-staging", "tizi-needs-can", [], [
|
||||
step("build release-tizi-staging", "RELEASE_BRANCH=release-tizi-staging $SOURCE_DIR/release/build_release.sh"),
|
||||
deviceStage("build release3-staging", "tici-needs-can", [], [
|
||||
step("build release3-staging", "RELEASE_BRANCH=release3-staging $SOURCE_DIR/release/build_release.sh"),
|
||||
])
|
||||
}
|
||||
|
||||
if (env.BRANCH_NAME == '__nightly') {
|
||||
parallel (
|
||||
'nightly': {
|
||||
deviceStage("build nightly", "tizi-needs-can", [], [
|
||||
deviceStage("build nightly", "tici-needs-can", [], [
|
||||
step("build nightly", "RELEASE_BRANCH=nightly $SOURCE_DIR/release/build_release.sh"),
|
||||
])
|
||||
},
|
||||
'nightly-dev': {
|
||||
deviceStage("build nightly-dev", "tizi-needs-can", [], [
|
||||
deviceStage("build nightly-dev", "tici-needs-can", [], [
|
||||
step("build nightly-dev", "PANDA_DEBUG_BUILD=1 RELEASE_BRANCH=nightly-dev $SOURCE_DIR/release/build_release.sh"),
|
||||
])
|
||||
},
|
||||
@@ -200,30 +200,39 @@ node {
|
||||
|
||||
if (!env.BRANCH_NAME.matches(excludeRegex)) {
|
||||
parallel (
|
||||
// tici tests
|
||||
'onroad tests': {
|
||||
deviceStage("onroad", "tizi-needs-can", ["UNSAFE=1"], [
|
||||
deviceStage("onroad", "tici-needs-can", ["UNSAFE=1"], [
|
||||
step("build openpilot", "cd system/manager && ./build.py"),
|
||||
step("check dirty", "release/check-dirty.sh"),
|
||||
step("onroad tests", "pytest selfdrive/test/test_onroad.py -s", [timeout: 60]),
|
||||
])
|
||||
},
|
||||
'HW + Unit Tests': {
|
||||
deviceStage("tizi-hardware", "tizi-common", ["UNSAFE=1"], [
|
||||
deviceStage("tici-hardware", "tici-common", ["UNSAFE=1"], [
|
||||
step("build", "cd system/manager && ./build.py"),
|
||||
step("test pandad", "pytest selfdrive/pandad/tests/test_pandad.py", [diffPaths: ["panda", "selfdrive/pandad/"]]),
|
||||
step("test power draw", "pytest -s system/hardware/tici/tests/test_power_draw.py"),
|
||||
step("test encoder", "LD_LIBRARY_PATH=/usr/local/lib pytest system/loggerd/tests/test_encoder.py", [diffPaths: ["system/loggerd/"]]),
|
||||
step("test pigeond", "pytest system/ubloxd/tests/test_pigeond.py", [diffPaths: ["system/ubloxd/"]]),
|
||||
step("test manager", "pytest system/manager/test/test_manager.py"),
|
||||
])
|
||||
},
|
||||
'loopback': {
|
||||
deviceStage("loopback", "tizi-loopback", ["UNSAFE=1"], [
|
||||
deviceStage("loopback", "tici-loopback", ["UNSAFE=1"], [
|
||||
step("build openpilot", "cd system/manager && ./build.py"),
|
||||
step("test pandad loopback", "pytest selfdrive/pandad/tests/test_pandad_loopback.py"),
|
||||
])
|
||||
},
|
||||
'camerad AR0231': {
|
||||
deviceStage("AR0231", "tici-ar0231", ["UNSAFE=1"], [
|
||||
step("build", "cd system/manager && ./build.py"),
|
||||
step("test camerad", "pytest system/camerad/test/test_camerad.py", [timeout: 60]),
|
||||
step("test exposure", "pytest system/camerad/test/test_exposure.py"),
|
||||
])
|
||||
},
|
||||
'camerad OX03C10': {
|
||||
deviceStage("OX03C10", "tizi-ox03c10", ["UNSAFE=1"], [
|
||||
deviceStage("OX03C10", "tici-ox03c10", ["UNSAFE=1"], [
|
||||
step("build", "cd system/manager && ./build.py"),
|
||||
step("test camerad", "pytest system/camerad/test/test_camerad.py", [timeout: 60]),
|
||||
step("test exposure", "pytest system/camerad/test/test_exposure.py"),
|
||||
@@ -237,13 +246,17 @@ node {
|
||||
])
|
||||
},
|
||||
'sensord': {
|
||||
deviceStage("LSM + MMC", "tizi-lsmc", ["UNSAFE=1"], [
|
||||
deviceStage("LSM + MMC", "tici-lsmc", ["UNSAFE=1"], [
|
||||
step("build", "cd system/manager && ./build.py"),
|
||||
step("test sensord", "pytest system/sensord/tests/test_sensord.py"),
|
||||
])
|
||||
deviceStage("BMX + LSM", "tici-bmx-lsm", ["UNSAFE=1"], [
|
||||
step("build", "cd system/manager && ./build.py"),
|
||||
step("test sensord", "pytest system/sensord/tests/test_sensord.py"),
|
||||
])
|
||||
},
|
||||
'replay': {
|
||||
deviceStage("model-replay", "tizi-replay", ["UNSAFE=1"], [
|
||||
deviceStage("model-replay", "tici-replay", ["UNSAFE=1"], [
|
||||
step("build", "cd system/manager && ./build.py", [diffPaths: ["selfdrive/modeld/", "tinygrad_repo", "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"]]),
|
||||
])
|
||||
@@ -253,9 +266,8 @@ node {
|
||||
step("build openpilot", "cd system/manager && ./build.py"),
|
||||
step("test pandad loopback", "SINGLE_PANDA=1 pytest selfdrive/pandad/tests/test_pandad_loopback.py"),
|
||||
step("test pandad spi", "pytest selfdrive/pandad/tests/test_pandad_spi.py"),
|
||||
step("test pandad", "pytest selfdrive/pandad/tests/test_pandad.py", [diffPaths: ["panda", "selfdrive/pandad/"]]),
|
||||
step("test amp", "pytest system/hardware/tici/tests/test_amplifier.py"),
|
||||
// TODO: enable once new AGNOS is available
|
||||
// step("test esim", "pytest system/hardware/tici/tests/test_esim.py"),
|
||||
step("test qcomgpsd", "pytest system/qcomgpsd/tests/test_qcomgpsd.py", [diffPaths: ["system/qcomgpsd/"]]),
|
||||
])
|
||||
},
|
||||
|
||||
21
LICENSE.md
21
LICENSE.md
@@ -1,21 +0,0 @@
|
||||
# Custom MIT License
|
||||
|
||||
Copyright (c) 2024, Haibin Wen, SUNNYPILOT LLC
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to view and modify the Software, subject to the following conditions:
|
||||
|
||||
1. **Permission Required**: Permission Required for Commercial, For-Profit, or Closed Source Use: Use of the Software, in whole or in part, for any commercial purposes, for-profit projects, or in closed source projects requires explicit written permission from the original author(s).
|
||||
|
||||
2. **Redistribution**: Any redistribution of the Software, modified or unmodified, must retain this license notice and the following acknowledgment:
|
||||
"This software is licensed under a custom license requiring permission for use."
|
||||
|
||||
3. **Visibility**: Any project that uses the Software must visibly mention the following acknowledgment:
|
||||
"This project uses software from Haibin Wen and SUNNYPILOT LLC and is licensed under a custom license requiring permission for use."
|
||||
|
||||
4. **No Warranty**: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
Contact sunnypilot Support <support@sunnypilot.ai> for permission requests.
|
||||
|
||||
---
|
||||
|
||||
Haibin Wen, SUNNYPILOT LLC
|
||||
149
README.md
149
README.md
@@ -1,74 +1,107 @@
|
||||

|
||||
<div align="center" style="text-align: center;">
|
||||
|
||||
## 🌞 What is sunnypilot?
|
||||
[sunnypilot](https://github.com/sunnyhaibin/sunnypilot) is a fork of comma.ai's openpilot, an open source driver assistance system. sunnypilot offers the user a unique driving experience for over 300+ supported car makes and models with modified behaviors of driving assist engagements. sunnypilot complies with comma.ai's safety rules as accurately as possible.
|
||||
<h1>openpilot</h1>
|
||||
|
||||
## 💭 Join our Community Forum
|
||||
Join the official sunnypilot community forum to stay up to date with all the latest features and be a part of shaping the future of sunnypilot!
|
||||
* https://community.sunnypilot.ai/
|
||||
<p>
|
||||
<b>openpilot is an operating system for robotics.</b>
|
||||
<br>
|
||||
Currently, it upgrades the driver assistance system in 275+ supported cars.
|
||||
</p>
|
||||
|
||||
## Documentation
|
||||
https://docs.sunnypilot.ai/ is your one stop shop for everything from features to installation to FAQ about the sunnypilot
|
||||
<h3>
|
||||
<a href="https://docs.comma.ai">Docs</a>
|
||||
<span> · </span>
|
||||
<a href="https://docs.comma.ai/contributing/roadmap/">Roadmap</a>
|
||||
<span> · </span>
|
||||
<a href="https://github.com/commaai/openpilot/blob/master/docs/CONTRIBUTING.md">Contribute</a>
|
||||
<span> · </span>
|
||||
<a href="https://discord.comma.ai">Community</a>
|
||||
<span> · </span>
|
||||
<a href="https://comma.ai/shop">Try it on a comma 3X</a>
|
||||
</h3>
|
||||
|
||||
## 🚘 Running on a dedicated device in a car
|
||||
First, check out this list of items you'll need to [get started](https://community.sunnypilot.ai/t/getting-started-using-sunnypilot-in-your-supported-car/251).
|
||||
Quick start: `bash <(curl -fsSL openpilot.comma.ai)`
|
||||
|
||||
## Installation
|
||||
Next, refer to the sunnypilot community forum for [installation instructions](https://community.sunnypilot.ai/t/read-before-installing-sunnypilot/254), as well as a complete list of [Recommended Branch Installations](https://community.sunnypilot.ai/t/recommended-branch-installations/235).
|
||||

|
||||
[](https://codecov.io/gh/commaai/openpilot)
|
||||
[](LICENSE)
|
||||
[](https://x.com/comma_ai)
|
||||
[](https://discord.comma.ai)
|
||||
|
||||
## 🎆 Pull Requests
|
||||
We welcome both pull requests and issues on GitHub. Bug fixes are encouraged.
|
||||
</div>
|
||||
|
||||
Pull requests should be against the most current `master` branch.
|
||||
|
||||
## 📊 User Data
|
||||
|
||||
By default, sunnypilot uploads the driving data to comma servers. You can also access your data through [comma connect](https://connect.comma.ai/).
|
||||
|
||||
sunnypilot is open source software. The user is free to disable data collection if they wish to do so.
|
||||
|
||||
sunnypilot logs the road-facing camera, CAN, GPS, IMU, magnetometer, thermal sensors, crashes, and operating system logs.
|
||||
The driver-facing camera and microphone are only logged if you explicitly opt-in in settings.
|
||||
|
||||
By using this software, you understand that use of this software or its related services will generate certain types of user data, which may be logged and stored at the sole discretion of comma. By accepting this agreement, you grant an irrevocable, perpetual, worldwide right to comma for the use of this data.
|
||||
|
||||
## Licensing
|
||||
|
||||
sunnypilot is released under the [MIT License](LICENSE). This repository includes original work as well as significant portions of code derived from [openpilot by comma.ai](https://github.com/commaai/openpilot), which is also released under the MIT license with additional disclaimers.
|
||||
|
||||
The original openpilot license notice, including comma.ai’s indemnification and alpha software disclaimer, is reproduced below as required:
|
||||
|
||||
> openpilot is released under the MIT license. Some parts of the software are released under other licenses as specified.
|
||||
>
|
||||
> Any user of this software shall indemnify and hold harmless Comma.ai, Inc. and its directors, officers, employees, agents, stockholders, affiliates, subcontractors and customers from and against all allegations, claims, actions, suits, demands, damages, liabilities, obligations, losses, settlements, judgments, costs and expenses (including without limitation attorneys’ fees and costs) which arise out of, relate to or result from any use of this software by user.
|
||||
>
|
||||
> **THIS IS ALPHA QUALITY SOFTWARE FOR RESEARCH PURPOSES ONLY. THIS IS NOT A PRODUCT.
|
||||
> YOU ARE RESPONSIBLE FOR COMPLYING WITH LOCAL LAWS AND REGULATIONS.
|
||||
> NO WARRANTY EXPRESSED OR IMPLIED.**
|
||||
|
||||
For full license terms, please see the [`LICENSE`](LICENSE) file.
|
||||
|
||||
## 💰 Support sunnypilot
|
||||
If you find any of the features useful, consider becoming a [sponsor on GitHub](https://github.com/sponsors/sunnyhaibin) to support future feature development and improvements.
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="https://youtu.be/NmBfgOanCyk" title="Video By Greer Viau"><img src="https://github.com/commaai/openpilot/assets/8762862/2f7112ae-f748-4f39-b617-fabd689c3772"></a></td>
|
||||
<td><a href="https://youtu.be/VHKyqZ7t8Gw" title="Video By Logan LeGrand"><img src="https://github.com/commaai/openpilot/assets/8762862/92351544-2833-40d7-9e0b-7ef7ae37ec4c"></a></td>
|
||||
<td><a href="https://youtu.be/SUIZYzxtMQs" title="A drive to Taco Bell"><img src="https://github.com/commaai/openpilot/assets/8762862/05ceefc5-2628-439c-a9b2-89ce77dc6f63"></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
By becoming a sponsor, you will gain access to exclusive content, early access to new features, and the opportunity to directly influence the project's development.
|
||||
Using openpilot in a car
|
||||
------
|
||||
|
||||
To use openpilot in a car, you need four things:
|
||||
1. **Supported Device:** a comma 3/3X, available at [comma.ai/shop](https://comma.ai/shop/comma-3x).
|
||||
2. **Software:** The setup procedure for the comma 3/3X allows users to enter a URL for custom software. Use the URL `openpilot.comma.ai` to install the release version.
|
||||
3. **Supported Car:** Ensure that you have one of [the 275+ supported cars](docs/CARS.md).
|
||||
4. **Car Harness:** You will also need a [car harness](https://comma.ai/shop/car-harness) to connect your comma 3/3X to your car.
|
||||
|
||||
<h3>GitHub Sponsor</h3>
|
||||
We have detailed instructions for [how to install the harness and device in a car](https://comma.ai/setup). Note that it's possible to run openpilot on [other hardware](https://blog.comma.ai/self-driving-car-for-free/), although it's not plug-and-play.
|
||||
|
||||
<a href="https://github.com/sponsors/sunnyhaibin">
|
||||
<img src="https://user-images.githubusercontent.com/47793918/244135584-9800acbd-69fd-4b2b-bec9-e5fa2d85c817.png" alt="Become a Sponsor" width="300" style="max-width: 100%; height: auto;">
|
||||
</a>
|
||||
<br>
|
||||
### Branches
|
||||
| branch | URL | description |
|
||||
|------------------|----------------------------------------|-------------------------------------------------------------------------------------|
|
||||
| `release3` | openpilot.comma.ai | This is openpilot's release branch. |
|
||||
| `release3-staging` | openpilot-test.comma.ai | This is the staging branch for releases. Use it to get new releases slightly early. |
|
||||
| `nightly` | openpilot-nightly.comma.ai | This is the bleeding edge development branch. Do not expect this to be stable. |
|
||||
| `nightly-dev` | installer.comma.ai/commaai/nightly-dev | Same as nightly, but includes experimental development features for some cars. |
|
||||
|
||||
<h3>PayPal</h3>
|
||||
To start developing openpilot
|
||||
------
|
||||
|
||||
<a href="https://paypal.me/sunnyhaibin0850" target="_blank">
|
||||
<img src="https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif" alt="PayPal this" title="PayPal - The safer, easier way to pay online!" border="0" />
|
||||
</a>
|
||||
<br></br>
|
||||
openpilot is developed by [comma](https://comma.ai/) and by users like you. We welcome both pull requests and issues on [GitHub](http://github.com/commaai/openpilot).
|
||||
|
||||
Your continuous love and support are greatly appreciated! Enjoy 🥰
|
||||
* Join the [community Discord](https://discord.comma.ai)
|
||||
* Check out [the contributing docs](docs/CONTRIBUTING.md)
|
||||
* Check out the [openpilot tools](tools/)
|
||||
* Read about the [development workflow](docs/WORKFLOW.md)
|
||||
* Code documentation lives at https://docs.comma.ai
|
||||
* Information about running openpilot lives on the [community wiki](https://github.com/commaai/openpilot/wiki)
|
||||
|
||||
<span>-</span> Jason, Founder of sunnypilot
|
||||
Want to get paid to work on openpilot? [comma is hiring](https://comma.ai/jobs#open-positions) and offers lots of [bounties](https://comma.ai/bounties) for external contributors.
|
||||
|
||||
Safety and Testing
|
||||
----
|
||||
|
||||
* openpilot observes [ISO26262](https://en.wikipedia.org/wiki/ISO_26262) guidelines, see [SAFETY.md](docs/SAFETY.md) for more details.
|
||||
* openpilot has software-in-the-loop [tests](.github/workflows/selfdrive_tests.yaml) that run on every commit.
|
||||
* The code enforcing the safety model lives in panda and is written in C, see [code rigor](https://github.com/commaai/panda#code-rigor) for more details.
|
||||
* panda has software-in-the-loop [safety tests](https://github.com/commaai/panda/tree/master/tests/safety).
|
||||
* Internally, we have a hardware-in-the-loop Jenkins test suite that builds and unit tests the various processes.
|
||||
* panda has additional hardware-in-the-loop [tests](https://github.com/commaai/panda/blob/master/Jenkinsfile).
|
||||
* We run the latest openpilot in a testing closet containing 10 comma devices continuously replaying routes.
|
||||
|
||||
Licensing
|
||||
------
|
||||
|
||||
openpilot is released under the MIT license. Some parts of the software are released under other licenses as specified.
|
||||
|
||||
Any user of this software shall indemnify and hold harmless Comma.ai, Inc. and its directors, officers, employees, agents, stockholders, affiliates, subcontractors and customers from and against all allegations, claims, actions, suits, demands, damages, liabilities, obligations, losses, settlements, judgments, costs and expenses (including without limitation attorneys’ fees and costs) which arise out of, relate to or result from any use of this software by user.
|
||||
|
||||
**THIS IS ALPHA QUALITY SOFTWARE FOR RESEARCH PURPOSES ONLY. THIS IS NOT A PRODUCT.
|
||||
YOU ARE RESPONSIBLE FOR COMPLYING WITH LOCAL LAWS AND REGULATIONS.
|
||||
NO WARRANTY EXPRESSED OR IMPLIED.**
|
||||
|
||||
User Data and comma Account
|
||||
------
|
||||
|
||||
By default, openpilot uploads the driving data to our servers. You can also access your data through [comma connect](https://connect.comma.ai/). We use your data to train better models and improve openpilot for everyone.
|
||||
|
||||
openpilot is open source software: the user is free to disable data collection if they wish to do so.
|
||||
|
||||
openpilot logs the road-facing cameras, CAN, GPS, IMU, magnetometer, thermal sensors, crashes, and operating system logs.
|
||||
The driver-facing camera is only logged if you explicitly opt-in in settings. The microphone is not recorded.
|
||||
|
||||
By using openpilot, you agree to [our Privacy Policy](https://comma.ai/privacy). You understand that use of this software or its related services will generate certain types of user data, which may be logged and stored at the sole discretion of comma. By accepting this agreement, you grant an irrevocable, perpetual, worldwide right to comma for the use of this data.
|
||||
|
||||
54
RELEASES.md
54
RELEASES.md
@@ -1,56 +1,10 @@
|
||||
Version 0.10.3 (2025-12-17)
|
||||
========================
|
||||
* New driving model #36249
|
||||
* New temporal policy architecture
|
||||
* New on-policy training physics noise model
|
||||
* New driver monitoring model #36409
|
||||
* Trained on a new dataset, including comma four data
|
||||
* Improved inter-process communication memory efficiency
|
||||
|
||||
Version 0.10.2 (2025-11-19)
|
||||
========================
|
||||
* comma four support
|
||||
|
||||
Version 0.10.1 (2025-09-08)
|
||||
========================
|
||||
* New driving model #36276
|
||||
* World Model: removed global localization inputs
|
||||
* World Model: 2x the number of parameters
|
||||
* World Model: trained on 4x the number of segments
|
||||
* VAE Compression Model: new architecture and training objective
|
||||
* Driving Vision Model: trained on 4x the number of segments
|
||||
* New Driver Monitoring model #36198
|
||||
* Acura TLX 2021 support thanks to MVL!
|
||||
* Honda City 2023 support thanks to vanillagorillaa and drFritz!
|
||||
* Honda N-Box 2018 support thanks to miettal!
|
||||
* Honda Odyssey 2021-25 support thanks to csouers and MVL!
|
||||
* Honda Passport 2026 support thanks to vanillagorillaa and MVL!
|
||||
|
||||
Version 0.10.0 (2025-08-05)
|
||||
Version 0.9.9 (2025-04-30)
|
||||
========================
|
||||
* New driving model
|
||||
* New training architecture
|
||||
* Described in our CVPR paper: "Learning to Drive from a World Model"
|
||||
* Longitudinal MPC replaced by E2E planning from World Model in Experimental Mode
|
||||
* Action from lateral MPC as training objective replaced by E2E planning from World Model
|
||||
* Low-speed lead car ground-truth fixes
|
||||
* Enable live-learned steering actuation delay
|
||||
* Opt-in audio recording for dashcam video
|
||||
* Acura MDX 2025 support thanks to vanillagorillaa and MVL!
|
||||
* Honda Accord 2023-25 support thanks to vanillagorillaa and MVL!
|
||||
* Honda CR-V 2023-25 support thanks to vanillagorillaa and MVL!
|
||||
* Honda Pilot 2023-25 support thanks to vanillagorillaa and MVL!
|
||||
|
||||
Version 0.9.9 (2025-05-23)
|
||||
========================
|
||||
* New driving model
|
||||
* New training architecture using parts from MLSIM
|
||||
* Steering actuation delay is now learned online
|
||||
* Ford Escape 2023-24 support thanks to incognitojam!
|
||||
* Ford Kuga 2024 support thanks to incognitojam!
|
||||
* Hyundai Nexo 2021 support thanks to sunnyhaibin!
|
||||
* Tesla Model 3 and Y support thanks to lukasloetkolben!
|
||||
* Lexus RC 2023 support thanks to nelsonjchen!
|
||||
* Coming soon
|
||||
* New driving model supervised by MLSIM
|
||||
* An online learner for steering actuator delay
|
||||
|
||||
Version 0.9.8 (2025-02-28)
|
||||
========================
|
||||
|
||||
350
SConstruct
350
SConstruct
@@ -3,52 +3,168 @@ import subprocess
|
||||
import sys
|
||||
import sysconfig
|
||||
import platform
|
||||
import shlex
|
||||
import numpy as np
|
||||
|
||||
import SCons.Errors
|
||||
|
||||
SCons.Warnings.warningAsException(True)
|
||||
|
||||
# pending upstream fix - https://github.com/SCons/scons/issues/4461
|
||||
#SetOption('warn', 'all')
|
||||
|
||||
TICI = os.path.isfile('/TICI')
|
||||
AGNOS = TICI
|
||||
|
||||
Decider('MD5-timestamp')
|
||||
|
||||
SetOption('num_jobs', max(1, int(os.cpu_count()/2)))
|
||||
SetOption('num_jobs', int(os.cpu_count()/2))
|
||||
|
||||
AddOption('--kaitai',
|
||||
action='store_true',
|
||||
help='Regenerate kaitai struct parsers')
|
||||
|
||||
AddOption('--asan',
|
||||
action='store_true',
|
||||
help='turn on ASAN')
|
||||
|
||||
AddOption('--ubsan',
|
||||
action='store_true',
|
||||
help='turn on UBSan')
|
||||
|
||||
AddOption('--coverage',
|
||||
action='store_true',
|
||||
help='build with test coverage options')
|
||||
|
||||
AddOption('--clazy',
|
||||
action='store_true',
|
||||
help='build with clazy')
|
||||
|
||||
AddOption('--compile_db',
|
||||
action='store_true',
|
||||
help='build clang compilation database')
|
||||
|
||||
AddOption('--ccflags',
|
||||
action='store',
|
||||
type='string',
|
||||
default='',
|
||||
help='pass arbitrary flags over the command line')
|
||||
|
||||
AddOption('--external-sconscript',
|
||||
action='store',
|
||||
metavar='FILE',
|
||||
dest='external_sconscript',
|
||||
help='add an external SConscript to the build')
|
||||
|
||||
AddOption('--pc-thneed',
|
||||
action='store_true',
|
||||
dest='pc_thneed',
|
||||
help='use thneed on pc')
|
||||
|
||||
AddOption('--mutation',
|
||||
action='store_true',
|
||||
help='generate mutation-ready code')
|
||||
|
||||
AddOption('--kaitai', action='store_true', help='Regenerate kaitai struct parsers')
|
||||
AddOption('--asan', action='store_true', help='turn on ASAN')
|
||||
AddOption('--ubsan', action='store_true', help='turn on UBSan')
|
||||
AddOption('--mutation', action='store_true', help='generate mutation-ready code')
|
||||
AddOption('--ccflags', action='store', type='string', default='', help='pass arbitrary flags over the command line')
|
||||
AddOption('--minimal',
|
||||
action='store_false',
|
||||
dest='extras',
|
||||
default=os.path.exists(File('#.gitattributes').abspath), # minimal by default on release branch (where there's no LFS)
|
||||
default=os.path.exists(File('#.lfsconfig').abspath), # minimal by default on release branch (where there's no LFS)
|
||||
help='the minimum build to run openpilot. no tests, tools, etc.')
|
||||
|
||||
# Detect platform
|
||||
arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip()
|
||||
## Architecture name breakdown (arch)
|
||||
## - larch64: linux tici aarch64
|
||||
## - aarch64: linux pc aarch64
|
||||
## - x86_64: linux pc x64
|
||||
## - Darwin: mac x64 or arm64
|
||||
real_arch = arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip()
|
||||
if platform.system() == "Darwin":
|
||||
arch = "Darwin"
|
||||
brew_prefix = subprocess.check_output(['brew', '--prefix'], encoding='utf8').strip()
|
||||
elif arch == "aarch64" and os.path.isfile('/TICI'):
|
||||
elif arch == "aarch64" and AGNOS:
|
||||
arch = "larch64"
|
||||
assert arch in [
|
||||
"larch64", # linux tici arm64
|
||||
"aarch64", # linux pc arm64
|
||||
"x86_64", # linux pc x64
|
||||
"Darwin", # macOS arm64 (x86 not supported)
|
||||
]
|
||||
assert arch in ["larch64", "aarch64", "x86_64", "Darwin"]
|
||||
|
||||
lenv = {
|
||||
"PATH": os.environ['PATH'],
|
||||
"LD_LIBRARY_PATH": [Dir(f"#third_party/acados/{arch}/lib").abspath],
|
||||
"PYTHONPATH": Dir("#").abspath + ':' + Dir(f"#third_party/acados").abspath,
|
||||
|
||||
"ACADOS_SOURCE_DIR": Dir("#third_party/acados").abspath,
|
||||
"ACADOS_PYTHON_INTERFACE_PATH": Dir("#third_party/acados/acados_template").abspath,
|
||||
"TERA_PATH": Dir("#").abspath + f"/third_party/acados/{arch}/t_renderer"
|
||||
}
|
||||
|
||||
rpath = lenv["LD_LIBRARY_PATH"].copy()
|
||||
|
||||
if arch == "larch64":
|
||||
cpppath = [
|
||||
"#third_party/opencl/include",
|
||||
]
|
||||
|
||||
libpath = [
|
||||
"/usr/local/lib",
|
||||
"/system/vendor/lib64",
|
||||
f"#third_party/acados/{arch}/lib",
|
||||
]
|
||||
|
||||
libpath += [
|
||||
"#third_party/libyuv/larch64/lib",
|
||||
"/usr/lib/aarch64-linux-gnu"
|
||||
]
|
||||
cflags = ["-DQCOM2", "-mcpu=cortex-a57"]
|
||||
cxxflags = ["-DQCOM2", "-mcpu=cortex-a57"]
|
||||
rpath += ["/usr/local/lib"]
|
||||
else:
|
||||
cflags = []
|
||||
cxxflags = []
|
||||
cpppath = []
|
||||
rpath += []
|
||||
|
||||
# MacOS
|
||||
if arch == "Darwin":
|
||||
libpath = [
|
||||
f"#third_party/libyuv/{arch}/lib",
|
||||
f"#third_party/acados/{arch}/lib",
|
||||
f"{brew_prefix}/lib",
|
||||
f"{brew_prefix}/opt/openssl@3.0/lib",
|
||||
"/System/Library/Frameworks/OpenGL.framework/Libraries",
|
||||
]
|
||||
|
||||
cflags += ["-DGL_SILENCE_DEPRECATION"]
|
||||
cxxflags += ["-DGL_SILENCE_DEPRECATION"]
|
||||
cpppath += [
|
||||
f"{brew_prefix}/include",
|
||||
f"{brew_prefix}/opt/openssl@3.0/include",
|
||||
]
|
||||
lenv["DYLD_LIBRARY_PATH"] = lenv["LD_LIBRARY_PATH"]
|
||||
# Linux
|
||||
else:
|
||||
libpath = [
|
||||
f"#third_party/acados/{arch}/lib",
|
||||
f"#third_party/libyuv/{arch}/lib",
|
||||
"/usr/lib",
|
||||
"/usr/local/lib",
|
||||
]
|
||||
|
||||
if GetOption('asan'):
|
||||
ccflags = ["-fsanitize=address", "-fno-omit-frame-pointer"]
|
||||
ldflags = ["-fsanitize=address"]
|
||||
elif GetOption('ubsan'):
|
||||
ccflags = ["-fsanitize=undefined"]
|
||||
ldflags = ["-fsanitize=undefined"]
|
||||
else:
|
||||
ccflags = []
|
||||
ldflags = []
|
||||
|
||||
# no --as-needed on mac linker
|
||||
if arch != "Darwin":
|
||||
ldflags += ["-Wl,--as-needed", "-Wl,--no-undefined"]
|
||||
|
||||
ccflags_option = GetOption('ccflags')
|
||||
if ccflags_option:
|
||||
ccflags += ccflags_option.split(' ')
|
||||
|
||||
env = Environment(
|
||||
ENV={
|
||||
"PATH": os.environ['PATH'],
|
||||
"PYTHONPATH": Dir("#").abspath + ':' + Dir(f"#third_party/acados").abspath,
|
||||
"ACADOS_SOURCE_DIR": Dir("#third_party/acados").abspath,
|
||||
"ACADOS_PYTHON_INTERFACE_PATH": Dir("#third_party/acados/acados_template").abspath,
|
||||
"TERA_PATH": Dir("#").abspath + f"/third_party/acados/{arch}/t_renderer"
|
||||
},
|
||||
CC='clang',
|
||||
CXX='clang++',
|
||||
ENV=lenv,
|
||||
CCFLAGS=[
|
||||
"-g",
|
||||
"-fPIC",
|
||||
@@ -61,31 +177,36 @@ env = Environment(
|
||||
"-Wno-c99-designator",
|
||||
"-Wno-reorder-init-list",
|
||||
"-Wno-vla-cxx-extension",
|
||||
],
|
||||
CFLAGS=["-std=gnu11"],
|
||||
CXXFLAGS=["-std=c++1z"],
|
||||
CPPPATH=[
|
||||
] + cflags + ccflags,
|
||||
|
||||
CPPPATH=cpppath + [
|
||||
"#",
|
||||
"#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",
|
||||
"#third_party/libyuv/include",
|
||||
"#third_party/json11",
|
||||
"#third_party/linux/include",
|
||||
"#third_party",
|
||||
"#msgq",
|
||||
],
|
||||
LIBPATH=[
|
||||
"#common",
|
||||
|
||||
CC='clang',
|
||||
CXX='clang++',
|
||||
LINKFLAGS=ldflags,
|
||||
|
||||
RPATH=rpath,
|
||||
|
||||
CFLAGS=["-std=gnu11"] + cflags,
|
||||
CXXFLAGS=["-std=c++1z"] + cxxflags,
|
||||
LIBPATH=libpath + [
|
||||
"#msgq_repo",
|
||||
"#third_party",
|
||||
"#selfdrive/pandad",
|
||||
"#common",
|
||||
"#rednose/helpers",
|
||||
f"#third_party/libyuv/{arch}/lib",
|
||||
f"#third_party/acados/{arch}/lib",
|
||||
],
|
||||
RPATH=[],
|
||||
CYTHONCFILESUFFIX=".cpp",
|
||||
COMPILATIONDB_USE_ABSPATH=True,
|
||||
REDNOSE_ROOT="#",
|
||||
@@ -93,63 +214,30 @@ env = Environment(
|
||||
toolpath=["#site_scons/site_tools", "#rednose_repo/site_scons/site_tools"],
|
||||
)
|
||||
|
||||
# Arch-specific flags and paths
|
||||
if arch == "larch64":
|
||||
env.Append(CPPPATH=["#third_party/opencl/include"])
|
||||
env.Append(LIBPATH=[
|
||||
"/usr/local/lib",
|
||||
"/system/vendor/lib64",
|
||||
"/usr/lib/aarch64-linux-gnu",
|
||||
])
|
||||
arch_flags = ["-D__TICI__", "-mcpu=cortex-a57", "-DQCOM2"]
|
||||
env.Append(CCFLAGS=arch_flags)
|
||||
env.Append(CXXFLAGS=arch_flags)
|
||||
elif arch == "Darwin":
|
||||
env.Append(LIBPATH=[
|
||||
f"{brew_prefix}/lib",
|
||||
f"{brew_prefix}/opt/openssl@3.0/lib",
|
||||
f"{brew_prefix}/opt/llvm/lib/c++",
|
||||
"/System/Library/Frameworks/OpenGL.framework/Libraries",
|
||||
])
|
||||
env.Append(CCFLAGS=["-DGL_SILENCE_DEPRECATION"])
|
||||
env.Append(CXXFLAGS=["-DGL_SILENCE_DEPRECATION"])
|
||||
env.Append(CPPPATH=[
|
||||
f"{brew_prefix}/include",
|
||||
f"{brew_prefix}/opt/openssl@3.0/include",
|
||||
])
|
||||
else:
|
||||
env.Append(LIBPATH=[
|
||||
"/usr/lib",
|
||||
"/usr/local/lib",
|
||||
])
|
||||
if arch == "Darwin":
|
||||
# RPATH is not supported on macOS, instead use the linker flags
|
||||
darwin_rpath_link_flags = [f"-Wl,-rpath,{path}" for path in env["RPATH"]]
|
||||
env["LINKFLAGS"] += darwin_rpath_link_flags
|
||||
|
||||
# Sanitizers and extra CCFLAGS from CLI
|
||||
if GetOption('asan'):
|
||||
env.Append(CCFLAGS=["-fsanitize=address", "-fno-omit-frame-pointer"])
|
||||
env.Append(LINKFLAGS=["-fsanitize=address"])
|
||||
elif GetOption('ubsan'):
|
||||
env.Append(CCFLAGS=["-fsanitize=undefined"])
|
||||
env.Append(LINKFLAGS=["-fsanitize=undefined"])
|
||||
if GetOption('compile_db'):
|
||||
env.CompilationDatabase('compile_commands.json')
|
||||
|
||||
_extra_cc = shlex.split(GetOption('ccflags') or '')
|
||||
if _extra_cc:
|
||||
env.Append(CCFLAGS=_extra_cc)
|
||||
# Setup cache dir
|
||||
cache_dir = '/data/scons_cache' if AGNOS else '/tmp/scons_cache'
|
||||
CacheDir(cache_dir)
|
||||
Clean(["."], cache_dir)
|
||||
|
||||
# no --as-needed on mac linker
|
||||
if arch != "Darwin":
|
||||
env.Append(LINKFLAGS=["-Wl,--as-needed", "-Wl,--no-undefined"])
|
||||
|
||||
# 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
|
||||
py_include = sysconfig.get_paths()['include']
|
||||
envCython = env.Clone()
|
||||
envCython["CPPPATH"] += [py_include, np.get_include()]
|
||||
@@ -158,28 +246,89 @@ envCython["CCFLAGS"].remove("-Werror")
|
||||
|
||||
envCython["LIBS"] = []
|
||||
if arch == "Darwin":
|
||||
envCython["LINKFLAGS"] = env["LINKFLAGS"] + ["-bundle", "-undefined", "dynamic_lookup"]
|
||||
envCython["LINKFLAGS"] = ["-bundle", "-undefined", "dynamic_lookup"] + darwin_rpath_link_flags
|
||||
else:
|
||||
envCython["LINKFLAGS"] = ["-pthread", "-shared"]
|
||||
|
||||
np_version = SCons.Script.Value(np.__version__)
|
||||
Export('envCython', 'np_version')
|
||||
|
||||
Export('env', 'arch')
|
||||
# Qt build environment
|
||||
qt_env = env.Clone()
|
||||
qt_modules = ["Widgets", "Gui", "Core", "Network", "Concurrent", "DBus", "Xml"]
|
||||
|
||||
# Setup cache dir
|
||||
default_cache_dir = '/data/scons_cache' if arch == "larch64" else '/tmp/scons_cache'
|
||||
cache_dir = ARGUMENTS.get('cache_dir', default_cache_dir)
|
||||
CacheDir(cache_dir)
|
||||
Clean(["."], cache_dir)
|
||||
qt_libs = []
|
||||
if arch == "Darwin":
|
||||
qt_env['QTDIR'] = f"{brew_prefix}/opt/qt@5"
|
||||
qt_dirs = [
|
||||
os.path.join(qt_env['QTDIR'], "include"),
|
||||
]
|
||||
qt_dirs += [f"{qt_env['QTDIR']}/include/Qt{m}" for m in qt_modules]
|
||||
qt_env["LINKFLAGS"] += ["-F" + os.path.join(qt_env['QTDIR'], "lib")]
|
||||
qt_env["FRAMEWORKS"] += [f"Qt{m}" for m in qt_modules] + ["OpenGL"]
|
||||
qt_env.AppendENVPath('PATH', os.path.join(qt_env['QTDIR'], "bin"))
|
||||
else:
|
||||
qt_install_prefix = subprocess.check_output(['qmake', '-query', 'QT_INSTALL_PREFIX'], encoding='utf8').strip()
|
||||
qt_install_headers = subprocess.check_output(['qmake', '-query', 'QT_INSTALL_HEADERS'], encoding='utf8').strip()
|
||||
|
||||
# ********** start building stuff **********
|
||||
qt_env['QTDIR'] = qt_install_prefix
|
||||
qt_dirs = [
|
||||
f"{qt_install_headers}",
|
||||
]
|
||||
|
||||
qt_gui_path = os.path.join(qt_install_headers, "QtGui")
|
||||
qt_gui_dirs = [d for d in os.listdir(qt_gui_path) if os.path.isdir(os.path.join(qt_gui_path, d))]
|
||||
qt_dirs += [f"{qt_install_headers}/QtGui/{qt_gui_dirs[0]}/QtGui", ] if qt_gui_dirs else []
|
||||
qt_dirs += [f"{qt_install_headers}/Qt{m}" for m in qt_modules]
|
||||
|
||||
qt_libs = [f"Qt5{m}" for m in qt_modules]
|
||||
if arch == "larch64":
|
||||
qt_libs += ["GLESv2", "wayland-client"]
|
||||
qt_env.PrependENVPath('PATH', Dir("#third_party/qt5/larch64/bin/").abspath)
|
||||
elif arch != "Darwin":
|
||||
qt_libs += ["GL"]
|
||||
qt_env['QT3DIR'] = qt_env['QTDIR']
|
||||
|
||||
# compatibility for older SCons versions
|
||||
try:
|
||||
qt_env.Tool('qt3')
|
||||
except SCons.Errors.UserError:
|
||||
qt_env.Tool('qt')
|
||||
|
||||
qt_env['CPPPATH'] += qt_dirs + ["#third_party/qrcode"]
|
||||
qt_flags = [
|
||||
"-D_REENTRANT",
|
||||
"-DQT_NO_DEBUG",
|
||||
"-DQT_WIDGETS_LIB",
|
||||
"-DQT_GUI_LIB",
|
||||
"-DQT_CORE_LIB",
|
||||
"-DQT_MESSAGELOGCONTEXT",
|
||||
]
|
||||
qt_env['CXXFLAGS'] += qt_flags
|
||||
qt_env['LIBPATH'] += ['#selfdrive/ui', ]
|
||||
qt_env['LIBS'] = qt_libs
|
||||
|
||||
if GetOption("clazy"):
|
||||
checks = [
|
||||
"level0",
|
||||
"level1",
|
||||
"no-range-loop",
|
||||
"no-non-pod-global-static",
|
||||
]
|
||||
qt_env['CXX'] = 'clazy'
|
||||
qt_env['ENV']['CLAZY_IGNORE_DIRS'] = qt_dirs[0]
|
||||
qt_env['ENV']['CLAZY_CHECKS'] = ','.join(checks)
|
||||
|
||||
Export('env', 'qt_env', 'arch', 'real_arch')
|
||||
|
||||
# Build common module
|
||||
SConscript(['common/SConscript'])
|
||||
Import('_common')
|
||||
Import('_common', '_gpucommon')
|
||||
|
||||
common = [_common, 'json11', 'zmq']
|
||||
Export('common')
|
||||
gpucommon = [_gpucommon]
|
||||
|
||||
Export('common', 'gpucommon')
|
||||
|
||||
# Build messaging (cereal + msgq + socketmaster + their dependencies)
|
||||
# Enable swaglog include in submodules
|
||||
@@ -203,9 +352,15 @@ SConscript(['rednose/SConscript'])
|
||||
|
||||
# Build system services
|
||||
SConscript([
|
||||
'system/proclogd/SConscript',
|
||||
'system/ubloxd/SConscript',
|
||||
'system/loggerd/SConscript',
|
||||
])
|
||||
if arch != "Darwin":
|
||||
SConscript([
|
||||
'system/sensord/SConscript',
|
||||
'system/logcatd/SConscript',
|
||||
])
|
||||
|
||||
if arch == "larch64":
|
||||
SConscript(['system/camerad/SConscript'])
|
||||
@@ -215,12 +370,11 @@ SConscript(['third_party/SConscript'])
|
||||
|
||||
SConscript(['selfdrive/SConscript'])
|
||||
|
||||
SConscript(['sunnypilot/SConscript'])
|
||||
|
||||
if Dir('#tools/cabana/').exists() and GetOption('extras'):
|
||||
SConscript(['tools/replay/SConscript'])
|
||||
if arch != "larch64":
|
||||
SConscript(['tools/cabana/SConscript'])
|
||||
|
||||
|
||||
env.CompilationDatabase('compile_commands.json')
|
||||
external_sconscript = GetOption('external_sconscript')
|
||||
if external_sconscript:
|
||||
SConscript([external_sconscript])
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import os
|
||||
import capnp
|
||||
from importlib.resources import as_file, files
|
||||
|
||||
CEREAL_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||
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"))
|
||||
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"))
|
||||
|
||||
@@ -10,448 +10,34 @@ $Cxx.namespace("cereal");
|
||||
# DO rename the structs
|
||||
# DON'T change the identifier (e.g. @0x81c2f05a394cf4af)
|
||||
|
||||
struct ModularAssistiveDrivingSystem {
|
||||
state @0 :ModularAssistiveDrivingSystemState;
|
||||
enabled @1 :Bool;
|
||||
active @2 :Bool;
|
||||
available @3 :Bool;
|
||||
|
||||
enum ModularAssistiveDrivingSystemState {
|
||||
disabled @0;
|
||||
paused @1;
|
||||
enabled @2;
|
||||
softDisabling @3;
|
||||
overriding @4;
|
||||
}
|
||||
struct CustomReserved0 @0x81c2f05a394cf4af {
|
||||
}
|
||||
|
||||
struct IntelligentCruiseButtonManagement {
|
||||
state @0 :IntelligentCruiseButtonManagementState;
|
||||
sendButton @1 :SendButtonState;
|
||||
vTarget @2 :Float32;
|
||||
|
||||
enum IntelligentCruiseButtonManagementState {
|
||||
inactive @0; # No button press or default state
|
||||
preActive @1; # Pre-active state before transitioning to increasing or decreasing
|
||||
increasing @2; # Increasing speed
|
||||
decreasing @3; # Decreasing speed
|
||||
holding @4; # Holding steady speed
|
||||
}
|
||||
|
||||
enum SendButtonState {
|
||||
none @0;
|
||||
increase @1;
|
||||
decrease @2;
|
||||
}
|
||||
struct CustomReserved1 @0xaedffd8f31e7b55d {
|
||||
}
|
||||
|
||||
# Same struct as Log.RadarState.LeadData
|
||||
struct LeadData {
|
||||
dRel @0 :Float32;
|
||||
yRel @1 :Float32;
|
||||
vRel @2 :Float32;
|
||||
aRel @3 :Float32;
|
||||
vLead @4 :Float32;
|
||||
dPath @6 :Float32;
|
||||
vLat @7 :Float32;
|
||||
vLeadK @8 :Float32;
|
||||
aLeadK @9 :Float32;
|
||||
fcw @10 :Bool;
|
||||
status @11 :Bool;
|
||||
aLeadTau @12 :Float32;
|
||||
modelProb @13 :Float32;
|
||||
radar @14 :Bool;
|
||||
radarTrackId @15 :Int32 = -1;
|
||||
|
||||
aLeadDEPRECATED @5 :Float32;
|
||||
struct CustomReserved2 @0xf35cc4560bbf6ec2 {
|
||||
}
|
||||
|
||||
struct SelfdriveStateSP @0x81c2f05a394cf4af {
|
||||
mads @0 :ModularAssistiveDrivingSystem;
|
||||
intelligentCruiseButtonManagement @1 :IntelligentCruiseButtonManagement;
|
||||
|
||||
enum AudibleAlert {
|
||||
none @0;
|
||||
|
||||
engage @1;
|
||||
disengage @2;
|
||||
refuse @3;
|
||||
|
||||
warningSoft @4;
|
||||
warningImmediate @5;
|
||||
|
||||
prompt @6;
|
||||
promptRepeat @7;
|
||||
promptDistracted @8;
|
||||
|
||||
# unused, these are reserved for upstream events so we don't collide
|
||||
reserved9 @9;
|
||||
reserved10 @10;
|
||||
reserved11 @11;
|
||||
reserved12 @12;
|
||||
reserved13 @13;
|
||||
reserved14 @14;
|
||||
reserved15 @15;
|
||||
reserved16 @16;
|
||||
reserved17 @17;
|
||||
reserved18 @18;
|
||||
reserved19 @19;
|
||||
reserved20 @20;
|
||||
reserved21 @21;
|
||||
reserved22 @22;
|
||||
reserved23 @23;
|
||||
reserved24 @24;
|
||||
reserved25 @25;
|
||||
reserved26 @26;
|
||||
reserved27 @27;
|
||||
reserved28 @28;
|
||||
reserved29 @29;
|
||||
reserved30 @30;
|
||||
|
||||
promptSingleLow @31;
|
||||
promptSingleHigh @32;
|
||||
}
|
||||
struct CustomReserved3 @0xda96579883444c35 {
|
||||
}
|
||||
|
||||
struct ModelManagerSP @0xaedffd8f31e7b55d {
|
||||
activeBundle @0 :ModelBundle;
|
||||
selectedBundle @1 :ModelBundle;
|
||||
availableBundles @2 :List(ModelBundle);
|
||||
|
||||
struct DownloadUri {
|
||||
uri @0 :Text;
|
||||
sha256 @1 :Text;
|
||||
}
|
||||
|
||||
enum DownloadStatus {
|
||||
notDownloading @0;
|
||||
downloading @1;
|
||||
downloaded @2;
|
||||
cached @3;
|
||||
failed @4;
|
||||
}
|
||||
|
||||
struct DownloadProgress {
|
||||
status @0 :DownloadStatus;
|
||||
progress @1 :Float32;
|
||||
eta @2 :UInt32;
|
||||
}
|
||||
|
||||
struct Artifact {
|
||||
fileName @0 :Text;
|
||||
downloadUri @1 :DownloadUri;
|
||||
downloadProgress @2 :DownloadProgress;
|
||||
}
|
||||
|
||||
struct Model {
|
||||
type @0 :Type;
|
||||
artifact @1 :Artifact; # Main artifact
|
||||
metadata @2 :Artifact; # Metadata artifact
|
||||
|
||||
enum Type {
|
||||
supercombo @0;
|
||||
navigation @1;
|
||||
vision @2;
|
||||
policy @3;
|
||||
}
|
||||
}
|
||||
|
||||
enum Runner {
|
||||
snpe @0;
|
||||
tinygrad @1;
|
||||
stock @2;
|
||||
}
|
||||
|
||||
struct Override {
|
||||
key @0 :Text;
|
||||
value @1 :Text;
|
||||
}
|
||||
|
||||
struct ModelBundle {
|
||||
index @0 :UInt32;
|
||||
internalName @1 :Text;
|
||||
displayName @2 :Text;
|
||||
models @3 :List(Model);
|
||||
status @4 :DownloadStatus;
|
||||
generation @5 :UInt32;
|
||||
environment @6 :Text;
|
||||
runner @7 :Runner;
|
||||
is20hz @8 :Bool;
|
||||
ref @9 :Text;
|
||||
minimumSelectorVersion @10 :UInt32;
|
||||
overrides @11 :List(Override);
|
||||
}
|
||||
struct CustomReserved4 @0x80ae746ee2596b11 {
|
||||
}
|
||||
|
||||
struct LongitudinalPlanSP @0xf35cc4560bbf6ec2 {
|
||||
dec @0 :DynamicExperimentalControl;
|
||||
longitudinalPlanSource @1 :LongitudinalPlanSource;
|
||||
smartCruiseControl @2 :SmartCruiseControl;
|
||||
speedLimit @3 :SpeedLimit;
|
||||
vTarget @4 :Float32;
|
||||
aTarget @5 :Float32;
|
||||
events @6 :List(OnroadEventSP.Event);
|
||||
e2eAlerts @7 :E2eAlerts;
|
||||
|
||||
struct DynamicExperimentalControl {
|
||||
state @0 :DynamicExperimentalControlState;
|
||||
enabled @1 :Bool;
|
||||
active @2 :Bool;
|
||||
|
||||
enum DynamicExperimentalControlState {
|
||||
acc @0;
|
||||
blended @1;
|
||||
}
|
||||
}
|
||||
|
||||
struct SmartCruiseControl {
|
||||
vision @0 :Vision;
|
||||
map @1 :Map;
|
||||
|
||||
struct Vision {
|
||||
state @0 :VisionState;
|
||||
vTarget @1 :Float32;
|
||||
aTarget @2 :Float32;
|
||||
currentLateralAccel @3 :Float32;
|
||||
maxPredictedLateralAccel @4 :Float32;
|
||||
enabled @5 :Bool;
|
||||
active @6 :Bool;
|
||||
}
|
||||
|
||||
struct Map {
|
||||
state @0 :MapState;
|
||||
vTarget @1 :Float32;
|
||||
aTarget @2 :Float32;
|
||||
enabled @3 :Bool;
|
||||
active @4 :Bool;
|
||||
}
|
||||
|
||||
enum VisionState {
|
||||
disabled @0; # System disabled or inactive.
|
||||
enabled @1; # No predicted substantial turn on vision range.
|
||||
entering @2; # A substantial turn is predicted ahead, adapting speed to turn comfort levels.
|
||||
turning @3; # Actively turning. Managing acceleration to provide a roll on turn feeling.
|
||||
leaving @4; # Road ahead straightens. Start to allow positive acceleration.
|
||||
overriding @5; # System overriding with manual control.
|
||||
}
|
||||
|
||||
enum MapState {
|
||||
disabled @0; # System disabled or inactive.
|
||||
enabled @1; # No predicted substantial turn on map range.
|
||||
turning @2; # Actively turning. Managing acceleration to provide a roll on turn feeling.
|
||||
overriding @3; # System overriding with manual control.
|
||||
}
|
||||
}
|
||||
|
||||
struct SpeedLimit {
|
||||
resolver @0 :Resolver;
|
||||
assist @1 :Assist;
|
||||
|
||||
struct Resolver {
|
||||
speedLimit @0 :Float32;
|
||||
distToSpeedLimit @1 :Float32;
|
||||
source @2 :Source;
|
||||
speedLimitOffset @3 :Float32;
|
||||
speedLimitLast @4 :Float32;
|
||||
speedLimitFinal @5 :Float32;
|
||||
speedLimitFinalLast @6 :Float32;
|
||||
speedLimitValid @7 :Bool;
|
||||
speedLimitLastValid @8 :Bool;
|
||||
}
|
||||
|
||||
struct Assist {
|
||||
state @0 :AssistState;
|
||||
enabled @1 :Bool;
|
||||
active @2 :Bool;
|
||||
vTarget @3 :Float32;
|
||||
aTarget @4 :Float32;
|
||||
}
|
||||
|
||||
enum Source {
|
||||
none @0;
|
||||
car @1;
|
||||
map @2;
|
||||
}
|
||||
|
||||
enum AssistState {
|
||||
disabled @0;
|
||||
inactive @1; # No speed limit set or not enabled by parameter.
|
||||
preActive @2;
|
||||
pending @3; # Awaiting new speed limit.
|
||||
adapting @4; # Reducing speed to match new speed limit.
|
||||
active @5; # Cruising at speed limit.
|
||||
}
|
||||
}
|
||||
|
||||
enum LongitudinalPlanSource {
|
||||
cruise @0;
|
||||
sccVision @1;
|
||||
sccMap @2;
|
||||
speedLimitAssist @3;
|
||||
}
|
||||
|
||||
struct E2eAlerts {
|
||||
greenLightAlert @0 :Bool;
|
||||
leadDepartAlert @1 :Bool;
|
||||
}
|
||||
struct CustomReserved5 @0xa5cd762cd951a455 {
|
||||
}
|
||||
|
||||
struct OnroadEventSP @0xda96579883444c35 {
|
||||
events @0 :List(Event);
|
||||
|
||||
struct Event {
|
||||
name @0 :EventName;
|
||||
|
||||
# event types
|
||||
enable @1 :Bool;
|
||||
noEntry @2 :Bool;
|
||||
warning @3 :Bool; # alerts presented only when enabled or soft disabling
|
||||
userDisable @4 :Bool;
|
||||
softDisable @5 :Bool;
|
||||
immediateDisable @6 :Bool;
|
||||
preEnable @7 :Bool;
|
||||
permanent @8 :Bool; # alerts presented regardless of openpilot state
|
||||
overrideLateral @10 :Bool;
|
||||
overrideLongitudinal @9 :Bool;
|
||||
}
|
||||
|
||||
enum EventName {
|
||||
lkasEnable @0;
|
||||
lkasDisable @1;
|
||||
manualSteeringRequired @2;
|
||||
manualLongitudinalRequired @3;
|
||||
silentLkasEnable @4;
|
||||
silentLkasDisable @5;
|
||||
silentBrakeHold @6;
|
||||
silentWrongGear @7;
|
||||
silentReverseGear @8;
|
||||
silentDoorOpen @9;
|
||||
silentSeatbeltNotLatched @10;
|
||||
silentParkBrake @11;
|
||||
controlsMismatchLateral @12;
|
||||
hyundaiRadarTracksConfirmed @13;
|
||||
experimentalModeSwitched @14;
|
||||
wrongCarModeAlertOnly @15;
|
||||
pedalPressedAlertOnly @16;
|
||||
laneTurnLeft @17;
|
||||
laneTurnRight @18;
|
||||
speedLimitPreActive @19;
|
||||
speedLimitActive @20;
|
||||
speedLimitChanged @21;
|
||||
speedLimitPending @22;
|
||||
e2eChime @23;
|
||||
}
|
||||
struct CustomReserved6 @0xf98d843bfd7004a3 {
|
||||
}
|
||||
|
||||
struct CarParamsSP @0x80ae746ee2596b11 {
|
||||
flags @0 :UInt32; # flags for car specific quirks in sunnypilot
|
||||
safetyParam @1 : Int16; # flags for sunnypilot's custom safety flags
|
||||
pcmCruiseSpeed @3 :Bool;
|
||||
intelligentCruiseButtonManagementAvailable @4 :Bool;
|
||||
enableGasInterceptor @5 :Bool;
|
||||
|
||||
neuralNetworkLateralControl @2 :NeuralNetworkLateralControl;
|
||||
|
||||
struct NeuralNetworkLateralControl {
|
||||
model @0 :Model;
|
||||
fuzzyFingerprint @1 :Bool;
|
||||
|
||||
struct Model {
|
||||
path @0 :Text;
|
||||
name @1 :Text;
|
||||
}
|
||||
}
|
||||
struct CustomReserved7 @0xb86e6369214c01c8 {
|
||||
}
|
||||
|
||||
struct CarControlSP @0xa5cd762cd951a455 {
|
||||
mads @0 :ModularAssistiveDrivingSystem;
|
||||
params @1 :List(Param);
|
||||
leadOne @2 :LeadData;
|
||||
leadTwo @3 :LeadData;
|
||||
intelligentCruiseButtonManagement @4 :IntelligentCruiseButtonManagement;
|
||||
|
||||
struct Param {
|
||||
key @0 :Text;
|
||||
type @2 :ParamType;
|
||||
value @3 :Data;
|
||||
|
||||
valueDEPRECATED @1 :Text; # The data type change may cause issues with backwards compatibility.
|
||||
}
|
||||
|
||||
enum ParamType {
|
||||
string @0;
|
||||
bool @1;
|
||||
int @2;
|
||||
float @3;
|
||||
time @4;
|
||||
json @5;
|
||||
bytes @6;
|
||||
}
|
||||
struct CustomReserved8 @0xf416ec09499d9d19 {
|
||||
}
|
||||
|
||||
struct BackupManagerSP @0xf98d843bfd7004a3 {
|
||||
backupStatus @0 :Status;
|
||||
restoreStatus @1 :Status;
|
||||
backupProgress @2 :Float32;
|
||||
restoreProgress @3 :Float32;
|
||||
lastError @4 :Text;
|
||||
currentBackup @5 :BackupInfo;
|
||||
backupHistory @6 :List(BackupInfo);
|
||||
|
||||
enum Status {
|
||||
idle @0;
|
||||
inProgress @1;
|
||||
completed @2;
|
||||
failed @3;
|
||||
}
|
||||
|
||||
struct Version {
|
||||
major @0 :UInt16;
|
||||
minor @1 :UInt16;
|
||||
patch @2 :UInt16;
|
||||
build @3 :UInt16;
|
||||
branch @4 :Text;
|
||||
}
|
||||
|
||||
struct MetadataEntry {
|
||||
key @0 :Text;
|
||||
value @1 :Text;
|
||||
tags @2 :List(Text);
|
||||
}
|
||||
|
||||
struct BackupInfo {
|
||||
deviceId @0 :Text;
|
||||
version @1 :UInt32;
|
||||
config @2 :Text;
|
||||
isEncrypted @3 :Bool;
|
||||
createdAt @4 :Text; # ISO timestamp
|
||||
updatedAt @5 :Text; # ISO timestamp
|
||||
sunnypilotVersion @6 :Version;
|
||||
backupMetadata @7 :List(MetadataEntry);
|
||||
}
|
||||
}
|
||||
|
||||
struct CarStateSP @0xb86e6369214c01c8 {
|
||||
speedLimit @0 :Float32;
|
||||
}
|
||||
|
||||
struct LiveMapDataSP @0xf416ec09499d9d19 {
|
||||
speedLimitValid @0 :Bool;
|
||||
speedLimit @1 :Float32;
|
||||
speedLimitAheadValid @2 :Bool;
|
||||
speedLimitAhead @3 :Float32;
|
||||
speedLimitAheadDistance @4 :Float32;
|
||||
roadName @5 :Text;
|
||||
}
|
||||
|
||||
struct ModelDataV2SP @0xa1680744031fdb2d {
|
||||
laneTurnDirection @0 :TurnDirection;
|
||||
|
||||
enum TurnDirection {
|
||||
none @0;
|
||||
turnLeft @1;
|
||||
turnRight @2;
|
||||
}
|
||||
struct CustomReserved9 @0xa1680744031fdb2d {
|
||||
}
|
||||
|
||||
struct CustomReserved10 @0xcb9fd56c7057593a {
|
||||
|
||||
110
cereal/log.capnp
110
cereal/log.capnp
@@ -48,7 +48,6 @@ struct OnroadEvent @0xc4fa6047f024e718 {
|
||||
preEnableStandstill @12; # added during pre-enable state with brake
|
||||
gasPressedOverride @13; # added when user is pressing gas with no disengage on gas
|
||||
steerOverride @14;
|
||||
steerDisengage @94; # exits active state
|
||||
cruiseDisabled @15;
|
||||
speedTooLow @16;
|
||||
outOfSpace @17;
|
||||
@@ -127,9 +126,6 @@ struct OnroadEvent @0xc4fa6047f024e718 {
|
||||
espActive @90;
|
||||
personalityChanged @91;
|
||||
aeb @92;
|
||||
userBookmark @95;
|
||||
excessiveActuation @96;
|
||||
audioFeedback @97;
|
||||
|
||||
soundsUnavailableDEPRECATED @47;
|
||||
}
|
||||
@@ -492,8 +488,8 @@ struct DeviceState @0xa4d8b5af2aa492eb {
|
||||
# device thermals
|
||||
cpuTempC @26 :List(Float32);
|
||||
gpuTempC @27 :List(Float32);
|
||||
dspTempC @49 :Float32;
|
||||
memoryTempC @28 :Float32;
|
||||
nvmeTempC @35 :List(Float32);
|
||||
modemTempC @36 :List(Float32);
|
||||
pmicTempC @39 :List(Float32);
|
||||
intakeTempC @46 :Float32;
|
||||
@@ -569,7 +565,6 @@ struct DeviceState @0xa4d8b5af2aa492eb {
|
||||
chargingDisabledDEPRECATED @18 :Bool;
|
||||
usbOnlineDEPRECATED @12 :Bool;
|
||||
ambientTempCDEPRECATED @30 :Float32;
|
||||
nvmeTempCDEPRECATED @35 :List(Float32);
|
||||
}
|
||||
|
||||
struct PandaState @0xa7649e2575e4591e {
|
||||
@@ -585,8 +580,9 @@ struct PandaState @0xa7649e2575e4591e {
|
||||
heartbeatLost @22 :Bool;
|
||||
interruptLoad @25 :Float32;
|
||||
fanPower @28 :UInt8;
|
||||
fanStallCount @34 :UInt8;
|
||||
|
||||
spiErrorCount @33 :UInt16;
|
||||
spiChecksumErrorCount @33 :UInt16;
|
||||
|
||||
harnessStatus @21 :HarnessStatus;
|
||||
sbu1Voltage @35 :Float32;
|
||||
@@ -713,7 +709,6 @@ struct PandaState @0xa7649e2575e4591e {
|
||||
usbPowerModeDEPRECATED @12 :PeripheralState.UsbPowerModeDEPRECATED;
|
||||
safetyParamDEPRECATED @20 :Int16;
|
||||
safetyParam2DEPRECATED @26 :UInt32;
|
||||
fanStallCountDEPRECATED @34 :UInt8;
|
||||
}
|
||||
|
||||
struct PeripheralState {
|
||||
@@ -918,8 +913,6 @@ struct ControlsState @0x97ff69c53601abf1 {
|
||||
saturated @7 :Bool;
|
||||
actualLateralAccel @9 :Float32;
|
||||
desiredLateralAccel @10 :Float32;
|
||||
desiredLateralJerk @11 :Float32;
|
||||
version @12 :Int32;
|
||||
}
|
||||
|
||||
struct LateralLQRState {
|
||||
@@ -1087,7 +1080,7 @@ struct ModelDataV2 {
|
||||
confidence @23: ConfidenceClass;
|
||||
|
||||
# Model perceived motion
|
||||
temporalPoseDEPRECATED @21 :Pose;
|
||||
temporalPose @21 :Pose;
|
||||
|
||||
# e2e lateral planner
|
||||
action @26: Action;
|
||||
@@ -1181,8 +1174,6 @@ struct ModelDataV2 {
|
||||
|
||||
struct Action {
|
||||
desiredCurvature @0 :Float32;
|
||||
desiredAcceleration @1 :Float32;
|
||||
shouldStop @2 :Bool;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1595,10 +1586,6 @@ struct UbloxGnss {
|
||||
svId @0 :UInt8;
|
||||
gnssId @1 :UInt8;
|
||||
flagsBitfield @2 :UInt32;
|
||||
cno @3 :UInt8;
|
||||
elevationDeg @4 :Int8;
|
||||
azimuthDeg @5 :Int16;
|
||||
pseudorangeResidual @6 :Float32;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2148,10 +2135,13 @@ struct Joystick {
|
||||
struct DriverStateV2 {
|
||||
frameId @0 :UInt32;
|
||||
modelExecutionTime @1 :Float32;
|
||||
dspExecutionTimeDEPRECATED @2 :Float32;
|
||||
gpuExecutionTime @8 :Float32;
|
||||
rawPredictions @3 :Data;
|
||||
|
||||
poorVisionProb @4 :Float32;
|
||||
wheelOnRightProb @5 :Float32;
|
||||
|
||||
leftDriverData @6 :DriverData;
|
||||
rightDriverData @7 :DriverData;
|
||||
|
||||
@@ -2166,14 +2156,10 @@ struct DriverStateV2 {
|
||||
leftBlinkProb @7 :Float32;
|
||||
rightBlinkProb @8 :Float32;
|
||||
sunglassesProb @9 :Float32;
|
||||
phoneProb @13 :Float32;
|
||||
notReadyProbDEPRECATED @12 :List(Float32);
|
||||
occludedProbDEPRECATED @10 :Float32;
|
||||
readyProbDEPRECATED @11 :List(Float32);
|
||||
occludedProb @10 :Float32;
|
||||
readyProb @11 :List(Float32);
|
||||
notReadyProb @12 :List(Float32);
|
||||
}
|
||||
|
||||
dspExecutionTimeDEPRECATED @2 :Float32;
|
||||
poorVisionProbDEPRECATED @4 :Float32;
|
||||
}
|
||||
|
||||
struct DriverStateDEPRECATED @0xb83c6cc593ed0a00 {
|
||||
@@ -2225,9 +2211,6 @@ struct DriverMonitoringState @0xb83cda094a1da284 {
|
||||
hiStdCount @14 :UInt32;
|
||||
isActiveMode @16 :Bool;
|
||||
isRHD @4 :Bool;
|
||||
uncertainCount @19 :UInt32;
|
||||
phoneProbOffset @20 :Float32;
|
||||
phoneProbValidCount @21 :UInt32;
|
||||
|
||||
isPreviewDEPRECATED @15 :Bool;
|
||||
rhdCheckedDEPRECATED @5 :Bool;
|
||||
@@ -2289,24 +2272,6 @@ struct LiveTorqueParametersData {
|
||||
points @10 :List(List(Float32));
|
||||
version @11 :Int32;
|
||||
useParams @12 :Bool;
|
||||
calPerc @13 :Int8;
|
||||
}
|
||||
|
||||
struct LiveDelayData {
|
||||
lateralDelay @0 :Float32;
|
||||
validBlocks @1 :Int32;
|
||||
status @2 :Status;
|
||||
|
||||
lateralDelayEstimate @3 :Float32;
|
||||
lateralDelayEstimateStd @5 :Float32;
|
||||
points @4 :List(Float32);
|
||||
calPerc @6 :Int8;
|
||||
|
||||
enum Status {
|
||||
unestimated @0;
|
||||
estimated @1;
|
||||
invalid @2;
|
||||
}
|
||||
}
|
||||
|
||||
struct LiveMapDataDEPRECATED {
|
||||
@@ -2475,27 +2440,16 @@ struct DebugAlert {
|
||||
alertText2 @1 :Text;
|
||||
}
|
||||
|
||||
struct UserBookmark @0xfe346a9de48d9b50 {
|
||||
struct UserFlag {
|
||||
}
|
||||
|
||||
struct SoundPressure @0xdc24138990726023 {
|
||||
struct Microphone {
|
||||
soundPressure @0 :Float32;
|
||||
|
||||
# uncalibrated, A-weighted
|
||||
soundPressureWeighted @3 :Float32;
|
||||
soundPressureWeightedDb @1 :Float32;
|
||||
|
||||
filteredSoundPressureWeightedDbDEPRECATED @2 :Float32;
|
||||
}
|
||||
|
||||
struct AudioData {
|
||||
data @0 :Data;
|
||||
sampleRate @1 :UInt32;
|
||||
}
|
||||
|
||||
struct AudioFeedback {
|
||||
audio @0 :AudioData;
|
||||
blockNum @1 :UInt16;
|
||||
filteredSoundPressureWeightedDb @2 :Float32;
|
||||
}
|
||||
|
||||
struct Touch {
|
||||
@@ -2524,10 +2478,13 @@ struct Event {
|
||||
controlsState @7 :ControlsState;
|
||||
selfdriveState @130 :SelfdriveState;
|
||||
gyroscope @99 :SensorEventData;
|
||||
gyroscope2 @100 :SensorEventData;
|
||||
accelerometer @98 :SensorEventData;
|
||||
accelerometer2 @101 :SensorEventData;
|
||||
magnetometer @95 :SensorEventData;
|
||||
lightSensor @96 :SensorEventData;
|
||||
temperatureSensor @97 :SensorEventData;
|
||||
temperatureSensor2 @123 :SensorEventData;
|
||||
pandaStates @81 :List(PandaState);
|
||||
peripheralState @80 :PeripheralState;
|
||||
radarState @13 :RadarState;
|
||||
@@ -2547,7 +2504,6 @@ struct Event {
|
||||
gnssMeasurements @91 :GnssMeasurements;
|
||||
liveParameters @61 :LiveParametersData;
|
||||
liveTorqueParameters @94 :LiveTorqueParametersData;
|
||||
liveDelay @146 : LiveDelayData;
|
||||
cameraOdometry @63 :CameraOdometry;
|
||||
thumbnail @66: Thumbnail;
|
||||
onroadEvents @134: List(OnroadEvent);
|
||||
@@ -2572,8 +2528,7 @@ struct Event {
|
||||
livestreamDriverEncodeIdx @119 :EncodeIndex;
|
||||
|
||||
# microphone data
|
||||
soundPressure @103 :SoundPressure;
|
||||
rawAudioData @147 :AudioData;
|
||||
microphone @103 :Microphone;
|
||||
|
||||
# systems stuff
|
||||
androidLog @20 :AndroidLogEntry;
|
||||
@@ -2595,13 +2550,9 @@ struct Event {
|
||||
mapRenderState @105: MapRenderState;
|
||||
|
||||
# UI services
|
||||
userFlag @93 :UserFlag;
|
||||
uiDebug @102 :UIDebug;
|
||||
|
||||
# driving feedback
|
||||
userBookmark @93 :UserBookmark;
|
||||
bookmarkButton @148 :UserBookmark;
|
||||
audioFeedback @149 :AudioFeedback;
|
||||
|
||||
# *********** debug ***********
|
||||
testJoystick @52 :Joystick;
|
||||
roadEncodeData @86 :EncodeData;
|
||||
@@ -2625,16 +2576,16 @@ struct Event {
|
||||
# DO change the name of the field and struct
|
||||
# DON'T change the ID (e.g. @107)
|
||||
# DON'T change which struct it points to
|
||||
selfdriveStateSP @107 :Custom.SelfdriveStateSP;
|
||||
modelManagerSP @108 :Custom.ModelManagerSP;
|
||||
longitudinalPlanSP @109 :Custom.LongitudinalPlanSP;
|
||||
onroadEventsSP @110 :Custom.OnroadEventSP;
|
||||
carParamsSP @111 :Custom.CarParamsSP;
|
||||
carControlSP @112 :Custom.CarControlSP;
|
||||
backupManagerSP @113 :Custom.BackupManagerSP;
|
||||
carStateSP @114 :Custom.CarStateSP;
|
||||
liveMapDataSP @115 :Custom.LiveMapDataSP;
|
||||
modelDataV2SP @116 :Custom.ModelDataV2SP;
|
||||
customReserved0 @107 :Custom.CustomReserved0;
|
||||
customReserved1 @108 :Custom.CustomReserved1;
|
||||
customReserved2 @109 :Custom.CustomReserved2;
|
||||
customReserved3 @110 :Custom.CustomReserved3;
|
||||
customReserved4 @111 :Custom.CustomReserved4;
|
||||
customReserved5 @112 :Custom.CustomReserved5;
|
||||
customReserved6 @113 :Custom.CustomReserved6;
|
||||
customReserved7 @114 :Custom.CustomReserved7;
|
||||
customReserved8 @115 :Custom.CustomReserved8;
|
||||
customReserved9 @116 :Custom.CustomReserved9;
|
||||
customReserved10 @136 :Custom.CustomReserved10;
|
||||
customReserved11 @137 :Custom.CustomReserved11;
|
||||
customReserved12 @138 :Custom.CustomReserved12;
|
||||
@@ -2687,11 +2638,8 @@ struct Event {
|
||||
lateralPlanDEPRECATED @64 :LateralPlan;
|
||||
navModelDEPRECATED @104 :NavModelData;
|
||||
uiPlanDEPRECATED @106 :UiPlan;
|
||||
liveLocationKalman @72 :LiveLocationKalman;
|
||||
liveLocationKalmanDEPRECATED @72 :LiveLocationKalman;
|
||||
liveTracksDEPRECATED @16 :List(LiveTracksDEPRECATED);
|
||||
onroadEventsDEPRECATED @68: List(Car.OnroadEventDEPRECATED);
|
||||
gyroscope2DEPRECATED @100 :SensorEventData;
|
||||
accelerometer2DEPRECATED @101 :SensorEventData;
|
||||
temperatureSensor2DEPRECATED @123 :SensorEventData;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
from msgq.ipc_pyx import Context, Poller, SubSocket, PubSocket, SocketEventHandle, toggle_fake_events, \
|
||||
set_fake_prefix, get_fake_prefix, delete_fake_prefix, wait_for_one_event
|
||||
from msgq.ipc_pyx import MultiplePublishersError, IpcError
|
||||
from msgq import fake_event_handle, drain_sock_raw
|
||||
from msgq import fake_event_handle, pub_sock, sub_sock, drain_sock_raw
|
||||
import msgq
|
||||
|
||||
import os
|
||||
@@ -18,20 +18,6 @@ from openpilot.common.util import MovingAverage
|
||||
NO_TRAVERSAL_LIMIT = 2**64-1
|
||||
|
||||
|
||||
def pub_sock(endpoint: str) -> PubSocket:
|
||||
service = SERVICE_LIST.get(endpoint)
|
||||
segment_size = service.queue_size if service else 0
|
||||
return msgq.pub_sock(endpoint, segment_size)
|
||||
|
||||
|
||||
def sub_sock(endpoint: str, poller: Optional[Poller] = None, addr: str = "127.0.0.1",
|
||||
conflate: bool = False, timeout: Optional[int] = None) -> SubSocket:
|
||||
service = SERVICE_LIST.get(endpoint)
|
||||
segment_size = service.queue_size if service else 0
|
||||
return msgq.sub_sock(endpoint, poller=poller, addr=addr, conflate=conflate,
|
||||
timeout=timeout, segment_size=segment_size)
|
||||
|
||||
|
||||
def reset_context():
|
||||
msgq.context = Context()
|
||||
|
||||
@@ -159,16 +145,12 @@ class SubMaster:
|
||||
self.updated = {s: False for s in services}
|
||||
self.recv_time = {s: 0. for s in services}
|
||||
self.recv_frame = {s: 0 for s in services}
|
||||
self.alive = {s: False for s in services}
|
||||
self.freq_ok = {s: False for s in services}
|
||||
self.sock = {}
|
||||
self.data = {}
|
||||
self.logMonoTime = {s: 0 for s in services}
|
||||
|
||||
# zero-frequency / on-demand services are always alive and presumed valid; all others must pass checks
|
||||
on_demand = {s: SERVICE_LIST[s].frequency <= 1e-5 for s in services}
|
||||
self.static_freq_services = set(s for s in services if not on_demand[s])
|
||||
self.alive = {s: on_demand[s] for s in services}
|
||||
self.freq_ok = {s: on_demand[s] for s in services}
|
||||
self.valid = {s: on_demand[s] for s in services}
|
||||
self.valid = {}
|
||||
self.logMonoTime = {}
|
||||
|
||||
self.freq_tracker: Dict[str, FrequencyTracker] = {}
|
||||
self.poller = Poller()
|
||||
@@ -195,6 +177,8 @@ class SubMaster:
|
||||
data = new_message(s, 0) # lists
|
||||
|
||||
self.data[s] = getattr(data.as_reader(), s)
|
||||
self.logMonoTime[s] = 0
|
||||
self.valid[s] = False
|
||||
self.freq_tracker[s] = FrequencyTracker(SERVICE_LIST[s].frequency, self.update_freq, s == poll)
|
||||
|
||||
def __getitem__(self, s: str) -> capnp.lib.capnp._DynamicStructReader:
|
||||
@@ -231,10 +215,14 @@ class SubMaster:
|
||||
self.logMonoTime[s] = msg.logMonoTime
|
||||
self.valid[s] = msg.valid
|
||||
|
||||
for s in self.static_freq_services:
|
||||
# alive if delay is within 10x the expected frequency; checks relaxed in simulator
|
||||
self.alive[s] = (cur_time - self.recv_time[s]) < (10. / SERVICE_LIST[s].frequency) or (self.seen[s] and self.simulation)
|
||||
self.freq_ok[s] = self.freq_tracker[s].valid or self.simulation
|
||||
for s in self.services:
|
||||
if SERVICE_LIST[s].frequency > 1e-5 and not self.simulation:
|
||||
# alive if delay is within 10x the expected frequency
|
||||
self.alive[s] = (cur_time - self.recv_time[s]) < (10. / SERVICE_LIST[s].frequency)
|
||||
self.freq_ok[s] = self.freq_tracker[s].valid
|
||||
else:
|
||||
self.freq_ok[s] = True
|
||||
self.alive[s] = self.seen[s] if self.simulation else True
|
||||
|
||||
def all_alive(self, service_list: Optional[List[str]] = None) -> bool:
|
||||
return all(self.alive[s] for s in (service_list or self.services) if s not in self.ignore_alive)
|
||||
|
||||
@@ -33,7 +33,7 @@ MessageContext message_context;
|
||||
struct SubMaster::SubMessage {
|
||||
std::string name;
|
||||
SubSocket *socket = nullptr;
|
||||
float freq = 0.0f;
|
||||
int freq = 0;
|
||||
bool updated = false, alive = false, valid = false, ignore_alive;
|
||||
uint64_t rcv_time = 0, rcv_frame = 0;
|
||||
void *allocated_msg_reader = nullptr;
|
||||
@@ -50,7 +50,7 @@ SubMaster::SubMaster(const std::vector<const char *> &service_list, const std::v
|
||||
assert(services.count(std::string(name)) > 0);
|
||||
|
||||
service serv = services.at(std::string(name));
|
||||
SubSocket *socket = SubSocket::create(message_context.context(), name, address ? address : "127.0.0.1", true, true, serv.queue_size);
|
||||
SubSocket *socket = SubSocket::create(message_context.context(), name, address ? address : "127.0.0.1", true);
|
||||
assert(socket != 0);
|
||||
bool is_polled = inList(poll, name) || poll.empty();
|
||||
if (is_polled) poller_->registerSocket(socket);
|
||||
@@ -187,8 +187,7 @@ SubMaster::~SubMaster() {
|
||||
PubMaster::PubMaster(const std::vector<const char *> &service_list) {
|
||||
for (auto name : service_list) {
|
||||
assert(services.count(name) > 0);
|
||||
service serv = services.at(std::string(name));
|
||||
PubSocket *socket = PubSocket::create(message_context.context(), name, true, serv.queue_size);
|
||||
PubSocket *socket = PubSocket::create(message_context.context(), name);
|
||||
assert(socket);
|
||||
sockets_[name] = socket;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ def zmq_sleep(t=1):
|
||||
|
||||
# TODO: this should take any capnp struct and returrn a msg with random populated data
|
||||
def random_carstate():
|
||||
fields = ["vEgo", "aEgo", "brake", "steeringAngleDeg"]
|
||||
fields = ["vEgo", "aEgo", "gas", "steeringAngleDeg"]
|
||||
msg = messaging.new_message("carState")
|
||||
cs = msg.carState
|
||||
for f in fields:
|
||||
@@ -177,8 +177,8 @@ class TestMessaging:
|
||||
|
||||
# wait 5 socket timeouts before sending
|
||||
msg = random_carstate()
|
||||
start_time = time.monotonic()
|
||||
delayed_send(sock_timeout*5, pub_sock, msg.to_bytes())
|
||||
start_time = time.monotonic()
|
||||
recvd = messaging.recv_one_retry(sub_sock)
|
||||
assert (time.monotonic() - start_time) >= sock_timeout*5
|
||||
assert isinstance(recvd, capnp._DynamicStructReader)
|
||||
|
||||
@@ -6,7 +6,6 @@ import cereal.messaging as messaging
|
||||
from cereal.messaging.tests.test_messaging import events, random_sock, random_socks, \
|
||||
random_bytes, random_carstate, assert_carstate, \
|
||||
zmq_sleep
|
||||
from cereal.services import SERVICE_LIST
|
||||
|
||||
|
||||
class TestSubMaster:
|
||||
@@ -27,9 +26,7 @@ class TestSubMaster:
|
||||
sm = messaging.SubMaster(socks)
|
||||
assert sm.frame == -1
|
||||
assert not any(sm.updated.values())
|
||||
assert not any(sm.seen.values())
|
||||
on_demand = {s: SERVICE_LIST[s].frequency <= 1e-5 for s in sm.services}
|
||||
assert all(sm.alive[s] == sm.valid[s] == sm.freq_ok[s] == on_demand[s] for s in sm.services)
|
||||
assert not any(sm.alive.values())
|
||||
assert all(t == 0. for t in sm.recv_time.values())
|
||||
assert all(f == 0 for f in sm.recv_frame.values())
|
||||
assert all(t == 0 for t in sm.logMonoTime.values())
|
||||
@@ -86,7 +83,6 @@ class TestSubMaster:
|
||||
"cameraOdometry": (20, 10),
|
||||
"liveCalibration": (4, 4),
|
||||
"carParams": (None, None),
|
||||
"userBookmark": (None, None),
|
||||
}
|
||||
|
||||
for service, (max_freq, min_freq) in checks.items():
|
||||
|
||||
@@ -1,222 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import sys
|
||||
from typing import Any, List, Tuple
|
||||
|
||||
DEBUG = False
|
||||
|
||||
|
||||
def print_debug(string: str) -> None:
|
||||
if DEBUG:
|
||||
print(string)
|
||||
|
||||
|
||||
def create_schema_instance(struct: Any, prop: Tuple[str, Any]) -> Any:
|
||||
"""
|
||||
Create a new instance of a schema type, handling different field types.
|
||||
|
||||
Args:
|
||||
struct: The Cap'n Proto schema structure
|
||||
prop: A tuple containing the field name and field metadata
|
||||
|
||||
Returns:
|
||||
A new initialized schema instance
|
||||
"""
|
||||
struct_instance = struct.new_message()
|
||||
field_name, field_metadata = prop
|
||||
|
||||
try:
|
||||
field_type = field_metadata.proto.slot.type.which()
|
||||
|
||||
# Initialize different types of fields
|
||||
if field_type in ('list', 'text', 'data'):
|
||||
struct_instance.init(field_name, 1)
|
||||
print_debug(f"Initialized list/text/data field: {field_name}")
|
||||
elif field_type in ('struct', 'object'):
|
||||
struct_instance.init(field_name)
|
||||
print_debug(f"Initialized struct/object field: {field_name}")
|
||||
|
||||
return struct_instance
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error creating instance for {field_name}: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def get_schema_fields(schema_struct: Any) -> List[Tuple[str, Any]]:
|
||||
"""
|
||||
Retrieve all fields from a given schema structure.
|
||||
|
||||
Args:
|
||||
schema_struct: The Cap'n Proto schema structure
|
||||
|
||||
Returns:
|
||||
A list of field names and their metadata
|
||||
"""
|
||||
try:
|
||||
# Get all fields from the schema
|
||||
schema_fields = list(schema_struct.schema.fields.items())
|
||||
|
||||
print_debug("Discovered schema fields:")
|
||||
for field_name, field_metadata in schema_fields:
|
||||
print_debug(f"- {field_name}")
|
||||
|
||||
return schema_fields
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error retrieving schema fields: {e}")
|
||||
return []
|
||||
|
||||
|
||||
def generate_schema_instances(schema_struct: Any) -> List[Any]:
|
||||
"""
|
||||
Generate instances for all fields in a given schema.
|
||||
|
||||
Args:
|
||||
schema_struct: The Cap'n Proto schema structure
|
||||
|
||||
Returns:
|
||||
A list of schema instances
|
||||
"""
|
||||
schema_fields = get_schema_fields(schema_struct)
|
||||
instances = []
|
||||
|
||||
for field_prop in schema_fields:
|
||||
try:
|
||||
instance = create_schema_instance(schema_struct, field_prop)
|
||||
if instance is not None:
|
||||
instances.append(instance)
|
||||
except Exception as e:
|
||||
print(f"Skipping field due to error: {e}")
|
||||
|
||||
print(f"Generated {len(instances)} schema instances")
|
||||
return instances
|
||||
|
||||
|
||||
def persist_instances(instances: List[Any], filename: str) -> None:
|
||||
"""
|
||||
Write schema instances to a binary file.
|
||||
|
||||
Args:
|
||||
instances: List of schema instances
|
||||
filename: Output file path
|
||||
"""
|
||||
try:
|
||||
with open(filename, 'wb') as f:
|
||||
for instance in instances:
|
||||
f.write(instance.to_bytes())
|
||||
|
||||
print(f"Successfully wrote {len(instances)} instances to {filename}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error persisting instances: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def read_instances(filename: str, schema_type: Any) -> List[Any]:
|
||||
"""
|
||||
Read schema instances from a binary file.
|
||||
|
||||
Args:
|
||||
filename: Input file path
|
||||
schema_type: The schema type to use for reading
|
||||
|
||||
Returns:
|
||||
A list of read schema instances
|
||||
"""
|
||||
try:
|
||||
with open(filename, 'rb') as f:
|
||||
data = f.read()
|
||||
|
||||
instances = list(schema_type.read_multiple_bytes(data))
|
||||
|
||||
print(f"Read {len(instances)} instances from {filename}")
|
||||
return instances
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error reading instances: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def compare_schemas(original_instances: List[Any], read_instances: List[Any]) -> bool:
|
||||
"""
|
||||
Compare original and read-back instances to detect potential breaking changes.
|
||||
|
||||
Args:
|
||||
original_instances: List of originally generated instances
|
||||
read_instances: List of instances read back from file
|
||||
|
||||
Returns:
|
||||
Boolean indicating whether schemas appear compatible
|
||||
"""
|
||||
if len(original_instances) != len(read_instances):
|
||||
print("❌ Schema Compatibility Warning: Instance count mismatch")
|
||||
return False
|
||||
|
||||
compatible = True
|
||||
for struct in read_instances:
|
||||
try:
|
||||
getattr(struct, struct.which()) # Attempting to access the field to validate readability
|
||||
except Exception as e:
|
||||
print(f"❌ Structural change detected: {struct.which()} is not readable.\nFull error: {e}")
|
||||
compatible = False
|
||||
|
||||
return compatible
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
CLI entry point for schema compatibility testing.
|
||||
"""
|
||||
# Setup argument parser
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Cap\'n Proto Schema Compatibility Testing Tool',
|
||||
epilog='Test schema compatibility by generating and reading back instances.'
|
||||
)
|
||||
|
||||
# Add mutually exclusive group for generation or reading mode
|
||||
mode_group = parser.add_mutually_exclusive_group(required=True)
|
||||
mode_group.add_argument('-g', '--generate', action='store_true',
|
||||
help='Generate schema instances')
|
||||
mode_group.add_argument('-r', '--read', action='store_true',
|
||||
help='Read and validate schema instances')
|
||||
|
||||
# Common arguments
|
||||
parser.add_argument('-f', '--file',
|
||||
default='schema_instances.bin',
|
||||
help='Output/input binary file (default: schema_instances.bin)')
|
||||
|
||||
# Parse arguments
|
||||
args = parser.parse_args()
|
||||
|
||||
# Import the schema dynamically
|
||||
try:
|
||||
from cereal import log
|
||||
schema_type = log.Event
|
||||
except ImportError:
|
||||
print("Error: Unable to import schema. Ensure 'cereal' is installed.")
|
||||
sys.exit(1)
|
||||
|
||||
# Execute based on mode
|
||||
if args.generate:
|
||||
print("🔧 Generating Schema Instances")
|
||||
instances = generate_schema_instances(schema_type)
|
||||
persist_instances(instances, args.file)
|
||||
print("✅ Instance generation complete")
|
||||
|
||||
elif args.read:
|
||||
print("🔍 Reading and Validating Schema Instances")
|
||||
generated_instances = generate_schema_instances(schema_type)
|
||||
read_back_instances = read_instances(args.file, schema_type)
|
||||
|
||||
# Compare schemas
|
||||
if compare_schemas(generated_instances, read_back_instances):
|
||||
print("✅ Schema Compatibility: No breaking changes detected")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("❌ Potential Schema Breaking Changes Detected")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,56 +1,48 @@
|
||||
#!/usr/bin/env python3
|
||||
from enum import IntEnum
|
||||
from typing import Optional
|
||||
|
||||
|
||||
# TODO: this should be automatically determined using the capnp schema
|
||||
class QueueSize(IntEnum):
|
||||
BIG = 10 * 1024 * 1024 # 10MB - video frames, large AI outputs
|
||||
MEDIUM = 2 * 1024 * 1024 # 2MB - high freq (CAN), livestream
|
||||
SMALL = 250 * 1024 # 250KB - most services
|
||||
|
||||
|
||||
class Service:
|
||||
def __init__(self, should_log: bool, frequency: float, decimation: Optional[int] = None,
|
||||
queue_size: QueueSize = QueueSize.SMALL):
|
||||
def __init__(self, should_log: bool, frequency: float, decimation: Optional[int] = None):
|
||||
self.should_log = should_log
|
||||
self.frequency = frequency
|
||||
self.decimation = decimation
|
||||
self.queue_size = queue_size
|
||||
|
||||
|
||||
_services: dict[str, tuple] = {
|
||||
# service: (should_log, frequency, qlog decimation (optional))
|
||||
# note: the "EncodeIdx" packets will still be in the log
|
||||
"gyroscope": (True, 104., 104),
|
||||
"gyroscope2": (True, 100., 100),
|
||||
"accelerometer": (True, 104., 104),
|
||||
"accelerometer2": (True, 100., 100),
|
||||
"magnetometer": (True, 25.),
|
||||
"lightSensor": (True, 100., 100),
|
||||
"temperatureSensor": (True, 2., 200),
|
||||
"temperatureSensor2": (True, 2., 200),
|
||||
"gpsNMEA": (True, 9.),
|
||||
"deviceState": (True, 2., 1),
|
||||
"touch": (True, 20., 1),
|
||||
"can": (True, 100., 2053, QueueSize.BIG), # decimation gives ~3 msgs in a full segment
|
||||
"controlsState": (True, 100., 10, QueueSize.MEDIUM),
|
||||
"can": (True, 100., 2053), # decimation gives ~3 msgs in a full segment
|
||||
"controlsState": (True, 100., 10),
|
||||
"selfdriveState": (True, 100., 10),
|
||||
"pandaStates": (True, 10., 1),
|
||||
"peripheralState": (True, 2., 1),
|
||||
"radarState": (True, 20., 5),
|
||||
"roadEncodeIdx": (False, 20., 1),
|
||||
"liveTracks": (True, 20.),
|
||||
"sendcan": (True, 100., 139, QueueSize.MEDIUM),
|
||||
"sendcan": (True, 100., 139),
|
||||
"logMessage": (True, 0.),
|
||||
"errorLogMessage": (True, 0., 1),
|
||||
"liveCalibration": (True, 4., 4),
|
||||
"liveTorqueParameters": (True, 4., 1),
|
||||
"liveDelay": (True, 4., 1),
|
||||
"androidLog": (True, 0.),
|
||||
"carState": (True, 100., 10),
|
||||
"carControl": (True, 100., 10),
|
||||
"carOutput": (True, 100., 10),
|
||||
"longitudinalPlan": (True, 20., 10),
|
||||
"driverAssistance": (True, 20., 20),
|
||||
"procLog": (True, 0.5, 15, QueueSize.BIG),
|
||||
"procLog": (True, 0.5, 15),
|
||||
"gpsLocationExternal": (True, 10., 10),
|
||||
"gpsLocation": (True, 1., 1),
|
||||
"ubloxGnss": (True, 10.),
|
||||
@@ -72,46 +64,30 @@ _services: dict[str, tuple] = {
|
||||
"wideRoadEncodeIdx": (False, 20., 1),
|
||||
"wideRoadCameraState": (True, 20., 20),
|
||||
"drivingModelData": (True, 20., 10),
|
||||
"modelV2": (True, 20., None, QueueSize.BIG),
|
||||
"modelV2": (True, 20.),
|
||||
"managerState": (True, 2., 1),
|
||||
"uploaderState": (True, 0., 1),
|
||||
"navInstruction": (True, 1., 10),
|
||||
"navRoute": (True, 0.),
|
||||
"navThumbnail": (True, 0.),
|
||||
"qRoadEncodeIdx": (False, 20.),
|
||||
"userBookmark": (True, 0., 1),
|
||||
"soundPressure": (True, 10., 10),
|
||||
"rawAudioData": (False, 20.),
|
||||
"bookmarkButton": (True, 0., 1),
|
||||
"audioFeedback": (True, 0., 1),
|
||||
"roadEncodeData": (False, 20., None, QueueSize.BIG),
|
||||
"driverEncodeData": (False, 20., None, QueueSize.BIG),
|
||||
"wideRoadEncodeData": (False, 20., None, QueueSize.BIG),
|
||||
"qRoadEncodeData": (False, 20., None, QueueSize.BIG),
|
||||
|
||||
# sunnypilot
|
||||
"modelManagerSP": (False, 1., 1, QueueSize.BIG),
|
||||
"backupManagerSP": (False, 1., 1, QueueSize.BIG),
|
||||
"selfdriveStateSP": (True, 100., 10),
|
||||
"longitudinalPlanSP": (True, 20., 10),
|
||||
"onroadEventsSP": (True, 1., 1),
|
||||
"carParamsSP": (True, 0.02, 1),
|
||||
"carControlSP": (True, 100., 10),
|
||||
"carStateSP": (True, 100., 10),
|
||||
"liveMapDataSP": (True, 1., 1),
|
||||
"modelDataV2SP": (True, 20., None, QueueSize.BIG),
|
||||
"liveLocationKalman": (True, 20.),
|
||||
"userFlag": (True, 0., 1),
|
||||
"microphone": (True, 10., 10),
|
||||
|
||||
# debug
|
||||
"uiDebug": (True, 0., 1),
|
||||
"testJoystick": (True, 0.),
|
||||
"alertDebug": (True, 20., 5),
|
||||
"roadEncodeData": (False, 20.),
|
||||
"driverEncodeData": (False, 20.),
|
||||
"wideRoadEncodeData": (False, 20.),
|
||||
"qRoadEncodeData": (False, 20.),
|
||||
"livestreamWideRoadEncodeIdx": (False, 20.),
|
||||
"livestreamRoadEncodeIdx": (False, 20.),
|
||||
"livestreamDriverEncodeIdx": (False, 20.),
|
||||
"livestreamWideRoadEncodeData": (False, 20., None, QueueSize.MEDIUM),
|
||||
"livestreamRoadEncodeData": (False, 20., None, QueueSize.MEDIUM),
|
||||
"livestreamDriverEncodeData": (False, 20., None, QueueSize.MEDIUM),
|
||||
"livestreamWideRoadEncodeData": (False, 20.),
|
||||
"livestreamRoadEncodeData": (False, 20.),
|
||||
"livestreamDriverEncodeData": (False, 20.),
|
||||
"customReservedRawData0": (True, 0.),
|
||||
"customReservedRawData1": (True, 0.),
|
||||
"customReservedRawData2": (True, 0.),
|
||||
@@ -129,13 +105,13 @@ def build_header():
|
||||
h += "#include <map>\n"
|
||||
h += "#include <string>\n"
|
||||
|
||||
h += "struct service { std::string name; bool should_log; float frequency; int decimation; size_t queue_size; };\n"
|
||||
h += "struct service { std::string name; bool should_log; int frequency; int decimation; };\n"
|
||||
h += "static std::map<std::string, service> services = {\n"
|
||||
for k, v in SERVICE_LIST.items():
|
||||
should_log = "true" if v.should_log else "false"
|
||||
decimation = -1 if v.decimation is None else v.decimation
|
||||
h += ' { "%s", {"%s", %s, %f, %d, %d}},\n' % \
|
||||
(k, k, should_log, v.frequency, decimation, v.queue_size)
|
||||
h += ' { "%s", {"%s", %s, %d, %d}},\n' % \
|
||||
(k, k, should_log, v.frequency, decimation)
|
||||
h += "};\n"
|
||||
|
||||
h += "#endif\n"
|
||||
|
||||
13
codecov.yml
Normal file
13
codecov.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
comment: false
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
informational: true
|
||||
patch: off
|
||||
|
||||
ignore:
|
||||
- "**/test_*.py"
|
||||
- "selfdrive/test/**"
|
||||
- "system/version.py" # codecov changes depending on if we are in a branch or not
|
||||
- "tools"
|
||||
@@ -4,12 +4,22 @@ common_libs = [
|
||||
'params.cc',
|
||||
'swaglog.cc',
|
||||
'util.cc',
|
||||
'ratekeeper.cc',
|
||||
'i2c.cc',
|
||||
'watchdog.cc',
|
||||
'ratekeeper.cc'
|
||||
]
|
||||
|
||||
if arch != "Darwin":
|
||||
common_libs.append('gpio.cc')
|
||||
|
||||
_common = env.Library('common', common_libs, LIBS="json11")
|
||||
|
||||
files = [
|
||||
'clutil.cc',
|
||||
]
|
||||
|
||||
_common = env.Library('common', common_libs, LIBS="json11")
|
||||
Export('_common')
|
||||
_gpucommon = env.Library('gpucommon', files)
|
||||
Export('_common', '_gpucommon')
|
||||
|
||||
if GetOption('extras'):
|
||||
env.Program('tests/test_common',
|
||||
|
||||
46
common/api.py
Normal file
46
common/api.py
Normal file
@@ -0,0 +1,46 @@
|
||||
import jwt
|
||||
import os
|
||||
import requests
|
||||
from datetime import datetime, timedelta, UTC
|
||||
from openpilot.system.hardware.hw import Paths
|
||||
from openpilot.system.version import get_version
|
||||
|
||||
API_HOST = os.getenv('API_HOST', 'https://api.commadotai.com')
|
||||
|
||||
class Api:
|
||||
def __init__(self, dongle_id):
|
||||
self.dongle_id = dongle_id
|
||||
with open(Paths.persist_root()+'/comma/id_rsa') as f:
|
||||
self.private_key = f.read()
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
return self.request('GET', *args, **kwargs)
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
return self.request('POST', *args, **kwargs)
|
||||
|
||||
def request(self, method, endpoint, timeout=None, access_token=None, **params):
|
||||
return api_get(endpoint, method=method, timeout=timeout, access_token=access_token, **params)
|
||||
|
||||
def get_token(self, expiry_hours=1):
|
||||
now = datetime.now(UTC).replace(tzinfo=None)
|
||||
payload = {
|
||||
'identity': self.dongle_id,
|
||||
'nbf': now,
|
||||
'iat': now,
|
||||
'exp': now + timedelta(hours=expiry_hours)
|
||||
}
|
||||
token = jwt.encode(payload, self.private_key, algorithm='RS256')
|
||||
if isinstance(token, bytes):
|
||||
token = token.decode('utf8')
|
||||
return token
|
||||
|
||||
|
||||
def api_get(endpoint, method='GET', timeout=None, access_token=None, **params):
|
||||
headers = {}
|
||||
if access_token is not None:
|
||||
headers['Authorization'] = "JWT " + access_token
|
||||
|
||||
headers['User-Agent'] = "openpilot-" + get_version()
|
||||
|
||||
return requests.request(method, API_HOST + "/" + endpoint, timeout=timeout, headers=headers, params=params)
|
||||
@@ -1,26 +0,0 @@
|
||||
from openpilot.common.api.comma_connect import CommaConnectApi
|
||||
|
||||
|
||||
class Api:
|
||||
def __init__(self, dongle_id):
|
||||
self.service = CommaConnectApi(dongle_id)
|
||||
|
||||
def request(self, method, endpoint, **params):
|
||||
return self.service.request(method, endpoint, **params)
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
return self.service.get(*args, **kwargs)
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
return self.service.post(*args, **kwargs)
|
||||
|
||||
def get_token(self, payload_extra=None, expiry_hours=1):
|
||||
return self.service.get_token(payload_extra, expiry_hours)
|
||||
|
||||
|
||||
def api_get(endpoint, method='GET', timeout=None, access_token=None, **params):
|
||||
return CommaConnectApi(None).api_get(endpoint, method, timeout, access_token, **params)
|
||||
|
||||
|
||||
def get_key_pair() -> tuple[str, str, str] | tuple[None, None, None]:
|
||||
return CommaConnectApi(None).get_key_pair()
|
||||
@@ -1,70 +0,0 @@
|
||||
import jwt
|
||||
import os
|
||||
import requests
|
||||
import unicodedata
|
||||
from datetime import datetime, timedelta, UTC
|
||||
from openpilot.system.hardware.hw import Paths
|
||||
from openpilot.system.version import get_version
|
||||
|
||||
# name: jwt signature algorithm
|
||||
KEYS = {"id_rsa": "RS256",
|
||||
"id_ecdsa": "ES256"}
|
||||
|
||||
|
||||
class BaseApi:
|
||||
def __init__(self, dongle_id, api_host, user_agent="openpilot-"):
|
||||
self.dongle_id = dongle_id
|
||||
self.api_host = api_host
|
||||
self.user_agent = user_agent
|
||||
self.jwt_algorithm, self.private_key, _ = self.get_key_pair()
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
return self.request('GET', *args, **kwargs)
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
return self.request('POST', *args, **kwargs)
|
||||
|
||||
def request(self, method, endpoint, timeout=None, access_token=None, **params):
|
||||
return self.api_get(endpoint, method=method, timeout=timeout, access_token=access_token, **params)
|
||||
|
||||
def _get_token(self, payload_extra=None, expiry_hours=1, **extra_payload):
|
||||
now = datetime.now(UTC).replace(tzinfo=None)
|
||||
payload = {
|
||||
'identity': self.dongle_id,
|
||||
'nbf': now,
|
||||
'iat': now,
|
||||
'exp': now + timedelta(hours=expiry_hours),
|
||||
**extra_payload
|
||||
}
|
||||
if payload_extra is not None:
|
||||
payload.update(payload_extra)
|
||||
token = jwt.encode(payload, self.private_key, algorithm=self.jwt_algorithm)
|
||||
if isinstance(token, bytes):
|
||||
token = token.decode('utf8')
|
||||
return token
|
||||
|
||||
def get_token(self, payload_extra=None, expiry_hours=1):
|
||||
return self._get_token(payload_extra, expiry_hours)
|
||||
|
||||
def remove_non_ascii_chars(self, text):
|
||||
normalized_text = unicodedata.normalize('NFD', text)
|
||||
ascii_encoded_text = normalized_text.encode('ascii', 'ignore')
|
||||
return ascii_encoded_text.decode()
|
||||
|
||||
def api_get(self, endpoint, method='GET', timeout=None, access_token=None, json=None, **params):
|
||||
headers = {}
|
||||
if access_token is not None:
|
||||
headers['Authorization'] = "JWT " + access_token
|
||||
|
||||
version = self.remove_non_ascii_chars(get_version())
|
||||
headers['User-Agent'] = self.user_agent + version
|
||||
|
||||
return requests.request(method, f"{self.api_host}/{endpoint}", timeout=timeout, headers=headers, json=json, params=params)
|
||||
|
||||
@staticmethod
|
||||
def get_key_pair() -> tuple[str, str, str] | tuple[None, None, None]:
|
||||
for key in KEYS:
|
||||
if os.path.isfile(Paths.persist_root() + f'/comma/{key}') and os.path.isfile(Paths.persist_root() + f'/comma/{key}.pub'):
|
||||
with open(Paths.persist_root() + f'/comma/{key}') as private, open(Paths.persist_root() + f'/comma/{key}.pub') as public:
|
||||
return KEYS[key], private.read(), public.read()
|
||||
return None, None, None
|
||||
@@ -1,11 +0,0 @@
|
||||
import os
|
||||
|
||||
from openpilot.common.api.base import BaseApi
|
||||
|
||||
API_HOST = os.getenv('API_HOST', 'https://api.commadotai.com')
|
||||
|
||||
|
||||
class CommaConnectApi(BaseApi):
|
||||
def __init__(self, dongle_id):
|
||||
super().__init__(dongle_id, API_HOST)
|
||||
self.user_agent = "openpilot-"
|
||||
@@ -1,7 +1,6 @@
|
||||
import numpy as np
|
||||
|
||||
# conversions
|
||||
class CV:
|
||||
class Conversions:
|
||||
# Speed
|
||||
MPH_TO_KPH = 1.609344
|
||||
KPH_TO_MPH = 1. / MPH_TO_KPH
|
||||
@@ -18,6 +17,3 @@ class CV:
|
||||
|
||||
# Mass
|
||||
LB_TO_KG = 0.453592
|
||||
|
||||
|
||||
ACCELERATION_DUE_TO_GRAVITY = 9.81 # m/s^2
|
||||
9
common/dict_helpers.py
Normal file
9
common/dict_helpers.py
Normal file
@@ -0,0 +1,9 @@
|
||||
# remove all keys that end in DEPRECATED
|
||||
def strip_deprecated_keys(d):
|
||||
for k in list(d.keys()):
|
||||
if isinstance(k, str):
|
||||
if k.endswith('DEPRECATED'):
|
||||
d.pop(k)
|
||||
elif isinstance(d[k], dict):
|
||||
strip_deprecated_keys(d[k])
|
||||
return d
|
||||
8
common/ffi_wrapper.py
Normal file
8
common/ffi_wrapper.py
Normal file
@@ -0,0 +1,8 @@
|
||||
import platform
|
||||
|
||||
|
||||
def suffix():
|
||||
if platform.system() == "Darwin":
|
||||
return ".dylib"
|
||||
else:
|
||||
return ".so"
|
||||
58
common/file_helpers.py
Normal file
58
common/file_helpers.py
Normal file
@@ -0,0 +1,58 @@
|
||||
import io
|
||||
import os
|
||||
import tempfile
|
||||
import contextlib
|
||||
import zstandard as zstd
|
||||
|
||||
LOG_COMPRESSION_LEVEL = 10 # little benefit up to level 15. level ~17 is a small step change
|
||||
|
||||
|
||||
class CallbackReader:
|
||||
"""Wraps a file, but overrides the read method to also
|
||||
call a callback function with the number of bytes read so far."""
|
||||
def __init__(self, f, callback, *args):
|
||||
self.f = f
|
||||
self.callback = callback
|
||||
self.cb_args = args
|
||||
self.total_read = 0
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.f, attr)
|
||||
|
||||
def read(self, *args, **kwargs):
|
||||
chunk = self.f.read(*args, **kwargs)
|
||||
self.total_read += len(chunk)
|
||||
self.callback(*self.cb_args, self.total_read)
|
||||
return chunk
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def atomic_write_in_dir(path: str, mode: str = 'w', buffering: int = -1, encoding: str = None, newline: str = None,
|
||||
overwrite: bool = False):
|
||||
"""Write to a file atomically using a temporary file in the same directory as the destination file."""
|
||||
dir_name = os.path.dirname(path)
|
||||
|
||||
if not overwrite and os.path.exists(path):
|
||||
raise FileExistsError(f"File '{path}' already exists. To overwrite it, set 'overwrite' to True.")
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode=mode, buffering=buffering, encoding=encoding, newline=newline, dir=dir_name, delete=False) as tmp_file:
|
||||
yield tmp_file
|
||||
tmp_file_name = tmp_file.name
|
||||
os.replace(tmp_file_name, path)
|
||||
|
||||
|
||||
def get_upload_stream(filepath: str, should_compress: bool) -> tuple[io.BufferedIOBase, int]:
|
||||
if not should_compress:
|
||||
file_size = os.path.getsize(filepath)
|
||||
file_stream = open(filepath, "rb")
|
||||
return file_stream, file_size
|
||||
|
||||
# Compress the file on the fly
|
||||
compressed_stream = io.BytesIO()
|
||||
compressor = zstd.ZstdCompressor(level=LOG_COMPRESSION_LEVEL)
|
||||
|
||||
with open(filepath, "rb") as f:
|
||||
compressor.copy_stream(f, compressed_stream)
|
||||
compressed_size = compressed_stream.tell()
|
||||
compressed_stream.seek(0)
|
||||
return compressed_stream, compressed_size
|
||||
@@ -1,4 +1,5 @@
|
||||
class FirstOrderFilter:
|
||||
# first order filter
|
||||
def __init__(self, x0, rc, dt, initialized=True):
|
||||
self.x = x0
|
||||
self.dt = dt
|
||||
@@ -15,20 +16,3 @@ class FirstOrderFilter:
|
||||
self.initialized = True
|
||||
self.x = x
|
||||
return self.x
|
||||
|
||||
|
||||
class BounceFilter(FirstOrderFilter):
|
||||
def __init__(self, x0, rc, dt, initialized=True, bounce=2):
|
||||
self.velocity = FirstOrderFilter(0.0, 0.15, dt)
|
||||
self.bounce = bounce
|
||||
super().__init__(x0, rc, dt, initialized)
|
||||
|
||||
def update(self, x):
|
||||
super().update(x)
|
||||
scale = self.dt / (1.0 / 60.0) # tuned at 60 fps
|
||||
self.velocity.x += (x - self.x) * self.bounce * scale * self.dt
|
||||
self.velocity.update(0.0)
|
||||
if abs(self.velocity.x) < 1e-5:
|
||||
self.velocity.x = 0.0
|
||||
self.x += self.velocity.x
|
||||
return self.x
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from functools import cache
|
||||
import subprocess
|
||||
from openpilot.common.utils import run_cmd, run_cmd_default
|
||||
from openpilot.common.run import run_cmd, run_cmd_default
|
||||
|
||||
|
||||
@cache
|
||||
|
||||
84
common/gpio.cc
Normal file
84
common/gpio.cc
Normal file
@@ -0,0 +1,84 @@
|
||||
#include "common/gpio.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef __APPLE__
|
||||
int gpio_init(int pin_nr, bool output) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpio_set(int pin_nr, bool high) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpiochip_get_ro_value_fd(const char* consumer_label, int gpiochiop_id, int pin_nr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <linux/gpio.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "common/util.h"
|
||||
#include "common/swaglog.h"
|
||||
|
||||
int gpio_init(int pin_nr, bool output) {
|
||||
char pin_dir_path[50];
|
||||
int pin_dir_path_len = snprintf(pin_dir_path, sizeof(pin_dir_path),
|
||||
"/sys/class/gpio/gpio%d/direction", pin_nr);
|
||||
if (pin_dir_path_len <= 0) {
|
||||
return -1;
|
||||
}
|
||||
const char *value = output ? "out" : "in";
|
||||
return util::write_file(pin_dir_path, (void*)value, strlen(value));
|
||||
}
|
||||
|
||||
int gpio_set(int pin_nr, bool high) {
|
||||
char pin_val_path[50];
|
||||
int pin_val_path_len = snprintf(pin_val_path, sizeof(pin_val_path),
|
||||
"/sys/class/gpio/gpio%d/value", pin_nr);
|
||||
if (pin_val_path_len <= 0) {
|
||||
return -1;
|
||||
}
|
||||
return util::write_file(pin_val_path, (void*)(high ? "1" : "0"), 1);
|
||||
}
|
||||
|
||||
int gpiochip_get_ro_value_fd(const char* consumer_label, int gpiochiop_id, int pin_nr) {
|
||||
|
||||
// Assumed that all interrupt pins are unexported and rights are given to
|
||||
// read from gpiochip0.
|
||||
std::string gpiochip_path = "/dev/gpiochip" + std::to_string(gpiochiop_id);
|
||||
int fd = open(gpiochip_path.c_str(), O_RDONLY);
|
||||
if (fd < 0) {
|
||||
LOGE("Error opening gpiochip0 fd");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Setup event
|
||||
struct gpioevent_request rq;
|
||||
rq.lineoffset = pin_nr;
|
||||
rq.handleflags = GPIOHANDLE_REQUEST_INPUT;
|
||||
|
||||
/* Requesting both edges as the data ready pulse from the lsm6ds sensor is
|
||||
very short(75us) and is mostly detected as falling edge instead of rising.
|
||||
So if it is detected as rising the following falling edge is skipped. */
|
||||
rq.eventflags = GPIOEVENT_REQUEST_BOTH_EDGES;
|
||||
|
||||
strncpy(rq.consumer_label, consumer_label, std::size(rq.consumer_label) - 1);
|
||||
int ret = util::safe_ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &rq);
|
||||
if (ret == -1) {
|
||||
LOGE("Unable to get line event from ioctl : %s", strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return rq.fd;
|
||||
}
|
||||
|
||||
#endif
|
||||
33
common/gpio.h
Normal file
33
common/gpio.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
// Pin definitions
|
||||
#ifdef QCOM2
|
||||
#define GPIO_HUB_RST_N 30
|
||||
#define GPIO_UBLOX_RST_N 32
|
||||
#define GPIO_UBLOX_SAFEBOOT_N 33
|
||||
#define GPIO_GNSS_PWR_EN 34 /* SCHEMATIC LABEL: GPIO_UBLOX_PWR_EN */
|
||||
#define GPIO_STM_RST_N 124
|
||||
#define GPIO_STM_BOOT0 134
|
||||
#define GPIO_BMX_ACCEL_INT 21
|
||||
#define GPIO_BMX_GYRO_INT 23
|
||||
#define GPIO_BMX_MAGN_INT 87
|
||||
#define GPIO_LSM_INT 84
|
||||
#define GPIOCHIP_INT 0
|
||||
#else
|
||||
#define GPIO_HUB_RST_N 0
|
||||
#define GPIO_UBLOX_RST_N 0
|
||||
#define GPIO_UBLOX_SAFEBOOT_N 0
|
||||
#define GPIO_GNSS_PWR_EN 0 /* SCHEMATIC LABEL: GPIO_UBLOX_PWR_EN */
|
||||
#define GPIO_STM_RST_N 0
|
||||
#define GPIO_STM_BOOT0 0
|
||||
#define GPIO_BMX_ACCEL_INT 0
|
||||
#define GPIO_BMX_GYRO_INT 0
|
||||
#define GPIO_BMX_MAGN_INT 0
|
||||
#define GPIO_LSM_INT 0
|
||||
#define GPIOCHIP_INT 0
|
||||
#endif
|
||||
|
||||
int gpio_init(int pin_nr, bool output);
|
||||
int gpio_set(int pin_nr, bool high);
|
||||
|
||||
int gpiochip_get_ro_value_fd(const char* consumer_label, int gpiochiop_id, int pin_nr);
|
||||
@@ -1,6 +1,4 @@
|
||||
import os
|
||||
import fcntl
|
||||
import ctypes
|
||||
from functools import cache
|
||||
|
||||
def gpio_init(pin: int, output: bool) -> None:
|
||||
@@ -54,36 +52,3 @@ def get_irqs_for_action(action: str) -> list[str]:
|
||||
if irq.isdigit() and action in get_irq_action(irq):
|
||||
ret.append(irq)
|
||||
return ret
|
||||
|
||||
# *** gpiochip ***
|
||||
|
||||
class gpioevent_data(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("timestamp", ctypes.c_uint64),
|
||||
("id", ctypes.c_uint32),
|
||||
]
|
||||
|
||||
class gpioevent_request(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("lineoffset", ctypes.c_uint32),
|
||||
("handleflags", ctypes.c_uint32),
|
||||
("eventflags", ctypes.c_uint32),
|
||||
("label", ctypes.c_char * 32),
|
||||
("fd", ctypes.c_int)
|
||||
]
|
||||
|
||||
def gpiochip_get_ro_value_fd(label: str, gpiochip_id: int, pin: int) -> int:
|
||||
GPIOEVENT_REQUEST_BOTH_EDGES = 0x3
|
||||
GPIOHANDLE_REQUEST_INPUT = 0x1
|
||||
GPIO_GET_LINEEVENT_IOCTL = 0xc030b404
|
||||
|
||||
rq = gpioevent_request()
|
||||
rq.lineoffset = pin
|
||||
rq.handleflags = GPIOHANDLE_REQUEST_INPUT
|
||||
rq.eventflags = GPIOEVENT_REQUEST_BOTH_EDGES
|
||||
rq.label = label.encode('utf-8')[:31] + b'\0'
|
||||
|
||||
fd = os.open(f"/dev/gpiochip{gpiochip_id}", os.O_RDONLY)
|
||||
fcntl.ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, rq)
|
||||
os.close(fd)
|
||||
return int(rq.fd)
|
||||
|
||||
92
common/i2c.cc
Normal file
92
common/i2c.cc
Normal file
@@ -0,0 +1,92 @@
|
||||
#include "common/i2c.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "common/swaglog.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#define UNUSED(x) (void)(x)
|
||||
|
||||
#ifdef QCOM2
|
||||
// TODO: decide if we want to install libi2c-dev everywhere
|
||||
extern "C" {
|
||||
#include <linux/i2c-dev.h>
|
||||
#include <i2c/smbus.h>
|
||||
}
|
||||
|
||||
I2CBus::I2CBus(uint8_t bus_id) {
|
||||
char bus_name[20];
|
||||
snprintf(bus_name, 20, "/dev/i2c-%d", bus_id);
|
||||
|
||||
i2c_fd = HANDLE_EINTR(open(bus_name, O_RDWR));
|
||||
if (i2c_fd < 0) {
|
||||
throw std::runtime_error("Failed to open I2C bus");
|
||||
}
|
||||
}
|
||||
|
||||
I2CBus::~I2CBus() {
|
||||
if (i2c_fd >= 0) {
|
||||
close(i2c_fd);
|
||||
}
|
||||
}
|
||||
|
||||
int I2CBus::read_register(uint8_t device_address, uint register_address, uint8_t *buffer, uint8_t len) {
|
||||
std::lock_guard lk(m);
|
||||
|
||||
int ret = 0;
|
||||
|
||||
ret = HANDLE_EINTR(ioctl(i2c_fd, I2C_SLAVE, device_address));
|
||||
if (ret < 0) { goto fail; }
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(i2c_fd, register_address, len, buffer);
|
||||
if ((ret < 0) || (ret != len)) { goto fail; }
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int I2CBus::set_register(uint8_t device_address, uint register_address, uint8_t data) {
|
||||
std::lock_guard lk(m);
|
||||
|
||||
int ret = 0;
|
||||
|
||||
ret = HANDLE_EINTR(ioctl(i2c_fd, I2C_SLAVE, device_address));
|
||||
if (ret < 0) { goto fail; }
|
||||
|
||||
ret = i2c_smbus_write_byte_data(i2c_fd, register_address, data);
|
||||
if (ret < 0) { goto fail; }
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
I2CBus::I2CBus(uint8_t bus_id) {
|
||||
UNUSED(bus_id);
|
||||
i2c_fd = -1;
|
||||
}
|
||||
|
||||
I2CBus::~I2CBus() {}
|
||||
|
||||
int I2CBus::read_register(uint8_t device_address, uint register_address, uint8_t *buffer, uint8_t len) {
|
||||
UNUSED(device_address);
|
||||
UNUSED(register_address);
|
||||
UNUSED(buffer);
|
||||
UNUSED(len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int I2CBus::set_register(uint8_t device_address, uint register_address, uint8_t data) {
|
||||
UNUSED(device_address);
|
||||
UNUSED(register_address);
|
||||
UNUSED(data);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
19
common/i2c.h
Normal file
19
common/i2c.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
class I2CBus {
|
||||
private:
|
||||
int i2c_fd;
|
||||
std::mutex m;
|
||||
|
||||
public:
|
||||
I2CBus(uint8_t bus_id);
|
||||
~I2CBus();
|
||||
|
||||
int read_register(uint8_t device_address, uint register_address, uint8_t *buffer, uint8_t len);
|
||||
int set_register(uint8_t device_address, uint register_address, uint8_t data);
|
||||
};
|
||||
@@ -199,6 +199,7 @@ class SwagLogger(logging.Logger):
|
||||
co = f.f_code
|
||||
filename = os.path.normcase(co.co_filename)
|
||||
|
||||
# TODO: is this pylint exception correct?
|
||||
if filename == _srcfile:
|
||||
f = f.f_back
|
||||
continue
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
#define DEFAULT_MODEL "Dark Souls 2 (Default)"
|
||||
@@ -103,12 +103,10 @@ Params::~Params() {
|
||||
assert(queue.empty());
|
||||
}
|
||||
|
||||
std::vector<std::string> Params::allKeys(ParamKeyFlag flag) const {
|
||||
std::vector<std::string> Params::allKeys() const {
|
||||
std::vector<std::string> ret;
|
||||
for (auto &p : keys) {
|
||||
if (flag == ALL || (p.second.flags & flag)) {
|
||||
ret.push_back(p.first);
|
||||
}
|
||||
ret.push_back(p.first);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -117,16 +115,8 @@ bool Params::checkKey(const std::string &key) {
|
||||
return keys.find(key) != keys.end();
|
||||
}
|
||||
|
||||
ParamKeyFlag Params::getKeyFlag(const std::string &key) {
|
||||
return static_cast<ParamKeyFlag>(keys[key].flags);
|
||||
}
|
||||
|
||||
ParamKeyType Params::getKeyType(const std::string &key) {
|
||||
return keys[key].type;
|
||||
}
|
||||
|
||||
std::optional<std::string> Params::getKeyDefaultValue(const std::string &key) {
|
||||
return keys[key].default_value;
|
||||
return static_cast<ParamKeyType>(keys[key]);
|
||||
}
|
||||
|
||||
int Params::put(const char* key, const char* value, size_t value_size) {
|
||||
@@ -150,7 +140,7 @@ int Params::put(const char* key, const char* value, size_t value_size) {
|
||||
}
|
||||
|
||||
// fsync to force persist the changes.
|
||||
if ((result = HANDLE_EINTR(fsync(tmp_fd))) < 0) break;
|
||||
if ((result = fsync(tmp_fd)) < 0) break;
|
||||
|
||||
FileLock file_lock(params_path + "/.lock");
|
||||
|
||||
@@ -205,17 +195,17 @@ std::map<std::string, std::string> Params::readAll() {
|
||||
return util::read_files_in_dir(getParamPath());
|
||||
}
|
||||
|
||||
void Params::clearAll(ParamKeyFlag key_flag) {
|
||||
void Params::clearAll(ParamKeyType key_type) {
|
||||
FileLock file_lock(params_path + "/.lock");
|
||||
|
||||
// 1) delete params of key_flag
|
||||
// 1) delete params of key_type
|
||||
// 2) delete files that are not defined in the keys.
|
||||
if (DIR *d = opendir(getParamPath().c_str())) {
|
||||
struct dirent *de = NULL;
|
||||
while ((de = readdir(d))) {
|
||||
if (de->d_type != DT_DIR) {
|
||||
auto it = keys.find(de->d_name);
|
||||
if (it == keys.end() || (it->second.flags & key_flag)) {
|
||||
if (it == keys.end() || (it->second & key_type)) {
|
||||
unlink(getParamPath(de->d_name).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <future>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
@@ -10,34 +9,16 @@
|
||||
|
||||
#include "common/queue.h"
|
||||
|
||||
enum ParamKeyFlag {
|
||||
enum ParamKeyType {
|
||||
PERSISTENT = 0x02,
|
||||
CLEAR_ON_MANAGER_START = 0x04,
|
||||
CLEAR_ON_ONROAD_TRANSITION = 0x08,
|
||||
CLEAR_ON_OFFROAD_TRANSITION = 0x10,
|
||||
DONT_LOG = 0x20,
|
||||
DEVELOPMENT_ONLY = 0x40,
|
||||
CLEAR_ON_IGNITION_ON = 0x80,
|
||||
BACKUP = 0x100,
|
||||
ALL = 0xFFFFFFFF
|
||||
};
|
||||
|
||||
enum ParamKeyType {
|
||||
STRING = 0, // must be utf-8 decodable
|
||||
BOOL = 1,
|
||||
INT = 2,
|
||||
FLOAT = 3,
|
||||
TIME = 4, // ISO 8601
|
||||
JSON = 5,
|
||||
BYTES = 6
|
||||
};
|
||||
|
||||
struct ParamKeyAttributes {
|
||||
uint32_t flags;
|
||||
ParamKeyType type;
|
||||
std::optional<std::string> default_value = std::nullopt;
|
||||
};
|
||||
|
||||
class Params {
|
||||
public:
|
||||
explicit Params(const std::string &path = {});
|
||||
@@ -46,18 +27,16 @@ public:
|
||||
Params(const Params&) = delete;
|
||||
Params& operator=(const Params&) = delete;
|
||||
|
||||
std::vector<std::string> allKeys(ParamKeyFlag flag = ALL) const;
|
||||
std::vector<std::string> allKeys() const;
|
||||
bool checkKey(const std::string &key);
|
||||
ParamKeyFlag getKeyFlag(const std::string &key);
|
||||
ParamKeyType getKeyType(const std::string &key);
|
||||
std::optional<std::string> getKeyDefaultValue(const std::string &key);
|
||||
inline std::string getParamPath(const std::string &key = {}) {
|
||||
return params_path + params_prefix + (key.empty() ? "" : "/" + key);
|
||||
}
|
||||
|
||||
// Delete a value
|
||||
int remove(const std::string &key);
|
||||
void clearAll(ParamKeyFlag flag);
|
||||
void clearAll(ParamKeyType type);
|
||||
|
||||
// helpers for reading values
|
||||
std::string get(const std::string &key, bool block = false);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from openpilot.common.params_pyx import Params, ParamKeyFlag, ParamKeyType, UnknownKeyName
|
||||
from openpilot.common.params_pyx import Params, ParamKeyType, UnknownKeyName
|
||||
assert Params
|
||||
assert ParamKeyFlag
|
||||
assert ParamKeyType
|
||||
assert UnknownKeyName
|
||||
|
||||
|
||||
@@ -3,270 +3,118 @@
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "cereal/gen/cpp/log.capnp.h"
|
||||
|
||||
inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"AccessToken", {CLEAR_ON_MANAGER_START | DONT_LOG, STRING}},
|
||||
{"AdbEnabled", {PERSISTENT | BACKUP, BOOL}},
|
||||
{"AlwaysOnDM", {PERSISTENT | BACKUP, BOOL}},
|
||||
{"ApiCache_Device", {PERSISTENT, STRING}},
|
||||
{"ApiCache_FirehoseStats", {PERSISTENT, JSON}},
|
||||
{"AssistNowToken", {PERSISTENT, STRING}},
|
||||
{"AthenadPid", {PERSISTENT, INT}},
|
||||
{"AthenadUploadQueue", {PERSISTENT, JSON}},
|
||||
{"AthenadRecentlyViewedRoutes", {PERSISTENT, STRING}},
|
||||
{"BootCount", {PERSISTENT, INT}},
|
||||
{"CalibrationParams", {PERSISTENT, BYTES}},
|
||||
{"CameraDebugExpGain", {CLEAR_ON_MANAGER_START, STRING}},
|
||||
{"CameraDebugExpTime", {CLEAR_ON_MANAGER_START, STRING}},
|
||||
{"CarBatteryCapacity", {PERSISTENT, INT}},
|
||||
{"CarParams", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BYTES}},
|
||||
{"CarParamsCache", {CLEAR_ON_MANAGER_START, BYTES}},
|
||||
{"CarParamsPersistent", {PERSISTENT, BYTES}},
|
||||
{"CarParamsPrevRoute", {PERSISTENT, BYTES}},
|
||||
{"CompletedTrainingVersion", {PERSISTENT, STRING, "0"}},
|
||||
{"ControlsReady", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
|
||||
{"CurrentBootlog", {PERSISTENT, STRING}},
|
||||
{"CurrentRoute", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, STRING}},
|
||||
{"DisableLogging", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
|
||||
{"DisablePowerDown", {PERSISTENT | BACKUP, BOOL}},
|
||||
{"DisableUpdates", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"DisengageOnAccelerator", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"DongleId", {PERSISTENT, STRING}},
|
||||
{"DoReboot", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"DoShutdown", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"DoUninstall", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"DriverTooDistracted", {CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON, BOOL}},
|
||||
{"AlphaLongitudinalEnabled", {PERSISTENT | DEVELOPMENT_ONLY | BACKUP, BOOL}},
|
||||
{"ExperimentalMode", {PERSISTENT | BACKUP, BOOL}},
|
||||
{"ExperimentalModeConfirmed", {PERSISTENT | BACKUP, BOOL}},
|
||||
{"FirmwareQueryDone", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
|
||||
{"ForcePowerDown", {PERSISTENT, BOOL}},
|
||||
{"GitBranch", {PERSISTENT, STRING}},
|
||||
{"GitCommit", {PERSISTENT, STRING}},
|
||||
{"GitCommitDate", {PERSISTENT, STRING}},
|
||||
{"GitDiff", {PERSISTENT, STRING}},
|
||||
{"GithubSshKeys", {PERSISTENT | BACKUP, STRING}},
|
||||
{"GithubUsername", {PERSISTENT | BACKUP, STRING}},
|
||||
{"GitRemote", {PERSISTENT, STRING}},
|
||||
{"GsmApn", {PERSISTENT | BACKUP, STRING}},
|
||||
{"GsmMetered", {PERSISTENT | BACKUP, BOOL, "1"}},
|
||||
{"GsmRoaming", {PERSISTENT | BACKUP, BOOL}},
|
||||
{"HardwareSerial", {PERSISTENT, STRING}},
|
||||
{"HasAcceptedTerms", {PERSISTENT, STRING, "0"}},
|
||||
{"InstallDate", {PERSISTENT, TIME}},
|
||||
{"IsDriverViewEnabled", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"IsEngaged", {PERSISTENT, BOOL}},
|
||||
{"IsLdwEnabled", {PERSISTENT | BACKUP, BOOL}},
|
||||
{"IsMetric", {PERSISTENT | BACKUP, BOOL}},
|
||||
{"IsOffroad", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"IsOnroad", {PERSISTENT, BOOL}},
|
||||
{"IsRhdDetected", {PERSISTENT, BOOL}},
|
||||
{"IsReleaseBranch", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"IsTakingSnapshot", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"IsTestedBranch", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"JoystickDebugMode", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||
{"LanguageSetting", {PERSISTENT | BACKUP, STRING, "en"}},
|
||||
{"LastAthenaPingTime", {CLEAR_ON_MANAGER_START, INT}},
|
||||
{"LastGPSPosition", {PERSISTENT, STRING}},
|
||||
{"LastManagerExitReason", {CLEAR_ON_MANAGER_START, STRING}},
|
||||
{"LastOffroadStatusPacket", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, JSON}},
|
||||
{"LastAgnosPowerMonitorShutdown", {CLEAR_ON_MANAGER_START, STRING}},
|
||||
{"LastPowerDropDetected", {CLEAR_ON_MANAGER_START, STRING}},
|
||||
{"LastUpdateException", {CLEAR_ON_MANAGER_START, STRING}},
|
||||
{"LastUpdateRouteCount", {PERSISTENT, INT, "0"}},
|
||||
{"LastUpdateTime", {PERSISTENT, TIME}},
|
||||
{"LastUpdateUptimeOnroad", {PERSISTENT, FLOAT, "0.0"}},
|
||||
{"LiveDelay", {PERSISTENT | BACKUP, BYTES}},
|
||||
{"LiveParameters", {PERSISTENT, JSON}},
|
||||
{"LiveParametersV2", {PERSISTENT, BYTES}},
|
||||
{"LiveTorqueParameters", {PERSISTENT | DONT_LOG, BYTES}},
|
||||
{"LocationFilterInitialState", {PERSISTENT, BYTES}},
|
||||
{"LongitudinalManeuverMode", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||
{"LongitudinalPersonality", {PERSISTENT | BACKUP, INT, std::to_string(static_cast<int>(cereal::LongitudinalPersonality::STANDARD))}},
|
||||
{"NetworkMetered", {PERSISTENT | BACKUP, BOOL}},
|
||||
{"ObdMultiplexingChanged", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
|
||||
{"ObdMultiplexingEnabled", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
|
||||
{"Offroad_CarUnrecognized", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}},
|
||||
{"Offroad_ConnectivityNeeded", {CLEAR_ON_MANAGER_START, JSON}},
|
||||
{"Offroad_ConnectivityNeededPrompt", {CLEAR_ON_MANAGER_START, JSON}},
|
||||
{"Offroad_ExcessiveActuation", {PERSISTENT, JSON}},
|
||||
{"Offroad_IsTakingSnapshot", {CLEAR_ON_MANAGER_START, JSON}},
|
||||
{"Offroad_NeosUpdate", {CLEAR_ON_MANAGER_START, JSON}},
|
||||
{"Offroad_NoFirmware", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}},
|
||||
{"Offroad_Recalibration", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}},
|
||||
{"Offroad_TemperatureTooHigh", {CLEAR_ON_MANAGER_START, JSON}},
|
||||
{"Offroad_UnregisteredHardware", {CLEAR_ON_MANAGER_START, JSON}},
|
||||
{"Offroad_UpdateFailed", {CLEAR_ON_MANAGER_START, JSON}},
|
||||
{"Offroad_DriverMonitoringUncertain", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}},
|
||||
{"OnroadCycleRequested", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"OpenpilotEnabledToggle", {PERSISTENT | BACKUP, BOOL, "1"}},
|
||||
{"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}},
|
||||
{"RecordAudio", {PERSISTENT | BACKUP, BOOL}},
|
||||
{"RecordAudioFeedback", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"RecordFront", {PERSISTENT | BACKUP, BOOL}},
|
||||
{"RecordFrontLock", {PERSISTENT, BOOL}}, // for the internal fleet
|
||||
{"SecOCKey", {PERSISTENT | DONT_LOG | BACKUP, STRING}},
|
||||
{"ShowDebugInfo", {PERSISTENT, BOOL}},
|
||||
{"RouteCount", {PERSISTENT, INT, "0"}},
|
||||
{"SnoozeUpdate", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||
{"SshEnabled", {PERSISTENT | BACKUP, BOOL}},
|
||||
{"TermsVersion", {PERSISTENT, STRING}},
|
||||
{"TrainingVersion", {PERSISTENT, STRING}},
|
||||
{"UbloxAvailable", {PERSISTENT, BOOL}},
|
||||
{"UpdateAvailable", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
|
||||
{"UpdateFailedCount", {CLEAR_ON_MANAGER_START, INT}},
|
||||
{"UpdaterAvailableBranches", {PERSISTENT, STRING}},
|
||||
{"UpdaterCurrentDescription", {CLEAR_ON_MANAGER_START, STRING}},
|
||||
{"UpdaterCurrentReleaseNotes", {CLEAR_ON_MANAGER_START, BYTES}},
|
||||
{"UpdaterFetchAvailable", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"UpdaterNewDescription", {CLEAR_ON_MANAGER_START, STRING}},
|
||||
{"UpdaterNewReleaseNotes", {CLEAR_ON_MANAGER_START, BYTES}},
|
||||
{"UpdaterState", {CLEAR_ON_MANAGER_START, STRING}},
|
||||
{"UpdaterTargetBranch", {CLEAR_ON_MANAGER_START, STRING}},
|
||||
{"UpdaterLastFetchTime", {PERSISTENT, TIME}},
|
||||
{"UptimeOffroad", {PERSISTENT, FLOAT, "0.0"}},
|
||||
{"UptimeOnroad", {PERSISTENT, FLOAT, "0.0"}},
|
||||
{"Version", {PERSISTENT, STRING}},
|
||||
|
||||
// --- sunnypilot params --- //
|
||||
{"ApiCache_DriveStats", {PERSISTENT, JSON}},
|
||||
{"AutoLaneChangeBsmDelay", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"AutoLaneChangeTimer", {PERSISTENT | BACKUP, INT, "0"}},
|
||||
{"BlinkerMinLateralControlSpeed", {PERSISTENT | BACKUP, INT, "20"}}, // MPH or km/h
|
||||
{"BlinkerPauseLateralControl", {PERSISTENT | BACKUP, INT, "0"}},
|
||||
{"Brightness", {PERSISTENT | BACKUP, INT, "0"}},
|
||||
{"CarList", {PERSISTENT, JSON}},
|
||||
{"CarParamsSP", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BYTES}},
|
||||
{"CarParamsSPCache", {CLEAR_ON_MANAGER_START, BYTES}},
|
||||
{"CarParamsSPPersistent", {PERSISTENT, BYTES}},
|
||||
{"CarPlatformBundle", {PERSISTENT | BACKUP, JSON}},
|
||||
{"ChevronInfo", {PERSISTENT | BACKUP, INT, "4"}},
|
||||
{"CompletedSunnylinkConsentVersion", {PERSISTENT, STRING, "0"}},
|
||||
{"CustomAccIncrementsEnabled", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"CustomAccLongPressIncrement", {PERSISTENT | BACKUP, INT, "5"}},
|
||||
{"CustomAccShortPressIncrement", {PERSISTENT | BACKUP, INT, "1"}},
|
||||
{"DeviceBootMode", {PERSISTENT | BACKUP, INT, "0"}},
|
||||
{"DevUIInfo", {PERSISTENT | BACKUP, INT, "0"}},
|
||||
{"EnableCopyparty", {PERSISTENT | BACKUP, BOOL}},
|
||||
{"EnableGithubRunner", {PERSISTENT | BACKUP, BOOL}},
|
||||
{"GreenLightAlert", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"GithubRunnerSufficientVoltage", {CLEAR_ON_MANAGER_START , BOOL}},
|
||||
{"HasAcceptedTermsSP", {PERSISTENT, STRING, "0"}},
|
||||
{"HideVEgoUI", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"IntelligentCruiseButtonManagement", {PERSISTENT | BACKUP , BOOL}},
|
||||
{"InteractivityTimeout", {PERSISTENT | BACKUP, INT, "0"}},
|
||||
{"IsDevelopmentBranch", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"IsReleaseSpBranch", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"LastGPSPositionLLK", {PERSISTENT, STRING}},
|
||||
{"LeadDepartAlert", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"MaxTimeOffroad", {PERSISTENT | BACKUP, INT, "1800"}},
|
||||
{"ModelRunnerTypeCache", {CLEAR_ON_ONROAD_TRANSITION, INT}},
|
||||
{"OffroadMode", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"Offroad_TiciSupport", {CLEAR_ON_MANAGER_START, JSON}},
|
||||
{"OnroadScreenOffBrightness", {PERSISTENT | BACKUP, INT, "0"}},
|
||||
{"OnroadScreenOffControl", {PERSISTENT | BACKUP, BOOL}},
|
||||
{"OnroadScreenOffTimer", {PERSISTENT | BACKUP, INT, "15"}},
|
||||
{"OnroadUploads", {PERSISTENT | BACKUP, BOOL, "1"}},
|
||||
{"QuickBootToggle", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"QuietMode", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"RainbowMode", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"ShowAdvancedControls", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"ShowTurnSignals", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"StandstillTimer", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"TrueVEgoUI", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
|
||||
// MADS params
|
||||
{"Mads", {PERSISTENT | BACKUP, BOOL, "1"}},
|
||||
{"MadsMainCruiseAllowed", {PERSISTENT | BACKUP, BOOL, "1"}},
|
||||
{"MadsSteeringMode", {PERSISTENT | BACKUP, INT, "0"}},
|
||||
{"MadsUnifiedEngagementMode", {PERSISTENT | BACKUP, BOOL, "1"}},
|
||||
|
||||
// Model Manager params
|
||||
{"ModelManager_ActiveBundle", {PERSISTENT, JSON}},
|
||||
{"ModelManager_ClearCache", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"ModelManager_DownloadIndex", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, INT, "0"}},
|
||||
{"ModelManager_Favs", {PERSISTENT | BACKUP, STRING}},
|
||||
{"ModelManager_LastSyncTime", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, INT, "0"}},
|
||||
{"ModelManager_ModelsCache", {PERSISTENT | BACKUP, JSON}},
|
||||
|
||||
// Neural Network Lateral Control
|
||||
{"NeuralNetworkLateralControl", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
|
||||
// sunnylink params
|
||||
{"EnableSunnylinkUploader", {PERSISTENT | BACKUP, BOOL}},
|
||||
{"LastSunnylinkPingTime", {CLEAR_ON_MANAGER_START, INT}},
|
||||
{"SunnylinkCache_Roles", {PERSISTENT, STRING}},
|
||||
{"SunnylinkCache_Users", {PERSISTENT, STRING}},
|
||||
{"SunnylinkDongleId", {PERSISTENT, STRING}},
|
||||
{"SunnylinkdPid", {PERSISTENT, INT}},
|
||||
{"SunnylinkEnabled", {PERSISTENT, BOOL, "1"}},
|
||||
{"SunnylinkTempFault", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL, "0"}},
|
||||
|
||||
// Backup Manager params
|
||||
{"BackupManager_CreateBackup", {PERSISTENT, BOOL}},
|
||||
{"BackupManager_RestoreVersion", {PERSISTENT, STRING}},
|
||||
|
||||
// sunnypilot car specific params
|
||||
{"HyundaiLongitudinalTuning", {PERSISTENT | BACKUP, INT, "0"}},
|
||||
{"SubaruStopAndGo", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"SubaruStopAndGoManualParkingBrake", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"TeslaCoopSteering", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"ToyotaEnforceStockLongitudinal", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
|
||||
{"DynamicExperimentalControl", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"BlindSpot", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
|
||||
// sunnypilot model params
|
||||
{"CameraOffset", {PERSISTENT | BACKUP, FLOAT, "0.0"}},
|
||||
{"LagdToggle", {PERSISTENT | BACKUP, BOOL, "1"}},
|
||||
{"LagdToggleDelay", {PERSISTENT | BACKUP, FLOAT, "0.2"}},
|
||||
{"LagdValueCache", {PERSISTENT, FLOAT, "0.2"}},
|
||||
{"LaneTurnDesire", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"LaneTurnValue", {PERSISTENT | BACKUP, FLOAT, "19.0"}},
|
||||
{"PlanplusControl", {PERSISTENT | BACKUP, FLOAT, "1.0"}},
|
||||
|
||||
// mapd
|
||||
{"MapAdvisorySpeedLimit", {CLEAR_ON_ONROAD_TRANSITION, FLOAT}},
|
||||
{"MapdVersion", {PERSISTENT, STRING}},
|
||||
{"MapSpeedLimit", {CLEAR_ON_ONROAD_TRANSITION, FLOAT, "0.0"}},
|
||||
{"NextMapSpeedLimit", {CLEAR_ON_ONROAD_TRANSITION, JSON}},
|
||||
{"Offroad_OSMUpdateRequired", {CLEAR_ON_MANAGER_START, JSON}},
|
||||
{"OsmDbUpdatesCheck", {CLEAR_ON_MANAGER_START, BOOL}}, // mapd database update happens with device ON, reset on boot
|
||||
{"OSMDownloadBounds", {PERSISTENT, STRING}},
|
||||
{"OsmDownloadedDate", {PERSISTENT, STRING, "0.0"}},
|
||||
{"OSMDownloadLocations", {PERSISTENT, JSON}},
|
||||
{"OSMDownloadProgress", {CLEAR_ON_MANAGER_START, JSON}},
|
||||
{"OsmLocal", {PERSISTENT, BOOL}},
|
||||
{"OsmLocationName", {PERSISTENT, STRING}},
|
||||
{"OsmLocationTitle", {PERSISTENT, STRING}},
|
||||
{"OsmLocationUrl", {PERSISTENT, STRING}},
|
||||
{"OsmStateName", {PERSISTENT, STRING, "All"}},
|
||||
{"OsmStateTitle", {PERSISTENT, STRING}},
|
||||
{"OsmWayTest", {PERSISTENT, STRING}},
|
||||
{"RoadName", {CLEAR_ON_ONROAD_TRANSITION, STRING}},
|
||||
{"RoadNameToggle", {PERSISTENT, STRING}},
|
||||
|
||||
// Speed Limit
|
||||
{"SpeedLimitMode", {PERSISTENT | BACKUP, INT, "1"}},
|
||||
{"SpeedLimitOffsetType", {PERSISTENT | BACKUP, INT, "0"}},
|
||||
{"SpeedLimitPolicy", {PERSISTENT | BACKUP, INT, "3"}},
|
||||
{"SpeedLimitValueOffset", {PERSISTENT | BACKUP, INT, "0"}},
|
||||
|
||||
// Smart Cruise Control
|
||||
{"MapTargetVelocities", {CLEAR_ON_ONROAD_TRANSITION, STRING}},
|
||||
{"SmartCruiseControlMap", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"SmartCruiseControlVision", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
|
||||
// Torque lateral control custom params
|
||||
{"CustomTorqueParams", {PERSISTENT | BACKUP , BOOL}},
|
||||
{"EnforceTorqueControl", {PERSISTENT | BACKUP, BOOL}},
|
||||
{"LiveTorqueParamsToggle", {PERSISTENT | BACKUP , BOOL}},
|
||||
{"LiveTorqueParamsRelaxedToggle", {PERSISTENT | BACKUP , BOOL}},
|
||||
{"TorqueParamsOverrideEnabled", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"TorqueParamsOverrideFriction", {PERSISTENT | BACKUP, FLOAT, "0.1"}},
|
||||
{"TorqueParamsOverrideLatAccelFactor", {PERSISTENT | BACKUP, FLOAT, "2.5"}},
|
||||
inline static std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"AccessToken", CLEAR_ON_MANAGER_START | DONT_LOG},
|
||||
{"AdbEnabled", PERSISTENT},
|
||||
{"AlwaysOnDM", PERSISTENT},
|
||||
{"ApiCache_Device", PERSISTENT},
|
||||
{"ApiCache_FirehoseStats", PERSISTENT},
|
||||
{"AssistNowToken", PERSISTENT},
|
||||
{"AthenadPid", PERSISTENT},
|
||||
{"AthenadUploadQueue", PERSISTENT},
|
||||
{"AthenadRecentlyViewedRoutes", PERSISTENT},
|
||||
{"BootCount", PERSISTENT},
|
||||
{"CalibrationParams", PERSISTENT},
|
||||
{"CameraDebugExpGain", CLEAR_ON_MANAGER_START},
|
||||
{"CameraDebugExpTime", CLEAR_ON_MANAGER_START},
|
||||
{"CarBatteryCapacity", PERSISTENT},
|
||||
{"CarParams", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"CarParamsCache", CLEAR_ON_MANAGER_START},
|
||||
{"CarParamsPersistent", PERSISTENT},
|
||||
{"CarParamsPrevRoute", PERSISTENT},
|
||||
{"CompletedTrainingVersion", PERSISTENT},
|
||||
{"ControlsReady", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"CurrentBootlog", PERSISTENT},
|
||||
{"CurrentRoute", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"DisableLogging", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"DisablePowerDown", PERSISTENT},
|
||||
{"DisableUpdates", PERSISTENT},
|
||||
{"DisengageOnAccelerator", PERSISTENT},
|
||||
{"DongleId", PERSISTENT},
|
||||
{"DoReboot", CLEAR_ON_MANAGER_START},
|
||||
{"DoShutdown", CLEAR_ON_MANAGER_START},
|
||||
{"DoUninstall", CLEAR_ON_MANAGER_START},
|
||||
{"ExperimentalLongitudinalEnabled", PERSISTENT | DEVELOPMENT_ONLY},
|
||||
{"ExperimentalMode", PERSISTENT},
|
||||
{"ExperimentalModeConfirmed", PERSISTENT},
|
||||
{"FirmwareQueryDone", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"ForcePowerDown", PERSISTENT},
|
||||
{"GitBranch", PERSISTENT},
|
||||
{"GitCommit", PERSISTENT},
|
||||
{"GitCommitDate", PERSISTENT},
|
||||
{"GitDiff", PERSISTENT},
|
||||
{"GithubSshKeys", PERSISTENT},
|
||||
{"GithubUsername", PERSISTENT},
|
||||
{"GitRemote", PERSISTENT},
|
||||
{"GsmApn", PERSISTENT},
|
||||
{"GsmMetered", PERSISTENT},
|
||||
{"GsmRoaming", PERSISTENT},
|
||||
{"HardwareSerial", PERSISTENT},
|
||||
{"HasAcceptedTerms", PERSISTENT},
|
||||
{"InstallDate", PERSISTENT},
|
||||
{"IsDriverViewEnabled", CLEAR_ON_MANAGER_START},
|
||||
{"IsEngaged", PERSISTENT},
|
||||
{"IsLdwEnabled", PERSISTENT},
|
||||
{"IsMetric", PERSISTENT},
|
||||
{"IsOffroad", CLEAR_ON_MANAGER_START},
|
||||
{"IsOnroad", PERSISTENT},
|
||||
{"IsRhdDetected", PERSISTENT},
|
||||
{"IsReleaseBranch", CLEAR_ON_MANAGER_START},
|
||||
{"IsTakingSnapshot", CLEAR_ON_MANAGER_START},
|
||||
{"IsTestedBranch", CLEAR_ON_MANAGER_START},
|
||||
{"JoystickDebugMode", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
||||
{"LanguageSetting", PERSISTENT},
|
||||
{"LastAthenaPingTime", CLEAR_ON_MANAGER_START},
|
||||
{"LastGPSPosition", PERSISTENT},
|
||||
{"LastManagerExitReason", CLEAR_ON_MANAGER_START},
|
||||
{"LastOffroadStatusPacket", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
||||
{"LastPowerDropDetected", CLEAR_ON_MANAGER_START},
|
||||
{"LastUpdateException", CLEAR_ON_MANAGER_START},
|
||||
{"LastUpdateTime", PERSISTENT},
|
||||
{"LiveParameters", PERSISTENT},
|
||||
{"LiveTorqueParameters", PERSISTENT | DONT_LOG},
|
||||
{"LocationFilterInitialState", PERSISTENT},
|
||||
{"LongitudinalManeuverMode", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
||||
{"LongitudinalPersonality", PERSISTENT},
|
||||
{"NetworkMetered", PERSISTENT},
|
||||
{"ObdMultiplexingChanged", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"ObdMultiplexingEnabled", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"Offroad_BadNvme", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_CarUnrecognized", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"Offroad_ConnectivityNeeded", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_ConnectivityNeededPrompt", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_IsTakingSnapshot", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_NeosUpdate", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_NoFirmware", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"Offroad_Recalibration", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"Offroad_StorageMissing", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_TemperatureTooHigh", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_UnofficialHardware", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_UpdateFailed", CLEAR_ON_MANAGER_START},
|
||||
{"OpenpilotEnabledToggle", PERSISTENT},
|
||||
{"PandaHeartbeatLost", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
||||
{"PandaSomResetTriggered", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
||||
{"PandaSignatures", CLEAR_ON_MANAGER_START},
|
||||
{"PrimeType", PERSISTENT},
|
||||
{"RecordFront", PERSISTENT},
|
||||
{"RecordFrontLock", PERSISTENT}, // for the internal fleet
|
||||
{"SecOCKey", PERSISTENT | DONT_LOG},
|
||||
{"RouteCount", PERSISTENT},
|
||||
{"SnoozeUpdate", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
||||
{"SshEnabled", PERSISTENT},
|
||||
{"TermsVersion", PERSISTENT},
|
||||
{"TrainingVersion", PERSISTENT},
|
||||
{"UbloxAvailable", PERSISTENT},
|
||||
{"UpdateAvailable", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"UpdateFailedCount", CLEAR_ON_MANAGER_START},
|
||||
{"UpdaterAvailableBranches", PERSISTENT},
|
||||
{"UpdaterCurrentDescription", CLEAR_ON_MANAGER_START},
|
||||
{"UpdaterCurrentReleaseNotes", CLEAR_ON_MANAGER_START},
|
||||
{"UpdaterFetchAvailable", CLEAR_ON_MANAGER_START},
|
||||
{"UpdaterNewDescription", CLEAR_ON_MANAGER_START},
|
||||
{"UpdaterNewReleaseNotes", CLEAR_ON_MANAGER_START},
|
||||
{"UpdaterState", CLEAR_ON_MANAGER_START},
|
||||
{"UpdaterTargetBranch", CLEAR_ON_MANAGER_START},
|
||||
{"UpdaterLastFetchTime", PERSISTENT},
|
||||
{"Version", PERSISTENT},
|
||||
};
|
||||
|
||||
@@ -1,35 +1,18 @@
|
||||
# distutils: language = c++
|
||||
# cython: language_level = 3
|
||||
import builtins
|
||||
import datetime
|
||||
import json
|
||||
from libcpp cimport bool
|
||||
from libcpp.string cimport string
|
||||
from libcpp.vector cimport vector
|
||||
from libcpp.optional cimport optional
|
||||
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
|
||||
cdef extern from "common/params.h":
|
||||
cpdef enum ParamKeyFlag:
|
||||
cpdef enum ParamKeyType:
|
||||
PERSISTENT
|
||||
CLEAR_ON_MANAGER_START
|
||||
CLEAR_ON_ONROAD_TRANSITION
|
||||
CLEAR_ON_OFFROAD_TRANSITION
|
||||
DEVELOPMENT_ONLY
|
||||
CLEAR_ON_IGNITION_ON
|
||||
BACKUP
|
||||
ALL
|
||||
|
||||
cpdef enum ParamKeyType:
|
||||
STRING
|
||||
BOOL
|
||||
INT
|
||||
FLOAT
|
||||
TIME
|
||||
JSON
|
||||
BYTES
|
||||
|
||||
cdef cppclass c_Params "Params":
|
||||
c_Params(string) except + nogil
|
||||
string get(string, bool) nogil
|
||||
@@ -40,31 +23,10 @@ cdef extern from "common/params.h":
|
||||
void putBoolNonBlocking(string, bool) nogil
|
||||
int putBool(string, bool) nogil
|
||||
bool checkKey(string) nogil
|
||||
ParamKeyType getKeyType(string) nogil
|
||||
optional[string] getKeyDefaultValue(string) nogil
|
||||
string getParamPath(string) nogil
|
||||
void clearAll(ParamKeyFlag)
|
||||
vector[string] allKeys(ParamKeyFlag)
|
||||
void clearAll(ParamKeyType)
|
||||
vector[string] allKeys()
|
||||
|
||||
PYTHON_2_CPP = {
|
||||
(str, STRING): lambda v: v,
|
||||
(builtins.bool, BOOL): lambda v: "1" if v else "0",
|
||||
(int, INT): str,
|
||||
(float, FLOAT): str,
|
||||
(datetime.datetime, TIME): lambda v: v.isoformat(),
|
||||
(dict, JSON): json.dumps,
|
||||
(list, JSON): json.dumps,
|
||||
(bytes, BYTES): lambda v: v,
|
||||
}
|
||||
CPP_2_PYTHON = {
|
||||
STRING: lambda v: v.decode("utf-8"),
|
||||
BOOL: lambda v: v == b"1",
|
||||
INT: int,
|
||||
FLOAT: float,
|
||||
TIME: lambda v: datetime.datetime.fromisoformat(v.decode("utf-8")),
|
||||
JSON: json.loads,
|
||||
BYTES: lambda v: v,
|
||||
}
|
||||
|
||||
def ensure_bytes(v):
|
||||
return v.encode() if isinstance(v, str) else v
|
||||
@@ -88,8 +50,8 @@ cdef class Params:
|
||||
def __dealloc__(self):
|
||||
del self.p
|
||||
|
||||
def clear_all(self, tx_flag=ParamKeyFlag.ALL):
|
||||
self.p.clearAll(tx_flag)
|
||||
def clear_all(self, tx_type=ParamKeyType.ALL):
|
||||
self.p.clearAll(tx_type)
|
||||
|
||||
def check_key(self, key):
|
||||
key = ensure_bytes(key)
|
||||
@@ -97,38 +59,21 @@ cdef class Params:
|
||||
raise UnknownKeyName(key)
|
||||
return key
|
||||
|
||||
def python2cpp(self, proposed_type, expected_type, value, key):
|
||||
cast = PYTHON_2_CPP.get((proposed_type, expected_type))
|
||||
if cast:
|
||||
return cast(value)
|
||||
raise TypeError(f"Type mismatch while writing param {key}: {proposed_type=} {expected_type=} {value=}")
|
||||
|
||||
def _cpp2python(self, t, value, default, key):
|
||||
if value is None:
|
||||
return None
|
||||
try:
|
||||
return CPP_2_PYTHON[t](value)
|
||||
except (KeyError, TypeError, ValueError):
|
||||
cloudlog.warning(f"Failed to cast param {key} with {value=} from type {t=}")
|
||||
return self._cpp2python(t, default, None, key)
|
||||
|
||||
def get(self, key, bool block=False, bool return_default=False):
|
||||
def get(self, key, bool block=False, encoding=None):
|
||||
cdef string k = self.check_key(key)
|
||||
cdef ParamKeyType t = self.p.getKeyType(k)
|
||||
cdef optional[string] default = self.p.getKeyDefaultValue(k)
|
||||
cdef string val
|
||||
with nogil:
|
||||
val = self.p.get(k, block)
|
||||
|
||||
default_val = (default.value() if default.has_value() else None) if return_default else None
|
||||
if val == b"":
|
||||
if block:
|
||||
# If we got no value while running in blocked mode
|
||||
# it means we got an interrupt while waiting
|
||||
raise KeyboardInterrupt
|
||||
else:
|
||||
return self._cpp2python(t, default_val, None, key)
|
||||
return self._cpp2python(t, val, default_val, key)
|
||||
return None
|
||||
|
||||
return val if encoding is None else val.decode(encoding)
|
||||
|
||||
def get_bool(self, key, bool block=False):
|
||||
cdef string k = self.check_key(key)
|
||||
@@ -137,11 +82,6 @@ cdef class Params:
|
||||
r = self.p.getBool(k, block)
|
||||
return r
|
||||
|
||||
def _put_cast(self, key, dat):
|
||||
cdef string k = self.check_key(key)
|
||||
cdef ParamKeyType t = self.p.getKeyType(k)
|
||||
return ensure_bytes(self.python2cpp(type(dat), t, dat, key))
|
||||
|
||||
def put(self, key, dat):
|
||||
"""
|
||||
Warning: This function blocks until the param is written to disk!
|
||||
@@ -150,7 +90,7 @@ cdef class Params:
|
||||
in general try to avoid writing params as much as possible.
|
||||
"""
|
||||
cdef string k = self.check_key(key)
|
||||
cdef string dat_bytes = self._put_cast(key, dat)
|
||||
cdef string dat_bytes = ensure_bytes(dat)
|
||||
with nogil:
|
||||
self.p.put(k, dat_bytes)
|
||||
|
||||
@@ -161,7 +101,7 @@ cdef class Params:
|
||||
|
||||
def put_nonblocking(self, key, dat):
|
||||
cdef string k = self.check_key(key)
|
||||
cdef string dat_bytes = self._put_cast(key, dat)
|
||||
cdef string dat_bytes = ensure_bytes(dat)
|
||||
with nogil:
|
||||
self.p.putNonBlocking(k, dat_bytes)
|
||||
|
||||
@@ -179,19 +119,5 @@ cdef class Params:
|
||||
cdef string key_bytes = ensure_bytes(key)
|
||||
return self.p.getParamPath(key_bytes).decode("utf-8")
|
||||
|
||||
def get_type(self, key):
|
||||
return self.p.getKeyType(self.check_key(key))
|
||||
|
||||
def all_keys(self, flag=ParamKeyFlag.ALL):
|
||||
return self.p.allKeys(flag)
|
||||
|
||||
def get_default_value(self, key):
|
||||
cdef string k = self.check_key(key)
|
||||
cdef ParamKeyType t = self.p.getKeyType(k)
|
||||
cdef optional[string] default = self.p.getKeyDefaultValue(k)
|
||||
return self._cpp2python(t, default.value(), None, key) if default.has_value() else None
|
||||
|
||||
def cpp2python(self, key, value):
|
||||
cdef string k = self.check_key(key)
|
||||
cdef ParamKeyType t = self.p.getKeyType(k)
|
||||
return self._cpp2python(t, value, None, key)
|
||||
def all_keys(self):
|
||||
return self.p.allKeys()
|
||||
|
||||
@@ -2,10 +2,11 @@ import numpy as np
|
||||
from numbers import Number
|
||||
|
||||
class PIDController:
|
||||
def __init__(self, k_p, k_i, k_d=0., pos_limit=1e308, neg_limit=-1e308, rate=100):
|
||||
def __init__(self, k_p, k_i, k_f=0., k_d=0., pos_limit=1e308, neg_limit=-1e308, rate=100):
|
||||
self._k_p = k_p
|
||||
self._k_i = k_i
|
||||
self._k_d = k_d
|
||||
self.k_f = k_f # feedforward gain
|
||||
if isinstance(self._k_p, Number):
|
||||
self._k_p = [[0], [self._k_p]]
|
||||
if isinstance(self._k_i, Number):
|
||||
@@ -13,9 +14,11 @@ class PIDController:
|
||||
if isinstance(self._k_d, Number):
|
||||
self._k_d = [[0], [self._k_d]]
|
||||
|
||||
self.set_limits(pos_limit, neg_limit)
|
||||
self.pos_limit = pos_limit
|
||||
self.neg_limit = neg_limit
|
||||
|
||||
self.i_dt = 1.0 / rate
|
||||
self.i_unwind_rate = 0.3 / rate
|
||||
self.i_rate = 1.0 / rate
|
||||
self.speed = 0.0
|
||||
|
||||
self.reset()
|
||||
@@ -32,6 +35,10 @@ class PIDController:
|
||||
def k_d(self):
|
||||
return np.interp(self.speed, self._k_d[0], self._k_d[1])
|
||||
|
||||
@property
|
||||
def error_integral(self):
|
||||
return self.i/self.k_i
|
||||
|
||||
def reset(self):
|
||||
self.p = 0.0
|
||||
self.i = 0.0
|
||||
@@ -39,25 +46,25 @@ class PIDController:
|
||||
self.f = 0.0
|
||||
self.control = 0
|
||||
|
||||
def set_limits(self, pos_limit, neg_limit):
|
||||
self.pos_limit = pos_limit
|
||||
self.neg_limit = neg_limit
|
||||
|
||||
def update(self, error, error_rate=0.0, speed=0.0, feedforward=0., freeze_integrator=False):
|
||||
def update(self, error, error_rate=0.0, speed=0.0, override=False, feedforward=0., freeze_integrator=False):
|
||||
self.speed = speed
|
||||
self.p = self.k_p * float(error)
|
||||
self.d = self.k_d * error_rate
|
||||
self.f = feedforward
|
||||
|
||||
if not freeze_integrator:
|
||||
i = self.i + self.k_i * self.i_dt * error
|
||||
self.p = float(error) * self.k_p
|
||||
self.f = feedforward * self.k_f
|
||||
self.d = error_rate * self.k_d
|
||||
|
||||
# Don't allow windup if already clipping
|
||||
test_control = self.p + i + self.d + self.f
|
||||
i_upperbound = self.i if test_control > self.pos_limit else self.pos_limit
|
||||
i_lowerbound = self.i if test_control < self.neg_limit else self.neg_limit
|
||||
self.i = np.clip(i, i_lowerbound, i_upperbound)
|
||||
if override:
|
||||
self.i -= self.i_unwind_rate * float(np.sign(self.i))
|
||||
else:
|
||||
if not freeze_integrator:
|
||||
self.i = self.i + error * self.k_i * self.i_rate
|
||||
|
||||
# Clip i to prevent exceeding control limits
|
||||
control_no_i = self.p + self.d + self.f
|
||||
control_no_i = np.clip(control_no_i, self.neg_limit, self.pos_limit)
|
||||
self.i = np.clip(self.i, self.neg_limit - control_no_i, self.pos_limit - control_no_i)
|
||||
|
||||
control = self.p + self.i + self.d + self.f
|
||||
|
||||
self.control = np.clip(control, self.neg_limit, self.pos_limit)
|
||||
return self.control
|
||||
|
||||
@@ -9,19 +9,20 @@ from openpilot.system.hardware.hw import Paths
|
||||
from openpilot.system.hardware.hw import DEFAULT_DOWNLOAD_CACHE_ROOT
|
||||
|
||||
class OpenpilotPrefix:
|
||||
def __init__(self, prefix: str = None, create_dirs_on_enter: bool = True, clean_dirs_on_exit: bool = True, shared_download_cache: bool = False):
|
||||
def __init__(self, prefix: str = None, clean_dirs_on_exit: bool = True, shared_download_cache: bool = False):
|
||||
self.prefix = prefix if prefix else str(uuid.uuid4().hex[0:15])
|
||||
self.msgq_path = os.path.join(Paths.shm_path(), "msgq_" + self.prefix)
|
||||
self.create_dirs_on_enter = create_dirs_on_enter
|
||||
self.msgq_path = os.path.join(Paths.shm_path(), self.prefix)
|
||||
self.clean_dirs_on_exit = clean_dirs_on_exit
|
||||
self.shared_download_cache = shared_download_cache
|
||||
|
||||
def __enter__(self):
|
||||
self.original_prefix = os.environ.get('OPENPILOT_PREFIX', None)
|
||||
os.environ['OPENPILOT_PREFIX'] = self.prefix
|
||||
|
||||
if self.create_dirs_on_enter:
|
||||
self.create_dirs()
|
||||
try:
|
||||
os.mkdir(self.msgq_path)
|
||||
except FileExistsError:
|
||||
pass
|
||||
os.makedirs(Paths.log_root(), exist_ok=True)
|
||||
|
||||
if self.shared_download_cache:
|
||||
os.environ["COMMA_CACHE"] = DEFAULT_DOWNLOAD_CACHE_ROOT
|
||||
@@ -39,13 +40,6 @@ class OpenpilotPrefix:
|
||||
pass
|
||||
return False
|
||||
|
||||
def create_dirs(self):
|
||||
try:
|
||||
os.mkdir(self.msgq_path)
|
||||
except FileExistsError:
|
||||
pass
|
||||
os.makedirs(Paths.log_root(), exist_ok=True)
|
||||
|
||||
def clean_dirs(self):
|
||||
symlink_path = Params().get_param_path()
|
||||
if os.path.exists(symlink_path):
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"""Utilities for reading real time clocks and keeping soft real time constraints."""
|
||||
import gc
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
from setproctitle import getproctitle
|
||||
@@ -29,13 +28,13 @@ class Priority:
|
||||
|
||||
|
||||
def set_core_affinity(cores: list[int]) -> None:
|
||||
if sys.platform == 'linux' and not PC:
|
||||
if not PC:
|
||||
os.sched_setaffinity(0, cores)
|
||||
|
||||
|
||||
def config_realtime_process(cores: int | list[int], priority: int) -> None:
|
||||
gc.disable()
|
||||
if sys.platform == 'linux' and not PC:
|
||||
if not PC:
|
||||
os.sched_setscheduler(0, os.SCHED_FIFO, os.sched_param(priority))
|
||||
c = cores if isinstance(cores, list) else [cores, ]
|
||||
set_core_affinity(c)
|
||||
|
||||
30
common/retry.py
Normal file
30
common/retry.py
Normal file
@@ -0,0 +1,30 @@
|
||||
import time
|
||||
import functools
|
||||
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
|
||||
|
||||
def retry(attempts=3, delay=1.0, ignore_failure=False):
|
||||
def decorator(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
for _ in range(attempts):
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except Exception:
|
||||
cloudlog.exception(f"{func.__name__} failed, trying again")
|
||||
time.sleep(delay)
|
||||
|
||||
if ignore_failure:
|
||||
cloudlog.error(f"{func.__name__} failed after retry")
|
||||
else:
|
||||
raise Exception(f"{func.__name__} failed after retry")
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@retry(attempts=10)
|
||||
def abc():
|
||||
raise ValueError("abc failed :(")
|
||||
abc()
|
||||
13
common/run.py
Normal file
13
common/run.py
Normal file
@@ -0,0 +1,13 @@
|
||||
import subprocess
|
||||
|
||||
|
||||
def run_cmd(cmd: list[str], cwd=None, env=None) -> str:
|
||||
return subprocess.check_output(cmd, encoding='utf8', cwd=cwd, env=env).strip()
|
||||
|
||||
|
||||
def run_cmd_default(cmd: list[str], default: str = "", cwd=None, env=None) -> str:
|
||||
try:
|
||||
return run_cmd(cmd, cwd=cwd, env=env)
|
||||
except subprocess.CalledProcessError:
|
||||
return default
|
||||
|
||||
4
common/spinner.py
Executable file → Normal file
4
common/spinner.py
Executable file → Normal file
@@ -6,9 +6,9 @@ from openpilot.common.basedir import BASEDIR
|
||||
class Spinner:
|
||||
def __init__(self):
|
||||
try:
|
||||
self.spinner_proc = subprocess.Popen(["./spinner.py"],
|
||||
self.spinner_proc = subprocess.Popen(["./spinner"],
|
||||
stdin=subprocess.PIPE,
|
||||
cwd=os.path.join(BASEDIR, "system", "ui"),
|
||||
cwd=os.path.join(BASEDIR, "selfdrive", "ui"),
|
||||
close_fds=True)
|
||||
except OSError:
|
||||
self.spinner_proc = None
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
#include "common/version.h"
|
||||
#include "system/hardware/hw.h"
|
||||
|
||||
#include "sunnypilot/common/version.h"
|
||||
|
||||
class SwaglogState {
|
||||
public:
|
||||
SwaglogState() {
|
||||
@@ -58,7 +56,7 @@ public:
|
||||
if (char* daemon_name = getenv("MANAGER_DAEMON")) {
|
||||
ctx_j["daemon"] = daemon_name;
|
||||
}
|
||||
ctx_j["version"] = SUNNYPILOT_VERSION;
|
||||
ctx_j["version"] = COMMA_VERSION;
|
||||
ctx_j["dirty"] = !getenv("CLEAN");
|
||||
ctx_j["device"] = Hardware::get_name();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import os
|
||||
from uuid import uuid4
|
||||
|
||||
from openpilot.common.utils import atomic_write
|
||||
from openpilot.common.file_helpers import atomic_write_in_dir
|
||||
|
||||
|
||||
class TestFileHelpers:
|
||||
@@ -15,5 +15,5 @@ class TestFileHelpers:
|
||||
assert f.read() == "test"
|
||||
os.remove(path)
|
||||
|
||||
def test_atomic_write(self):
|
||||
self.run_atomic_write_func(atomic_write)
|
||||
def test_atomic_write_in_dir(self):
|
||||
self.run_atomic_write_func(atomic_write_in_dir)
|
||||
|
||||
@@ -6,7 +6,7 @@ from openpilot.common.markdown import parse_markdown
|
||||
|
||||
class TestMarkdown:
|
||||
def test_all_release_notes(self):
|
||||
with open(os.path.join(BASEDIR, "CHANGELOG.md")) as f:
|
||||
with open(os.path.join(BASEDIR, "RELEASES.md")) as f:
|
||||
release_notes = f.read().split("\n\n")
|
||||
assert len(release_notes) > 10
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user