mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-06-20 01:02:07 +08:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9ef7b6f739 | |||
| c16ed567a2 | |||
| 06aedc3e60 |
@@ -0,0 +1 @@
|
|||||||
|
{"cache_date": "2024-08-08", "page_authors": {"docs/index.md": {"last_commit_date": "2024-07-28", "authors": [{"login": "adeebshihadeh", "name": "adeebshihadeh", "url": "https://github.com/adeebshihadeh", "avatar": "https://avatars.githubusercontent.com/u/8762862?v=4"}, {"login": "tecandrew", "name": "tecandrew", "url": "https://github.com/tecandrew", "avatar": "https://avatars.githubusercontent.com/u/21319730?v=4"}, {"login": "pd0wm", "name": "pd0wm", "url": "https://github.com/pd0wm", "avatar": "https://avatars.githubusercontent.com/u/1314752?v=4"}]}, "docs/SAFETY.md": {"last_commit_date": "2023-07-25", "authors": [{"login": "haraschax", "name": "haraschax", "url": "https://github.com/haraschax", "avatar": "https://avatars.githubusercontent.com/u/6804392?v=4"}, {"login": "sshane", "name": "sshane", "url": "https://github.com/sshane", "avatar": "https://avatars.githubusercontent.com/u/25857203?v=4"}, {"login": "pd0wm", "name": "pd0wm", "url": "https://github.com/pd0wm", "avatar": "https://avatars.githubusercontent.com/u/1314752?v=4"}, {"login": "geohot", "name": "geohot", "url": "https://github.com/geohot", "avatar": "https://avatars.githubusercontent.com/u/72895?v=4"}]}, "docs/how-to/turn-the-speed-blue.md": {"last_commit_date": "2024-07-28", "authors": [{"login": "adeebshihadeh", "name": "adeebshihadeh", "url": "https://github.com/adeebshihadeh", "avatar": "https://avatars.githubusercontent.com/u/8762862?v=4"}]}}}
|
||||||
+19
@@ -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 +1,3 @@
|
|||||||
Wen
|
Wen
|
||||||
REGIST
|
REGIST
|
||||||
PullRequest
|
PullRequest
|
||||||
cancelled
|
|
||||||
FOF
|
|
||||||
NoO
|
|
||||||
|
|||||||
@@ -13,6 +13,27 @@
|
|||||||
*.o-*
|
*.o-*
|
||||||
*.os
|
*.os
|
||||||
*.os-*
|
*.os-*
|
||||||
|
*.so
|
||||||
|
*.a
|
||||||
|
|
||||||
venv/
|
venv/
|
||||||
.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
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ end_of_line = lf
|
|||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
[*.{py,pyx,pxd}]
|
[{*.py, *.pyx, *.pxd}]
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|||||||
+3
-3
@@ -7,12 +7,12 @@
|
|||||||
*.png filter=lfs diff=lfs merge=lfs -text
|
*.png filter=lfs diff=lfs merge=lfs -text
|
||||||
*.gif filter=lfs diff=lfs merge=lfs -text
|
*.gif filter=lfs diff=lfs merge=lfs -text
|
||||||
*.ttf 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
|
*.wav filter=lfs diff=lfs merge=lfs -text
|
||||||
|
|
||||||
selfdrive/car/tests/test_models_segs.txt filter=lfs diff=lfs merge=lfs -text
|
selfdrive/car/tests/test_models_segs.txt filter=lfs diff=lfs merge=lfs -text
|
||||||
system/hardware/tici/updater_weston filter=lfs diff=lfs merge=lfs -text
|
system/hardware/tici/updater filter=lfs diff=lfs merge=lfs -text
|
||||||
system/hardware/tici/updater_magic 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/**/*.a filter=lfs diff=lfs merge=lfs -text
|
||||||
third_party/**/*.so filter=lfs diff=lfs merge=lfs -text
|
third_party/**/*.so filter=lfs diff=lfs merge=lfs -text
|
||||||
third_party/**/*.so.* filter=lfs diff=lfs merge=lfs -text
|
third_party/**/*.so.* filter=lfs diff=lfs merge=lfs -text
|
||||||
|
|||||||
+1
-9
@@ -1,11 +1,3 @@
|
|||||||
* @sunnypilot/dev-internal
|
* @sunnypilot/dev-internal
|
||||||
/.github/ @devtekve @sunnyhaibin
|
/.github/ @devtekve @sunnyhaibin
|
||||||
/release/ci/ @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
|
|
||||||
@@ -1,10 +1,6 @@
|
|||||||
ci:
|
CI / testing:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-all-files: "{.github/**,**/test_*,**/test/**,Jenkinsfile}"
|
- any-glob-to-all-files: "{.github/**,**/test_*,Jenkinsfile}"
|
||||||
|
|
||||||
chore:
|
|
||||||
- changed-files:
|
|
||||||
- any-glob-to-all-files: "{.github/**}"
|
|
||||||
|
|
||||||
car:
|
car:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
@@ -16,7 +12,7 @@ simulation:
|
|||||||
|
|
||||||
ui:
|
ui:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-all-files: '{selfdrive/assets/**,selfdrive/ui/**,system/ui/**}'
|
- any-glob-to-all-files: 'selfdrive/ui/**'
|
||||||
|
|
||||||
tools:
|
tools:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
name: 'automatically cache based on current runner'
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
path:
|
||||||
|
description: 'path to cache'
|
||||||
|
required: true
|
||||||
|
key:
|
||||||
|
description: 'key'
|
||||||
|
required: true
|
||||||
|
restore-keys:
|
||||||
|
description: 'restore-keys'
|
||||||
|
required: true
|
||||||
|
save:
|
||||||
|
description: 'whether to save the cache'
|
||||||
|
default: 'false'
|
||||||
|
required: false
|
||||||
|
outputs:
|
||||||
|
cache-hit:
|
||||||
|
description: 'cache hit occurred'
|
||||||
|
value: ${{ (contains(runner.name, 'nsc') && steps.ns-cache.outputs.cache-hit) ||
|
||||||
|
(!contains(runner.name, 'nsc') && inputs.save != 'false' && steps.gha-cache.outputs.cache-hit) ||
|
||||||
|
(!contains(runner.name, 'nsc') && inputs.save == 'false' && steps.gha-cache-ro.outputs.cache-hit) }}
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- name: setup namespace cache
|
||||||
|
id: ns-cache
|
||||||
|
if: ${{ contains(runner.name, 'nsc') }}
|
||||||
|
uses: namespacelabs/nscloud-cache-action@v1
|
||||||
|
with:
|
||||||
|
path: ${{ inputs.path }}
|
||||||
|
|
||||||
|
- name: setup github cache
|
||||||
|
id: gha-cache
|
||||||
|
if: ${{ !contains(runner.name, 'nsc') && inputs.save != 'false' }}
|
||||||
|
uses: 'actions/cache@v4'
|
||||||
|
with:
|
||||||
|
path: ${{ inputs.path }}
|
||||||
|
key: ${{ inputs.key }}
|
||||||
|
restore-keys: ${{ inputs.restore-keys }}
|
||||||
|
|
||||||
|
- name: setup github cache
|
||||||
|
id: gha-cache-ro
|
||||||
|
if: ${{ !contains(runner.name, 'nsc') && inputs.save == 'false' }}
|
||||||
|
uses: 'actions/cache/restore@v4'
|
||||||
|
with:
|
||||||
|
path: ${{ inputs.path }}
|
||||||
|
key: ${{ inputs.key }}
|
||||||
|
restore-keys: ${{ inputs.restore-keys }}
|
||||||
|
|
||||||
|
# make the directory manually in case we didn't get a hit, so it doesn't fail on future steps
|
||||||
|
- id: scons-cache-setup
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
mkdir -p ${{ inputs.path }}
|
||||||
|
sudo chmod -R 777 ${{ inputs.path }}
|
||||||
|
sudo chown -R $USER ${{ inputs.path }}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
name: "PR review"
|
name: "PR review"
|
||||||
on:
|
on:
|
||||||
pull_request_target:
|
pull_request_target:
|
||||||
types: [ opened, reopened, synchronize, edited ]
|
types: [opened, reopened, synchronize, edited]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
labeler:
|
labeler:
|
||||||
@@ -9,15 +9,14 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
issues: write
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: false
|
submodules: false
|
||||||
|
|
||||||
# Label PRs
|
# Label PRs
|
||||||
- uses: actions/labeler@v6
|
- uses: actions/labeler@v5.0.0
|
||||||
with:
|
with:
|
||||||
dot: true
|
dot: true
|
||||||
configuration-path: .github/labeler.yaml
|
configuration-path: .github/labeler.yaml
|
||||||
@@ -30,62 +29,24 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
target: /^(?!master$).*/
|
target: /^(?!master$).*/
|
||||||
exclude: /sunnypilot:.*/
|
exclude: /commaai:.*/
|
||||||
change-to: ${{ github.base_ref }}
|
change-to: ${{ github.base_ref }}
|
||||||
already-exists-action: close_this
|
already-exists-action: close_this
|
||||||
already-exists-comment: "Your PR should be made against the `master` branch"
|
already-exists-comment: "Your PR should be made against the `master` branch"
|
||||||
|
|
||||||
update-pr-labels:
|
# Welcome comment
|
||||||
name: Update fork's PR Labels
|
- name: "First timers PR"
|
||||||
runs-on: ubuntu-latest
|
uses: actions/first-interaction@v1
|
||||||
if: (github.event.pull_request.head.repo.fork && (contains(github.event_name, 'pull_request') && github.event.action == 'synchronize'))
|
if: github.event.pull_request.head.repo.full_name != 'sunnypilot/sunnypilot'
|
||||||
env:
|
with:
|
||||||
PR_LABEL: 'dev'
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
TRUST_FORK_PR_LABEL: 'trust-fork-pr'
|
pr-message: |
|
||||||
steps:
|
<!-- _(run_id **${{ github.run_id }}**)_ -->
|
||||||
- name: Check if PR has dev label
|
Thanks for contributing to openpilot! In order for us to review your PR as quickly as possible, check the following:
|
||||||
id: check-labels
|
* Convert your PR to a draft unless it's ready to review
|
||||||
uses: actions/github-script@v7
|
* Read the [contributing docs](https://github.com/sunnypilot/sunnypilot/blob/master/docs/CONTRIBUTING.md)
|
||||||
with:
|
* Before marking as "ready for review", ensure:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
* the goal is clearly stated in the description
|
||||||
script: |
|
* all the tests are passing
|
||||||
const prNumber = context.payload.pull_request.number;
|
* the change is [something we merge](https://github.com/sunnypilot/sunnypilot/blob/master/docs/CONTRIBUTING.md#what-gets-merged)
|
||||||
const { data: labels } = await github.rest.issues.listLabelsOnIssue({
|
* include a route or your device' dongle ID if relevant
|
||||||
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.`
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
PYTHONPATH: ${{ github.workspace }}
|
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:
|
jobs:
|
||||||
badges:
|
badges:
|
||||||
@@ -15,13 +17,13 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- run: ./tools/op.sh setup
|
- uses: ./.github/workflows/setup-with-retry
|
||||||
- name: Push badges
|
- name: Push badges
|
||||||
run: |
|
run: |
|
||||||
python3 selfdrive/ui/translations/create_badges.py
|
${{ env.RUN }} "scons -j$(nproc) && python3 selfdrive/ui/translations/create_badges.py"
|
||||||
|
|
||||||
rm .gitattributes
|
rm .gitattributes
|
||||||
|
|
||||||
|
|||||||
@@ -1,304 +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 < <(
|
|
||||||
find output -maxdepth 1 -name 'model-*' -printf "%f\n" | 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:
|
|
||||||
fail-fast: false
|
|
||||||
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
|
|
||||||
@@ -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
|
|
||||||
@@ -4,6 +4,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
- master-new
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- 'cereal/**'
|
- 'cereal/**'
|
||||||
@@ -16,27 +17,31 @@ on:
|
|||||||
type: string
|
type: string
|
||||||
|
|
||||||
concurrency:
|
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 }}
|
group: cereal-validation-ci-run-${{ inputs.run_number }}-${{ github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/master-new') && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CI: 1
|
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:
|
jobs:
|
||||||
generate_cereal_artifact:
|
generate_cereal_artifact:
|
||||||
name: Generate cereal validation artifacts
|
name: Generate cereal validation artifacts
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- run: ./tools/op.sh setup
|
- uses: ./.github/workflows/setup-with-retry
|
||||||
- name: Build openpilot
|
- name: Build openpilot
|
||||||
run: scons -j$(nproc) cereal
|
run: ${{ env.RUN }} "scons -j$(nproc) cereal"
|
||||||
- name: Generate the log file
|
- name: Generate the log file
|
||||||
run: |
|
run: |
|
||||||
export PYTHONPATH=${{ github.workspace }}
|
${{ env.RUN }} "cereal/messaging/tests/validate_sp_cereal_upstream.py -g -f schema_instances.bin" && \
|
||||||
python3 cereal/messaging/tests/validate_sp_cereal_upstream.py -g -f schema_instances.bin
|
ls -la
|
||||||
|
ls -la cereal/messaging/tests
|
||||||
- name: 'Prepare artifact'
|
- name: 'Prepare artifact'
|
||||||
run: |
|
run: |
|
||||||
mkdir -p "cereal/messaging/tests/cereal_validations"
|
mkdir -p "cereal/messaging/tests/cereal_validations"
|
||||||
@@ -53,26 +58,20 @@ jobs:
|
|||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
needs: generate_cereal_artifact
|
needs: generate_cereal_artifact
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sunnypilot
|
- uses: actions/checkout@v4
|
||||||
uses: actions/checkout@v6
|
|
||||||
- name: Checkout upstream openpilot
|
|
||||||
uses: actions/checkout@v6
|
|
||||||
with:
|
with:
|
||||||
repository: 'commaai/openpilot'
|
repository: 'commaai/openpilot'
|
||||||
path: openpilot
|
|
||||||
submodules: true
|
submodules: true
|
||||||
ref: "refs/heads/master"
|
ref: "refs/heads/master"
|
||||||
- run: ./tools/op.sh setup
|
- uses: ./.github/workflows/setup-with-retry
|
||||||
- name: Build openpilot
|
- name: Build openpilot
|
||||||
working-directory: openpilot
|
run: ${{ env.RUN }} "scons -j$(nproc) cereal"
|
||||||
run: scons -j$(nproc) cereal
|
|
||||||
- name: Download build artifacts
|
- name: Download build artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: cereal_validations
|
name: cereal_validations
|
||||||
path: openpilot/cereal/messaging/tests/cereal_validations
|
path: cereal/messaging/tests/cereal_validations
|
||||||
- name: 'Run the validation'
|
- name: 'Run the validation'
|
||||||
run: |
|
run: |
|
||||||
export PYTHONPATH=${{ github.workspace }}/openpilot
|
chmod +x cereal/messaging/tests/cereal_validations/validate_sp_cereal_upstream.py
|
||||||
chmod +x openpilot/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"
|
||||||
python3 openpilot/cereal/messaging/tests/cereal_validations/validate_sp_cereal_upstream.py -r -f openpilot/cereal/messaging/tests/cereal_validations/schema_instances.bin
|
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
name: weekly CI test report
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '37 9 * * 1' # 9:37AM UTC -> 2:37AM PST every monday
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
ci_runs:
|
||||||
|
description: 'The amount of runs to trigger in CI test report'
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
CI_RUNS: ${{ github.event.inputs.ci_runs || '50' }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
setup:
|
||||||
|
if: github.repository == 'sunnypilot/sunnypilot'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
ci_runs: ${{ steps.ci_runs_setup.outputs.matrix }}
|
||||||
|
steps:
|
||||||
|
- id: ci_runs_setup
|
||||||
|
name: CI_RUNS=${{ env.CI_RUNS }}
|
||||||
|
run: |
|
||||||
|
matrix=$(python3 -c "import json; print(json.dumps({ 'run_number' : list(range(${{ env.CI_RUNS }})) }))")
|
||||||
|
echo "matrix=$matrix" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
ci_matrix_run:
|
||||||
|
needs: [ setup ]
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix: ${{fromJSON(needs.setup.outputs.ci_runs)}}
|
||||||
|
uses: sunnypilot/sunnypilot/.github/workflows/ci_weekly_run.yaml@master
|
||||||
|
with:
|
||||||
|
run_number: ${{ matrix.run_number }}
|
||||||
|
|
||||||
|
report:
|
||||||
|
needs: [ci_matrix_run]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: always()
|
||||||
|
steps:
|
||||||
|
- name: Get job results
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
id: get-job-results
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const jobs = await github
|
||||||
|
.paginate("GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt}/jobs", {
|
||||||
|
owner: "commaai",
|
||||||
|
repo: "${{ github.event.repository.name }}",
|
||||||
|
run_id: "${{ github.run_id }}",
|
||||||
|
attempt: "${{ github.run_attempt }}",
|
||||||
|
})
|
||||||
|
var report = {}
|
||||||
|
jobs.slice(1, jobs.length-1).forEach(job => {
|
||||||
|
if (job.conclusion === "skipped") return;
|
||||||
|
const jobName = job.name.split(" / ")[2];
|
||||||
|
const runRegex = /\((.*?)\)/;
|
||||||
|
const run = job.name.match(runRegex)[1];
|
||||||
|
report[jobName] = report[jobName] || { successes: [], failures: [], canceled: [] };
|
||||||
|
switch (job.conclusion) {
|
||||||
|
case "success":
|
||||||
|
report[jobName].successes.push({ "run_number": run, "link": job.html_url}); break;
|
||||||
|
case "failure":
|
||||||
|
report[jobName].failures.push({ "run_number": run, "link": job.html_url }); break;
|
||||||
|
case "canceled":
|
||||||
|
report[jobName].canceled.push({ "run_number": run, "link": job.html_url }); break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return JSON.stringify({"jobs": report});
|
||||||
|
|
||||||
|
- name: Add job results to summary
|
||||||
|
env:
|
||||||
|
JOB_RESULTS: ${{ fromJSON(steps.get-job-results.outputs.result) }}
|
||||||
|
run: |
|
||||||
|
cat <<EOF >> template.html
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th>Job</th>
|
||||||
|
<th>✅ Passing</th>
|
||||||
|
<th>❌ Failure Details</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for key in jobs.keys() %}<tr>
|
||||||
|
<td>{% for i in range(5) %}{% if i+1 <= (5 * jobs[key]["successes"]|length // ${{ env.CI_RUNS }}) %}🟩{% else %}🟥{% endif %}{% endfor%}</td>
|
||||||
|
<td>{{ key }}</td>
|
||||||
|
<td>{{ 100 * jobs[key]["successes"]|length // ${{ env.CI_RUNS }} }}%</td>
|
||||||
|
<td>{% if jobs[key]["failures"]|length > 0 %}<details>{% for failure in jobs[key]["failures"] %}<a href="{{ failure['link'] }}">Log for run #{{ failure['run_number'] }}</a><br>{% endfor %}</details>{% else %}{% endif %}</td>
|
||||||
|
</td>
|
||||||
|
</tr>{% endfor %}
|
||||||
|
</table>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
pip install jinja2-cli
|
||||||
|
echo $JOB_RESULTS | jinja2 template.html > report.html
|
||||||
|
echo "# CI Test Report - ${{ env.CI_RUNS }} Runs" >> $GITHUB_STEP_SUMMARY
|
||||||
|
cat report.html >> $GITHUB_STEP_SUMMARY
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
name: weekly CI test run
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
run_number:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ci-run-${{ inputs.run_number }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
selfdrive_tests:
|
||||||
|
uses: sunnypilot/sunnypilot/.github/workflows/selfdrive_tests.yaml@master
|
||||||
|
with:
|
||||||
|
run_number: ${{ inputs.run_number }}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
name: 'compile openpilot'
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- shell: bash
|
||||||
|
name: Build openpilot with all flags
|
||||||
|
run: |
|
||||||
|
${{ env.RUN }} "scons -j$(nproc)"
|
||||||
|
${{ env.RUN }} "release/check-dirty.sh"
|
||||||
|
- shell: bash
|
||||||
|
name: Cleanup scons cache and rebuild
|
||||||
|
run: |
|
||||||
|
${{ env.RUN }} "rm -rf /tmp/scons_cache/* && \
|
||||||
|
scons -j$(nproc) --cache-populate"
|
||||||
|
- name: Save scons cache
|
||||||
|
uses: actions/cache/save@v4
|
||||||
|
if: (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/master-new')
|
||||||
|
with:
|
||||||
|
path: .ci_cache/scons_cache
|
||||||
|
key: scons-${{ runner.arch }}-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
name: sunnypilot docs
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- docs
|
|
||||||
paths:
|
|
||||||
- 'docs_sp/**'
|
|
||||||
- 'zensical.toml'
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- 'docs_sp/**'
|
|
||||||
- 'zensical.toml'
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: docs-sp-${{ github.event_name == 'push' && github.ref == 'refs/heads/docs' && github.run_id || github.head_ref || github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: build sunnypilot docs
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: '3.12'
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
pip install uv
|
|
||||||
uv pip install --system zensical
|
|
||||||
|
|
||||||
- name: Build docs
|
|
||||||
run: zensical build
|
|
||||||
|
|
||||||
# Push to docs.sunnypilot.ai
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
if: github.ref == 'refs/heads/docs' && github.repository == 'sunnypilot/sunnypilot'
|
|
||||||
with:
|
|
||||||
path: sunnypilot-docs
|
|
||||||
ssh-key: ${{ secrets.OPENPILOT_DOCS_KEY }}
|
|
||||||
repository: sunnypilot/sunnypilot-docs
|
|
||||||
|
|
||||||
- name: Push to GitHub Pages
|
|
||||||
if: github.ref == 'refs/heads/docs' && github.repository == 'sunnypilot/sunnypilot'
|
|
||||||
run: |
|
|
||||||
set -x
|
|
||||||
|
|
||||||
source release/identity.sh
|
|
||||||
|
|
||||||
cd sunnypilot-docs
|
|
||||||
git checkout --orphan tmp
|
|
||||||
git rm -rf .
|
|
||||||
|
|
||||||
cp -r ../docs_site_sp/ docs/
|
|
||||||
|
|
||||||
touch docs/.nojekyll
|
|
||||||
echo -n docs.sunnypilot.ai > docs/CNAME
|
|
||||||
|
|
||||||
git add -f .
|
|
||||||
git commit -m "build sunnypilot docs"
|
|
||||||
|
|
||||||
git push -f origin tmp:gh-pages
|
|
||||||
@@ -22,7 +22,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: commaai/timeout@v1
|
- uses: commaai/timeout@v1
|
||||||
|
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ jobs:
|
|||||||
mkdocs build
|
mkdocs build
|
||||||
|
|
||||||
# Push to docs.comma.ai
|
# Push to docs.comma.ai
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
if: github.ref == 'refs/heads/master' && github.repository == 'sunnypilot/sunnypilot'
|
if: github.ref == 'refs/heads/master' && github.repository == 'sunnypilot/sunnypilot'
|
||||||
with:
|
with:
|
||||||
path: openpilot-docs
|
path: openpilot-docs
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
name: Sync docs to Discourse
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_run:
|
|
||||||
workflows: ["sunnypilot docs"]
|
|
||||||
types:
|
|
||||||
- completed
|
|
||||||
branches:
|
|
||||||
- docs
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: forum-docs-sync
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
sync:
|
|
||||||
name: sync docs to Discourse
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
if: >
|
|
||||||
github.event.workflow_run.conclusion == 'success' &&
|
|
||||||
github.repository == 'sunnypilot/sunnypilot'
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
with:
|
|
||||||
ref: docs
|
|
||||||
|
|
||||||
- name: Set up Ruby
|
|
||||||
uses: ruby/setup-ruby@v1
|
|
||||||
with:
|
|
||||||
ruby-version: '3.3'
|
|
||||||
|
|
||||||
- name: Restore sync cache
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: .discourse_sync_cache
|
|
||||||
key: discourse-sync-${{ hashFiles('docs_sp/**/*.md') }}
|
|
||||||
restore-keys: |
|
|
||||||
discourse-sync-
|
|
||||||
|
|
||||||
- name: Sync to Discourse
|
|
||||||
env:
|
|
||||||
DISCOURSE_URL: ${{ secrets.DISCOURSE_URL }}
|
|
||||||
DISCOURSE_API_KEY: ${{ secrets.DISCOURSE_API_KEY }}
|
|
||||||
DISCOURSE_API_USER: ${{ secrets.DISCOURSE_API_USER }}
|
|
||||||
DISCOURSE_CATEGORY: ${{ vars.DISCOURSE_DOCS_CATEGORY || 'documentation' }}
|
|
||||||
DOCS_BASE_URL: https://docs.sunnypilot.ai
|
|
||||||
run: ruby docs_sp/tools/sync_docs_discourse.rb --verbose
|
|
||||||
@@ -5,54 +5,14 @@ on:
|
|||||||
types: [created, edited]
|
types: [created, edited]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
cleanup-branches:
|
# TODO: gc old branches in a separate job in this workflow
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
steps:
|
|
||||||
- name: Delete stale Jenkins branches
|
|
||||||
uses: actions/github-script@v8
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const cutoff = Date.now() - 24 * 60 * 60 * 1000;
|
|
||||||
const prefixes = ['tmp-jenkins', '__jenkins'];
|
|
||||||
|
|
||||||
for await (const response of github.paginate.iterator(github.rest.repos.listBranches, {
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
per_page: 100,
|
|
||||||
})) {
|
|
||||||
for (const branch of response.data) {
|
|
||||||
if (!prefixes.some(p => branch.name.startsWith(p))) continue;
|
|
||||||
|
|
||||||
const { data: commit } = await github.rest.repos.getCommit({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
ref: branch.commit.sha,
|
|
||||||
});
|
|
||||||
|
|
||||||
const commitDate = new Date(commit.commit.committer.date).getTime();
|
|
||||||
if (commitDate < cutoff) {
|
|
||||||
console.log(`Deleting branch: ${branch.name} (last commit: ${commit.commit.committer.date})`);
|
|
||||||
await github.rest.git.deleteRef({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
ref: `heads/${branch.name}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scan-comments:
|
scan-comments:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.event.issue.pull_request }}
|
if: ${{ github.event.issue.pull_request }}
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
issues: write
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check for trigger phrase
|
- name: Check for trigger phrase
|
||||||
id: check_comment
|
id: check_comment
|
||||||
uses: actions/github-script@v8
|
uses: actions/github-script@v7
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const triggerPhrase = "trigger-jenkins";
|
const triggerPhrase = "trigger-jenkins";
|
||||||
@@ -72,7 +32,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
if: steps.check_comment.outputs.result == 'true'
|
if: steps.check_comment.outputs.result == 'true'
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: refs/pull/${{ github.event.issue.number }}/head
|
ref: refs/pull/${{ github.event.issue.number }}/head
|
||||||
|
|
||||||
@@ -83,14 +43,3 @@ jobs:
|
|||||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
git checkout -b tmp-jenkins-${{ github.event.issue.number }}
|
git checkout -b tmp-jenkins-${{ github.event.issue.number }}
|
||||||
GIT_LFS_SKIP_PUSH=1 git push -f origin 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@v8
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
await github.rest.issues.deleteComment({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
comment_id: context.payload.comment.id,
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ on:
|
|||||||
- cron: '0 0 * * *' # Runs at 00:00 UTC every day
|
- cron: '0 0 * * *' # Runs at 00:00 UTC every day
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- 'master'
|
- 'master-new'
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- 'master'
|
- 'master-new'
|
||||||
workflow_dispatch: # enables manual triggering
|
workflow_dispatch: # enables manual triggering
|
||||||
inputs:
|
inputs:
|
||||||
upstream_branch:
|
upstream_branch:
|
||||||
default: 'master'
|
default: 'master'
|
||||||
@@ -30,7 +30,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
repository: 'commaai/openpilot'
|
repository: 'commaai/openpilot'
|
||||||
ref: ${{ inputs.upstream_branch }}
|
ref: ${{ inputs.upstream_branch }}
|
||||||
|
|
||||||
- name: LFS Fetch
|
- name: LFS Fetch
|
||||||
run: |
|
run: |
|
||||||
git lfs fetch
|
git lfs fetch
|
||||||
@@ -48,7 +48,7 @@ jobs:
|
|||||||
- name: Add GitLab public keys
|
- name: Add GitLab public keys
|
||||||
run: |
|
run: |
|
||||||
ssh-keyscan -H gitlab.com >> ~/.ssh/known_hosts
|
ssh-keyscan -H gitlab.com >> ~/.ssh/known_hosts
|
||||||
|
|
||||||
- name: Ensure branch
|
- name: Ensure branch
|
||||||
run: |
|
run: |
|
||||||
if git symbolic-ref -q HEAD >/dev/null; then
|
if git symbolic-ref -q HEAD >/dev/null; then
|
||||||
|
|||||||
@@ -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@v6
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
- name: Checkout master
|
|
||||||
uses: actions/checkout@v6
|
|
||||||
with:
|
|
||||||
ref: master
|
|
||||||
path: base
|
|
||||||
- run: git lfs pull
|
|
||||||
- run: cd base && git lfs pull
|
|
||||||
|
|
||||||
- name: scripts/reporter.py
|
|
||||||
id: report
|
|
||||||
run: |
|
|
||||||
echo "content<<EOF" >> $GITHUB_OUTPUT
|
|
||||||
echo "## Model Review" >> $GITHUB_OUTPUT
|
|
||||||
PYTHONPATH=${{ github.workspace }} 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 }}
|
|
||||||
@@ -6,7 +6,7 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
|
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
|
||||||
BUILD: release/ci/docker_build_sp.sh
|
BUILD: selfdrive/test/docker_build.sh prebuilt
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_prebuilt:
|
build_prebuilt:
|
||||||
@@ -28,8 +28,8 @@ jobs:
|
|||||||
wait-interval: 30
|
wait-interval: 30
|
||||||
running-workflow-name: 'build prebuilt'
|
running-workflow-name: 'build prebuilt'
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
check-regexp: ^((?!.*(build master-ci|create badges).*).)*$
|
check-regexp: ^((?!.*(build master-ci).*).)*$
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- run: git lfs pull
|
- run: git lfs pull
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ name: Release Drafter
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
|
- master-new
|
||||||
- master
|
- master
|
||||||
tags:
|
tags:
|
||||||
- 'v*'
|
- 'v*'
|
||||||
|
|||||||
@@ -5,27 +5,50 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build___nightly:
|
build_masterci:
|
||||||
name: build __nightly
|
name: build master-ci
|
||||||
|
env:
|
||||||
|
TARGET_DIR: /tmp/openpilot
|
||||||
|
ImageOS: ubuntu20
|
||||||
|
container:
|
||||||
|
image: ghcr.io/commaai/openpilot-base:latest
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.repository == 'sunnypilot/sunnypilot'
|
if: github.repository == 'sunnypilot/sunnypilot'
|
||||||
permissions:
|
permissions:
|
||||||
checks: read
|
checks: read
|
||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
|
- name: Install wait-on-check-action dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libyaml-dev
|
||||||
- name: Wait for green check mark
|
- name: Wait for green check mark
|
||||||
if: ${{ github.event_name == 'schedule' }}
|
if: ${{ github.event_name != 'workflow_dispatch' }}
|
||||||
uses: lewagon/wait-on-check-action@ccfb013c15c8afb7bf2b7c028fb74dc5a068cccc
|
uses: lewagon/wait-on-check-action@ccfb013c15c8afb7bf2b7c028fb74dc5a068cccc
|
||||||
with:
|
with:
|
||||||
ref: master
|
ref: master
|
||||||
wait-interval: 30
|
wait-interval: 30
|
||||||
running-workflow-name: 'build __nightly'
|
running-workflow-name: 'build master-ci'
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
check-regexp: ^((?!.*(build prebuilt|create badges).*).)*$
|
check-regexp: ^((?!.*(build prebuilt).*).)*$
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- run: ./tools/op.sh setup
|
- name: Pull LFS
|
||||||
- name: Push __nightly
|
run: |
|
||||||
run: BRANCH=__nightly release/build_stripped.sh
|
git config --global --add safe.directory '*'
|
||||||
|
git lfs pull
|
||||||
|
- 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
|
||||||
|
|||||||
@@ -6,23 +6,24 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
PYTHONPATH: ${{ github.workspace }}
|
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:
|
jobs:
|
||||||
update_translations:
|
update_translations:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.repository == 'sunnypilot/sunnypilot'
|
if: github.repository == 'commaai/openpilot'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
with:
|
- uses: ./.github/workflows/setup-with-retry
|
||||||
submodules: true
|
|
||||||
- run: ./tools/op.sh setup
|
|
||||||
- name: Update translations
|
- name: Update translations
|
||||||
run: python3 selfdrive/ui/update_translations.py --vanish
|
run: |
|
||||||
|
${{ env.RUN }} "python3 selfdrive/ui/update_translations.py --vanish"
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0
|
uses: peter-evans/create-pull-request@9153d834b60caba6d51c9b9510b087acf9f33f83
|
||||||
with:
|
with:
|
||||||
author: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
|
author: Vehicle Researcher <user@comma.ai>
|
||||||
commit-message: "Update translations"
|
commit-message: "Update translations"
|
||||||
title: "[bot] Update translations"
|
title: "[bot] Update translations"
|
||||||
body: "Automatic PR from repo-maintenance -> update_translations"
|
body: "Automatic PR from repo-maintenance -> update_translations"
|
||||||
@@ -34,67 +35,37 @@ jobs:
|
|||||||
package_updates:
|
package_updates:
|
||||||
name: package_updates
|
name: package_updates
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ghcr.io/commaai/openpilot-base:latest
|
||||||
if: github.repository == 'sunnypilot/sunnypilot'
|
if: github.repository == 'sunnypilot/sunnypilot'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- run: ./tools/op.sh setup
|
|
||||||
- name: uv lock
|
- name: uv lock
|
||||||
run: uv lock --upgrade
|
|
||||||
- name: uv pip tree
|
|
||||||
id: pip_tree
|
|
||||||
run: |
|
run: |
|
||||||
echo 'PIP_TREE<<EOF' >> $GITHUB_OUTPUT
|
python3 -m ensurepip --upgrade
|
||||||
uv pip tree >> $GITHUB_OUTPUT
|
pip3 install uv
|
||||||
echo 'EOF' >> $GITHUB_OUTPUT
|
uv lock --upgrade
|
||||||
- name: venv size
|
|
||||||
id: venv_size
|
|
||||||
run: |
|
|
||||||
echo 'VENV_SIZE<<EOF' >> $GITHUB_OUTPUT
|
|
||||||
echo "Total: $(du -sh .venv | cut -f1)" >> $GITHUB_OUTPUT
|
|
||||||
echo "" >> $GITHUB_OUTPUT
|
|
||||||
echo "Top 10 by size:" >> $GITHUB_OUTPUT
|
|
||||||
du -sh .venv/lib/python*/site-packages/* 2>/dev/null \
|
|
||||||
| grep -v '\.dist-info' \
|
|
||||||
| grep -v '__pycache__' \
|
|
||||||
| sort -rh \
|
|
||||||
| head -10 \
|
|
||||||
| while IFS=$'\t' read size path; do echo "$size ${path##*/}"; done >> $GITHUB_OUTPUT
|
|
||||||
echo 'EOF' >> $GITHUB_OUTPUT
|
|
||||||
- name: bump submodules
|
- name: bump submodules
|
||||||
run: |
|
run: |
|
||||||
git config submodule.msgq.update none
|
git config --global --add safe.directory '*'
|
||||||
git config submodule.rednose_repo.update none
|
git -c submodule."tinygrad".update=none submodule update --remote
|
||||||
git config submodule.teleoprtc_repo.update none
|
|
||||||
git config submodule.tinygrad.update none
|
|
||||||
git submodule update --remote
|
|
||||||
git add .
|
git add .
|
||||||
- name: update car docs
|
- name: update car docs
|
||||||
run: |
|
run: |
|
||||||
scons -j$(nproc) --minimal opendbc_repo
|
scons -j$(nproc) --minimal opendbc_repo
|
||||||
python selfdrive/car/docs.py
|
PYTHONPATH=. python selfdrive/car/docs.py
|
||||||
git add docs/CARS.md
|
git add docs/CARS.md
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0
|
uses: peter-evans/create-pull-request@9153d834b60caba6d51c9b9510b087acf9f33f83
|
||||||
with:
|
with:
|
||||||
author: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
|
author: Vehicle Researcher <user@comma.ai>
|
||||||
token: ${{ github.repository == 'commaai/openpilot' && secrets.ACTIONS_CREATE_PR_PAT || secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.ACTIONS_CREATE_PR_PAT }}
|
||||||
commit-message: Update Python packages
|
commit-message: Update Python packages
|
||||||
title: '[bot] Update Python packages'
|
title: '[bot] Update Python packages'
|
||||||
branch: auto-package-updates
|
branch: auto-package-updates
|
||||||
base: master
|
base: master
|
||||||
delete-branch: true
|
delete-branch: true
|
||||||
body: |
|
body: 'Automatic PR from repo-maintenance -> package_updates'
|
||||||
Automatic PR from repo-maintenance -> package_updates
|
|
||||||
|
|
||||||
```
|
|
||||||
$ du -sh .venv && du -sh .venv/lib/python*/site-packages/* | sort -rh | head -10
|
|
||||||
${{ steps.venv_size.outputs.VENV_SIZE }}
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
$ uv pip tree
|
|
||||||
${{ steps.pip_tree.outputs.PIP_TREE }}
|
|
||||||
```
|
|
||||||
labels: bot
|
labels: bot
|
||||||
|
|||||||
@@ -0,0 +1,366 @@
|
|||||||
|
name: selfdrive
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- master-new
|
||||||
|
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.ref == 'refs/heads/master-new') && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
REPORT_NAME: report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/master-new') && 'master' || github.event.number }}
|
||||||
|
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:
|
||||||
|
if: github.repository == 'commaai/openpilot' # build_release blocked for the time being to only comma as we may have a different process.
|
||||||
|
name: build release
|
||||||
|
runs-on:
|
||||||
|
- '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 == 'sunnypilot/sunnypilot'
|
||||||
|
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:
|
||||||
|
- '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') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-macos-8x14' || 'macos-latest' }}
|
||||||
|
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:
|
||||||
|
path: ~/Library/Caches/Homebrew
|
||||||
|
key: brew-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
|
||||||
|
restore-keys: |
|
||||||
|
brew-macos-${{ env.CACHE_COMMIT_DATE }}
|
||||||
|
brew-macos
|
||||||
|
- name: Install dependencies
|
||||||
|
run: ./tools/mac_setup.sh
|
||||||
|
env:
|
||||||
|
# package install has DeprecationWarnings
|
||||||
|
PYTHONWARNINGS: default
|
||||||
|
- name: Save Homebrew cache
|
||||||
|
uses: actions/cache/save@v4
|
||||||
|
if: (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/master-new')
|
||||||
|
with:
|
||||||
|
path: ~/Library/Caches/Homebrew
|
||||||
|
key: brew-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
|
||||||
|
- run: git lfs pull
|
||||||
|
- name: Getting scons cache
|
||||||
|
uses: ./.github/workflows/auto-cache
|
||||||
|
with:
|
||||||
|
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' || github.ref == 'refs/heads/master-new')
|
||||||
|
with:
|
||||||
|
path: /tmp/scons_cache
|
||||||
|
key: scons-${{ runner.arch }}-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
|
||||||
|
|
||||||
|
static_analysis:
|
||||||
|
name: static analysis
|
||||||
|
runs-on:
|
||||||
|
- 'ubuntu-latest'
|
||||||
|
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:
|
||||||
|
- '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
|
||||||
|
if: github.repository == 'commaai/openpilot' # disable process_replay for forks
|
||||||
|
runs-on:
|
||||||
|
- '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:
|
||||||
|
- '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:
|
||||||
|
- '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:
|
||||||
|
- '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') && 2 || 4) }}
|
||||||
|
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: ${{ env.REPORT_NAME }}
|
||||||
|
path: selfdrive/ui/tests/test_ui/report_1/screenshots
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
name: 'openpilot env setup, with retry on failure'
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
docker_hub_pat:
|
||||||
|
description: 'Auth token for Docker Hub, required for BuildJet jobs'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
sleep_time:
|
||||||
|
description: 'Time to sleep between retries'
|
||||||
|
required: false
|
||||||
|
default: 30
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- id: setup1
|
||||||
|
uses: ./.github/workflows/setup
|
||||||
|
continue-on-error: true
|
||||||
|
with:
|
||||||
|
is_retried: true
|
||||||
|
- if: steps.setup1.outcome == 'failure'
|
||||||
|
shell: bash
|
||||||
|
run: sleep ${{ inputs.sleep_time }}
|
||||||
|
- id: setup2
|
||||||
|
if: steps.setup1.outcome == 'failure'
|
||||||
|
uses: ./.github/workflows/setup
|
||||||
|
continue-on-error: true
|
||||||
|
with:
|
||||||
|
is_retried: true
|
||||||
|
- if: steps.setup2.outcome == 'failure'
|
||||||
|
shell: bash
|
||||||
|
run: sleep ${{ inputs.sleep_time }}
|
||||||
|
- id: setup3
|
||||||
|
if: steps.setup2.outcome == 'failure'
|
||||||
|
uses: ./.github/workflows/setup
|
||||||
|
with:
|
||||||
|
is_retried: true
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
name: 'openpilot env setup'
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
is_retried:
|
||||||
|
description: 'A mock param that asserts that we use the setup-with-retry instead of this action directly'
|
||||||
|
required: false
|
||||||
|
default: 'false'
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
# assert that this action is retried using the setup-with-retry
|
||||||
|
- shell: bash
|
||||||
|
if: ${{ inputs.is_retried == 'false' }}
|
||||||
|
run: |
|
||||||
|
echo "You should not run this action directly. Use setup-with-retry instead"
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
- 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
|
||||||
|
echo -e "\033[0;31m##################################################"
|
||||||
|
echo -e "\033[0;31m Retries not allowed! Fix the flaky test! "
|
||||||
|
echo -e "\033[0;31m##################################################\033[0m"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# do this after checkout to ensure our custom LFS config is used to pull from GitLab
|
||||||
|
- shell: bash
|
||||||
|
run: git lfs pull
|
||||||
|
|
||||||
|
# build cache
|
||||||
|
- id: date
|
||||||
|
shell: bash
|
||||||
|
run: echo "CACHE_COMMIT_DATE=$(git log -1 --pretty='format:%cd' --date=format:'%Y-%m-%d-%H:%M')" >> $GITHUB_ENV
|
||||||
|
- shell: bash
|
||||||
|
run: echo "$CACHE_COMMIT_DATE"
|
||||||
|
- id: scons-cache
|
||||||
|
uses: ./.github/workflows/auto-cache
|
||||||
|
with:
|
||||||
|
path: .ci_cache/scons_cache
|
||||||
|
key: scons-${{ runner.arch }}-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
|
||||||
|
restore-keys: |
|
||||||
|
scons-${{ runner.arch }}-${{ env.CACHE_COMMIT_DATE }}
|
||||||
|
scons-${{ runner.arch }}
|
||||||
|
# as suggested here: https://github.com/moby/moby/issues/32816#issuecomment-910030001
|
||||||
|
- id: normalize-file-permissions
|
||||||
|
shell: bash
|
||||||
|
name: Normalize file permissions to ensure a consistent docker build cache
|
||||||
|
run: |
|
||||||
|
find . -type f -executable -not -perm 755 -exec chmod 755 {} \;
|
||||||
|
find . -type f -not -executable -not -perm 644 -exec chmod 644 {} \;
|
||||||
|
# build our docker image
|
||||||
|
- shell: bash
|
||||||
|
run: eval ${{ env.BUILD }}
|
||||||
@@ -5,15 +5,14 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
DAYS_BEFORE_PR_CLOSE: 7
|
DAYS_BEFORE_PR_CLOSE: 2
|
||||||
DAYS_BEFORE_PR_STALE: 24
|
DAYS_BEFORE_PR_STALE: 9
|
||||||
DAYS_BEFORE_PR_STALE_DRAFT: 30
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
stale:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v10
|
- uses: actions/stale@v9
|
||||||
with:
|
with:
|
||||||
exempt-all-milestones: true
|
exempt-all-milestones: true
|
||||||
|
|
||||||
@@ -25,28 +24,6 @@ jobs:
|
|||||||
exempt-pr-labels: "ignore stale,needs testing" # if wip or it needs testing from the community, don't mark as stale
|
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-stale: ${{ env.DAYS_BEFORE_PR_STALE }}
|
||||||
days-before-pr-close: ${{ env.DAYS_BEFORE_PR_CLOSE }}
|
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@v10
|
|
||||||
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
|
# issue config
|
||||||
days-before-issue-stale: -1 # ignore issues for now
|
days-before-issue-stale: -1 # ignore issues for now
|
||||||
|
|||||||
@@ -5,31 +5,8 @@ env:
|
|||||||
OUTPUT_DIR: ${{ github.workspace }}/output
|
OUTPUT_DIR: ${{ github.workspace }}/output
|
||||||
SCONS_CACHE_DIR: ${{ github.workspace }}/release/ci/scons_cache
|
SCONS_CACHE_DIR: ${{ github.workspace }}/release/ci/scons_cache
|
||||||
UPSTREAM_REPO: "commaai/openpilot"
|
UPSTREAM_REPO: "commaai/openpilot"
|
||||||
TINYGRAD_PATH: ${{ github.workspace }}/tinygrad_repo
|
|
||||||
MODELS_DIR: ${{ github.workspace }}/selfdrive/modeld/models
|
|
||||||
|
|
||||||
on:
|
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:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
upstream_branch:
|
upstream_branch:
|
||||||
@@ -38,80 +15,41 @@ on:
|
|||||||
default: 'master'
|
default: 'master'
|
||||||
type: string
|
type: string
|
||||||
custom_name:
|
custom_name:
|
||||||
description: 'Custom name for the model (no date, only name)'
|
description: 'Custom name for the model'
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
file_name:
|
||||||
|
description: 'File name prefix for the model files'
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
is_20hz:
|
is_20hz:
|
||||||
description: 'Is this a 20Hz model'
|
description: 'Is this a 20Hz model'
|
||||||
required: false
|
required: false
|
||||||
type: boolean
|
type: boolean
|
||||||
default: true
|
default: false
|
||||||
|
|
||||||
|
|
||||||
run-name: Build model [${{ inputs.custom_name || inputs.upstream_branch }}] from ref [${{ inputs.upstream_branch }}]
|
run-name: Build model [${{ inputs.custom_name || inputs.upstream_branch }}] from ref [${{ inputs.upstream_branch }}]
|
||||||
|
|
||||||
jobs:
|
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:
|
build_model:
|
||||||
runs-on: [self-hosted, tici]
|
runs-on: self-hosted
|
||||||
needs: get_model
|
|
||||||
env:
|
|
||||||
MODEL_NAME: ${{ inputs.custom_name || inputs.upstream_branch }} (${{ needs.get_model.outputs.model_date }})
|
|
||||||
REF: ${{ inputs.upstream_branch }}
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
|
repository: ${{ env.UPSTREAM_REPO }}
|
||||||
|
ref: ${{ github.event.inputs.upstream_branch }}
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
- run: git lfs pull
|
- run: git lfs pull
|
||||||
|
|
||||||
- name: Cache SCons
|
- name: Cache SCons
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ${{env.SCONS_CACHE_DIR}}
|
path: ${{env.SCONS_CACHE_DIR}}
|
||||||
key: scons-${{ runner.os }}-${{ runner.arch }}-${{ github.head_ref || github.ref_name }}-model-${{ github.sha }}
|
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.)
|
# 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.
|
# for security. Only caches from the default branch are shared across all builds. This is by design and cannot be overridden.
|
||||||
restore-keys: |
|
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 }}-model
|
||||||
@@ -122,108 +60,74 @@ jobs:
|
|||||||
scons-${{ runner.os }}-${{ runner.arch }}-${{ env.MASTER_BRANCH }}
|
scons-${{ runner.os }}-${{ runner.arch }}-${{ env.MASTER_BRANCH }}
|
||||||
scons-${{ runner.os }}-${{ runner.arch }}
|
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
|
- name: Setup build environment
|
||||||
run: |
|
run: |
|
||||||
mkdir -p "${BUILD_DIR}/"
|
mkdir -p "${BUILD_DIR}/"
|
||||||
sudo find $BUILD_DIR/ -mindepth 1 -delete
|
sudo find $BUILD_DIR/ -mindepth 1 -delete
|
||||||
echo "Starting build stage..."
|
echo "Starting build stage..."
|
||||||
echo "BUILD_DIR: ${BUILD_DIR}"
|
echo "Building from: ${{ env.UPSTREAM_REPO }} branch: ${{ github.event.inputs.upstream_branch }}"
|
||||||
echo "CI_DIR: ${CI_DIR}"
|
|
||||||
echo "VERSION: ${{ steps.set-env.outputs.version }}"
|
- name: Patch SConstruct to pass arbitrary cache
|
||||||
echo "UV_PROJECT_ENVIRONMENT: ${UV_PROJECT_ENVIRONMENT}"
|
run: |
|
||||||
echo "VIRTUAL_ENV: ${VIRTUAL_ENV}"
|
sed -i.bak 's#cache_dir =#default_cache_dir =#' ${{ github.workspace }}/SConstruct
|
||||||
echo "-------"
|
printf '/default_cache_dir/a\\\ncache_dir = ARGUMENTS.get("cache_dir", default_cache_dir)\n' | sed -i.bak -f - ${{ github.workspace }}/SConstruct
|
||||||
if [[ "${{ runner.debug }}" == "1" ]]; then
|
cat ${{ github.workspace }}/SConstruct
|
||||||
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
|
- name: Build Model
|
||||||
run: |
|
run: |
|
||||||
source /etc/profile
|
source /etc/profile
|
||||||
export UV_PROJECT_ENVIRONMENT=${HOME}/venv
|
export UV_PROJECT_ENVIRONMENT=${HOME}/venv
|
||||||
export VIRTUAL_ENV=$UV_PROJECT_ENVIRONMENT
|
export VIRTUAL_ENV=$UV_PROJECT_ENVIRONMENT
|
||||||
export PYTHONPATH="${PYTHONPATH}:${{ env.TINYGRAD_PATH }}"
|
scons -j$(nproc) cache_dir=${{ env.SCONS_CACHE_DIR }} ${{ github.workspace }}/selfdrive/modeld
|
||||||
|
|
||||||
# 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"
|
|
||||||
DEV=QCOM FLOAT16=1 NOLOCALS=1 JIT_BATCH_SIZE=0 python3 "${{ env.MODELS_DIR }}/../get_model_metadata.py" "$onnx_file" || true
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: Validate Model Outputs
|
|
||||||
run: |
|
|
||||||
source /etc/profile
|
|
||||||
export UV_PROJECT_ENVIRONMENT=${HOME}/venv
|
|
||||||
export VIRTUAL_ENV=$UV_PROJECT_ENVIRONMENT
|
|
||||||
python3 "${{ github.workspace }}/release/ci/model_generator.py" \
|
|
||||||
--validate-only \
|
|
||||||
--model-dir "${{ env.MODELS_DIR }}"
|
|
||||||
|
|
||||||
- name: Prepare Output
|
- name: Prepare Output
|
||||||
run: |
|
run: |
|
||||||
sudo rm -rf ${{ env.OUTPUT_DIR }}
|
sudo rm -rf ${OUTPUT_DIR}
|
||||||
mkdir -p ${{ env.OUTPUT_DIR }}
|
mkdir -p ${OUTPUT_DIR}
|
||||||
|
|
||||||
# Copy the model files
|
# Copy the model files
|
||||||
rsync -avm \
|
rsync -avm \
|
||||||
--include='*.dlc' \
|
--include='*.dlc' \
|
||||||
|
--include='*.thneed' \
|
||||||
--include='*.pkl' \
|
--include='*.pkl' \
|
||||||
--include='*.onnx' \
|
--include='*.onnx' \
|
||||||
--exclude='*' \
|
--exclude='*' \
|
||||||
--delete-excluded \
|
--delete-excluded \
|
||||||
--chown=comma:comma \
|
--chown=comma:comma \
|
||||||
${{ env.MODELS_DIR }}/ ${{ env.OUTPUT_DIR }}/
|
./selfdrive/modeld/models/ ${OUTPUT_DIR}/
|
||||||
|
|
||||||
python3 "${{ github.workspace }}/release/ci/model_generator.py" \
|
# Rename files if file_name is provided
|
||||||
--model-dir "${{ env.MODELS_DIR }}" \
|
if [ ! -z "${{ inputs.file_name }}" ]; then
|
||||||
--output-dir "${{ env.OUTPUT_DIR }}" \
|
mv ${OUTPUT_DIR}/supercombo.thneed ${OUTPUT_DIR}/supercombo-${{ inputs.file_name }}.thneed
|
||||||
--custom-name "${{ env.MODEL_NAME }}" \
|
mv ${OUTPUT_DIR}/supercombo_metadata.pkl ${OUTPUT_DIR}/supercombo-${{ inputs.file_name }}_metadata.pkl
|
||||||
--upstream-branch "${{ inputs.upstream_branch }}" \
|
fi
|
||||||
${{ inputs.is_20hz && '--is-20hz' || '' }}
|
|
||||||
|
# Calculate SHA256 hashes
|
||||||
- name: Write artifact name to file
|
HASH=$(sha256sum ${OUTPUT_DIR}/supercombo*.thneed | cut -d' ' -f1)
|
||||||
run: echo "model-${{ env.MODEL_NAME }}${{ inputs.artifact_suffix }}-${{ github.run_number }}" > ${{ env.OUTPUT_DIR }}/artifact_name.txt
|
METADATA_HASH=$(sha256sum ${OUTPUT_DIR}/supercombo*_metadata.pkl | cut -d' ' -f1)
|
||||||
|
|
||||||
|
# Create metadata.json
|
||||||
|
cat > ${OUTPUT_DIR}/metadata.json << EOF
|
||||||
|
{
|
||||||
|
"display_name": "${{ inputs.custom_name || inputs.upstream_branch }}",
|
||||||
|
"full_name": "${{ inputs.file_name || 'default' }}",
|
||||||
|
"is_20hz": ${{ inputs.is_20hz || false }},
|
||||||
|
"files": {
|
||||||
|
"drive_model": {
|
||||||
|
"file_name": "$(basename ${OUTPUT_DIR}/supercombo*.thneed)",
|
||||||
|
"sha256": "${HASH}"
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"file_name": "$(basename ${OUTPUT_DIR}/supercombo*_metadata.pkl)",
|
||||||
|
"sha256": "${METADATA_HASH}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ref": "${{ inputs.upstream_branch }}",
|
||||||
|
"build_time": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
||||||
|
}
|
||||||
|
|
||||||
- name: Upload Build Artifacts
|
- name: Upload Build Artifacts
|
||||||
id: upload-artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: model-${{ env.MODEL_NAME }}${{ inputs.artifact_suffix }}-${{ github.run_number }}
|
name: model-${{ github.event.inputs.custom_name || github.event.inputs.upstream_branch }}-${{ github.run_number }}
|
||||||
path: ${{ env.OUTPUT_DIR }}
|
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
|
|
||||||
|
|||||||
@@ -6,149 +6,97 @@ env:
|
|||||||
CI_DIR: ${{ github.workspace }}/release/ci
|
CI_DIR: ${{ github.workspace }}/release/ci
|
||||||
SCONS_CACHE_DIR: ${{ github.workspace }}/release/ci/scons_cache
|
SCONS_CACHE_DIR: ${{ github.workspace }}/release/ci/scons_cache
|
||||||
PUBLIC_REPO_URL: "https://github.com/sunnypilot/sunnypilot"
|
PUBLIC_REPO_URL: "https://github.com/sunnypilot/sunnypilot"
|
||||||
|
|
||||||
# Branch configurations
|
# Branch configurations
|
||||||
STAGING_SOURCE_BRANCH: 'master'
|
MASTER_BRANCH: "master"
|
||||||
|
MASTER_NEW_BRANCH: "master-new"
|
||||||
# Runtime configuration
|
DEV_C3_SOURCE_BRANCH: "master-dev-c3-new"
|
||||||
SOURCE_BRANCH: "${{ github.head_ref || github.ref_name }}"
|
|
||||||
|
# Target branch configurations
|
||||||
|
STAGING_TARGET_BRANCH: "staging-c3-new"
|
||||||
|
DEV_TARGET_BRANCH: "dev-c3-new"
|
||||||
|
RELEASE_TARGET_BRANCH: "release-c3-new"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ master, master-dev ]
|
branches: [ master, master-new, master-dev-c3-new ]
|
||||||
tags: [ 'release/*' ]
|
tags: [ '*' ]
|
||||||
pull_request_target:
|
|
||||||
types: [ labeled ]
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
wait_for_tests:
|
extra_version:
|
||||||
description: 'Wait for tests to finish'
|
description: 'Extra version identifier'
|
||||||
required: false
|
required: false
|
||||||
type: boolean
|
default: ''
|
||||||
default: false
|
|
||||||
|
|
||||||
jobs:
|
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:
|
build:
|
||||||
needs: [ validate_tests, prepare_strategy ]
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: build-${{ github.head_ref || github.ref_name }}
|
group: build-${{ github.head_ref || github.ref_name }}
|
||||||
cancel-in-progress: false
|
cancel-in-progress: false
|
||||||
runs-on: [self-hosted, tici]
|
runs-on: self-hosted
|
||||||
outputs:
|
outputs:
|
||||||
new_branch: ${{ needs.prepare_strategy.outputs.new_branch }}
|
new_branch: ${{ steps.set-env.outputs.new_branch }}
|
||||||
version: ${{ needs.prepare_strategy.outputs.version }}
|
version: ${{ steps.set-env.outputs.version }}
|
||||||
extra_version_identifier: ${{ needs.prepare_strategy.outputs.extra_version_identifier }}
|
extra_version_identifier: ${{ steps.set-env.outputs.extra_version_identifier }}
|
||||||
commit_sha: ${{ github.sha }}
|
commit_sha: ${{ steps.set-env.outputs.commit_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:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
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
|
- run: git lfs pull
|
||||||
|
|
||||||
- name: Cache SCons
|
- name: Cache SCons
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ${{env.SCONS_CACHE_DIR}}
|
path: ${{env.SCONS_CACHE_DIR}}
|
||||||
key: scons-${{ runner.os }}-${{ runner.arch }}-${{ env.SOURCE_BRANCH }}-${{ github.sha }}
|
key: scons-${{ runner.os }}-${{ runner.arch }}-${{ github.head_ref || github.ref_name }}-${{ github.sha }}
|
||||||
# Note: GitHub Actions enforces cache isolation between different build sources (PR builds, workflow dispatches, etc.)
|
# 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.
|
# for security. Only caches from the default branch are shared across all builds. This is by design and cannot be overridden.
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
scons-${{ runner.os }}-${{ runner.arch }}-${{ env.SOURCE_BRANCH }}
|
scons-${{ runner.os }}-${{ runner.arch }}-${{ github.head_ref || github.ref_name }}
|
||||||
scons-${{ runner.os }}-${{ runner.arch }}-${{ env.STAGING_SOURCE_BRANCH }}
|
scons-${{ runner.os }}-${{ runner.arch }}-${{ env.MASTER_NEW_BRANCH }}
|
||||||
|
scons-${{ runner.os }}-${{ runner.arch }}-${{ env.MASTER_BRANCH }}
|
||||||
scons-${{ runner.os }}-${{ runner.arch }}
|
scons-${{ runner.os }}-${{ runner.arch }}
|
||||||
|
|
||||||
|
- name: Set Configuration
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.ref_name }}" == "${{ env.DEV_C3_SOURCE_BRANCH }}" ]]; then
|
||||||
|
# Dev configuration
|
||||||
|
echo "BRANCH_TYPE=dev" >> $GITHUB_ENV
|
||||||
|
echo "NEW_BRANCH=${{ env.DEV_TARGET_BRANCH }}" >> $GITHUB_ENV
|
||||||
|
echo "VERSION=$(date '+%Y.%m.%d')-${{ github.run_number }}" >> $GITHUB_ENV
|
||||||
|
echo "EXTRA_VERSION_IDENTIFIER=${{ github.run_number }}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
elif [[ "${{ github.ref_name }}" == "${{ env.MASTER_BRANCH }}" || "${{ github.ref_name }}" == "${{ env.MASTER_NEW_BRANCH }}" ]]; then
|
||||||
|
# Master configuration
|
||||||
|
echo "BRANCH_TYPE=master" >> $GITHUB_ENV
|
||||||
|
echo "NEW_BRANCH=${{ env.STAGING_TARGET_BRANCH }}" >> $GITHUB_ENV
|
||||||
|
echo "EXTRA_VERSION_IDENTIFIER=staging" >> $GITHUB_ENV
|
||||||
|
echo "VERSION=$(cat common/version.h | grep COMMA_VERSION | sed -e 's/[^0-9|.]//g')-staging" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
elif [[ "${{ github.ref }}" == refs/tags/* ]]; then
|
||||||
|
# Tag configuration
|
||||||
|
echo "BRANCH_TYPE=tag" >> $GITHUB_ENV
|
||||||
|
echo "NEW_BRANCH=${{ env.RELEASE_TARGET_BRANCH }}" >> $GITHUB_ENV
|
||||||
|
echo "EXTRA_VERSION_IDENTIFIER=release" >> $GITHUB_ENV
|
||||||
|
echo "VERSION=$(cat common/version.h | grep COMMA_VERSION | sed -e 's/[^0-9|.]//g')-release" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
else
|
||||||
|
# Feature branch configuration
|
||||||
|
echo "BRANCH_TYPE=dispatch" >> $GITHUB_ENV
|
||||||
|
echo "NEW_BRANCH=${{ github.head_ref || github.ref_name }}-prebuilt" >> $GITHUB_ENV
|
||||||
|
echo "VERSION=$(date '+%Y.%m.%d')-${{ github.run_number }}" >> $GITHUB_ENV
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Set environment variables
|
- name: Set environment variables
|
||||||
id: set-env
|
id: set-env
|
||||||
run: |
|
run: |
|
||||||
echo "new_branch=${{ needs.prepare_strategy.outputs.new_branch }}" >> $GITHUB_OUTPUT
|
# Write to GITHUB_OUTPUT from environment variables
|
||||||
echo "version=${{ needs.prepare_strategy.outputs.version }}" >> $GITHUB_OUTPUT
|
echo "new_branch=$NEW_BRANCH" >> $GITHUB_OUTPUT
|
||||||
echo "extra_version_identifier=${{ needs.prepare_strategy.outputs.extra_version_identifier }}" >> $GITHUB_OUTPUT
|
[[ ! -z "$EXTRA_VERSION_IDENTIFIER" ]] && echo "extra_version_identifier=$EXTRA_VERSION_IDENTIFIER" >> $GITHUB_OUTPUT
|
||||||
|
[[ ! -z "$VERSION" ]] && echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
echo "commit_sha=${{ github.sha }}" >> $GITHUB_OUTPUT
|
echo "commit_sha=${{ github.sha }}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
# Set up common environment
|
# Set up common environment
|
||||||
source /etc/profile;
|
source /etc/profile;
|
||||||
export UV_PROJECT_ENVIRONMENT=${HOME}/venv
|
export UV_PROJECT_ENVIRONMENT=${HOME}/venv
|
||||||
@@ -174,19 +122,16 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
PYTHONPATH=$PYTHONPATH:${{ github.workspace }}/ ${{ github.workspace }}/scripts/manage-powersave.py --disable
|
PYTHONPATH=$PYTHONPATH:${{ github.workspace }}/ ${{ github.workspace }}/scripts/manage-powersave.py --disable
|
||||||
|
|
||||||
|
- name: Build Panda
|
||||||
|
run: |
|
||||||
|
scons -j$(nproc) cache_dir=${{env.SCONS_CACHE_DIR}} ${{ github.workspace }}/panda
|
||||||
|
|
||||||
- name: Build Main Project
|
- name: Build Main Project
|
||||||
run: |
|
run: |
|
||||||
export PYTHONPATH="$BUILD_DIR"
|
export PYTHONPATH="$BUILD_DIR"
|
||||||
./release/release_files.py | sort | uniq | rsync -rRl${RUNNER_DEBUG:+v} --files-from=- . $BUILD_DIR/
|
./release/release_files.py | sort | uniq | rsync -rRl${RUNNER_DEBUG:+v} --files-from=- . $BUILD_DIR/
|
||||||
cd $BUILD_DIR
|
cd $BUILD_DIR
|
||||||
sed -i '/from .board.jungle import PandaJungle, PandaJungleDFU/s/^/#/' panda/__init__.py
|
sed -i '/from .board.jungle import PandaJungle, PandaJungleDFU/s/^/#/' panda/__init__.py
|
||||||
echo "Building sunnypilot's modeld_v2..."
|
|
||||||
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
|
scons -j$(nproc) cache_dir=${{env.SCONS_CACHE_DIR}} --minimal
|
||||||
touch ${BUILD_DIR}/prebuilt
|
touch ${BUILD_DIR}/prebuilt
|
||||||
if [[ "${{ runner.debug }}" == "1" ]]; then
|
if [[ "${{ runner.debug }}" == "1" ]]; then
|
||||||
@@ -198,27 +143,37 @@ jobs:
|
|||||||
sudo rm -rf ${OUTPUT_DIR}
|
sudo rm -rf ${OUTPUT_DIR}
|
||||||
mkdir -p ${OUTPUT_DIR}
|
mkdir -p ${OUTPUT_DIR}
|
||||||
rsync -am${RUNNER_DEBUG:+v} \
|
rsync -am${RUNNER_DEBUG:+v} \
|
||||||
|
--include='**/panda/board/' \
|
||||||
|
--include='**/panda/board/obj' \
|
||||||
|
--include='**/panda/board/obj/panda.bin.signed' \
|
||||||
|
--include='**/panda/board/obj/panda_h7.bin.signed' \
|
||||||
|
--include='**/panda/board/obj/bootstub.panda.bin' \
|
||||||
|
--include='**/panda/board/obj/bootstub.panda_h7.bin' \
|
||||||
--exclude='.sconsign.dblite' \
|
--exclude='.sconsign.dblite' \
|
||||||
--exclude='*.a' \
|
--exclude='*.a' \
|
||||||
--exclude='*.o' \
|
--exclude='*.o' \
|
||||||
--exclude='*.os' \
|
--exclude='*.os' \
|
||||||
--exclude='*.pyc' \
|
--exclude='*.pyc' \
|
||||||
--exclude='moc_*' \
|
--exclude='moc_*' \
|
||||||
--exclude='__pycache__' \
|
--exclude='*.cc' \
|
||||||
--exclude='Jenkinsfile' \
|
--exclude='Jenkinsfile' \
|
||||||
|
--exclude='supercombo.onnx' \
|
||||||
|
--exclude='**/panda/board/*' \
|
||||||
|
--exclude='**/panda/board/obj/**' \
|
||||||
|
--exclude='**/panda/certs/' \
|
||||||
|
--exclude='**/panda/crypto/' \
|
||||||
--exclude='**/release/' \
|
--exclude='**/release/' \
|
||||||
--exclude='**/.github/' \
|
--exclude='**/.github/' \
|
||||||
--exclude='**/selfdrive/ui/replay/' \
|
--exclude='**/selfdrive/ui/replay/' \
|
||||||
--exclude='**/__pycache__/' \
|
--exclude='**/__pycache__/' \
|
||||||
|
--exclude='**/selfdrive/ui/*.h' \
|
||||||
|
--exclude='**/selfdrive/ui/**/*.h' \
|
||||||
|
--exclude='**/selfdrive/ui/qt/offroad/sunnypilot/' \
|
||||||
--exclude='${{env.SCONS_CACHE_DIR}}' \
|
--exclude='${{env.SCONS_CACHE_DIR}}' \
|
||||||
--exclude='**/.git/' \
|
--exclude='**/.git/' \
|
||||||
--exclude='**/SConstruct' \
|
--exclude='**/SConstruct' \
|
||||||
--exclude='**/SConscript' \
|
--exclude='**/SConscript' \
|
||||||
--exclude='**/.venv/' \
|
--exclude='**/.venv/' \
|
||||||
--exclude='selfdrive/modeld/models/driving_vision.onnx' \
|
|
||||||
--exclude='selfdrive/modeld/models/driving_policy.onnx' \
|
|
||||||
--exclude='third_party/*x86*' \
|
|
||||||
--exclude='third_party/*Darwin*' \
|
|
||||||
--delete-excluded \
|
--delete-excluded \
|
||||||
--chown=comma:comma \
|
--chown=comma:comma \
|
||||||
${BUILD_DIR}/ ${OUTPUT_DIR}/
|
${BUILD_DIR}/ ${OUTPUT_DIR}/
|
||||||
@@ -239,18 +194,14 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
PYTHONPATH=$PYTHONPATH:${{ github.workspace }}/ ${{ github.workspace }}/scripts/manage-powersave.py --enable
|
PYTHONPATH=$PYTHONPATH:${{ github.workspace }}/ ${{ github.workspace }}/scripts/manage-powersave.py --enable
|
||||||
|
|
||||||
|
|
||||||
publish:
|
publish:
|
||||||
concurrency:
|
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.
|
group: publish-${{ github.head_ref || github.ref_name }}
|
||||||
# This means that if multiple commits come in while we're publishing, they will be queued up and publish one after the other.
|
cancel-in-progress: true
|
||||||
# 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.
|
if: ${{ github.event_name != 'pull_request' || github.event_name == 'pull_request' && github.event.pull_request.draft }}
|
||||||
group: ${{ needs.prepare_strategy.outputs.publish_concurrency_group }}
|
needs: build
|
||||||
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
|
runs-on: ubuntu-24.04
|
||||||
environment: ${{ needs.prepare_strategy.outputs.environment }}
|
environment: ${{ contains(fromJSON(vars.AUTO_DEPLOY_PREBUILT_BRANCHES), github.head_ref || github.ref_name) && 'auto-deploy' || 'feature-branch' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
@@ -282,8 +233,8 @@ jobs:
|
|||||||
"${{ needs.build.outputs.new_branch }}" \
|
"${{ needs.build.outputs.new_branch }}" \
|
||||||
"${{ needs.build.outputs.version }}" \
|
"${{ needs.build.outputs.version }}" \
|
||||||
"https://x-access-token:${{github.token}}@github.com/sunnypilot/sunnypilot.git" \
|
"https://x-access-token:${{github.token}}@github.com/sunnypilot/sunnypilot.git" \
|
||||||
"${{ needs.build.outputs.extra_version_identifier }}"
|
"-${{ needs.build.outputs.extra_version_identifier }}"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "---- ℹ️ To update the list of branches that auto deploy prebuilts -----"
|
echo "---- ℹ️ To update the list of branches that auto deploy prebuilts -----"
|
||||||
echo ""
|
echo ""
|
||||||
@@ -291,79 +242,34 @@ jobs:
|
|||||||
echo "2. Current value: ${{ vars.AUTO_DEPLOY_PREBUILT_BRANCHES }}"
|
echo "2. Current value: ${{ vars.AUTO_DEPLOY_PREBUILT_BRANCHES }}"
|
||||||
echo "3. Update as needed (JSON array with no spaces)"
|
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:
|
notify:
|
||||||
needs:
|
needs: [ build, publish ]
|
||||||
- prepare_strategy
|
|
||||||
- build
|
|
||||||
- publish
|
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
if: ${{ (always() && !cancelled() && !failure())
|
if: success()
|
||||||
&& 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:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setup Alpine Linux environment
|
||||||
|
uses: jirutka/setup-alpine@v1.2.0
|
||||||
|
with:
|
||||||
|
packages: 'jq gettext curl'
|
||||||
|
|
||||||
- name: Prepare notification message
|
- name: Send Discord Notification
|
||||||
id: message
|
env:
|
||||||
|
DISCORD_WEBHOOK: ${{ contains(fromJSON(vars.DEV_FEEDBACK_NOTIFICATION_BRANCHES), github.head_ref || github.ref_name) && secrets.DISCORD_DEV_FEEDBACK_CHANNEL_WEBHOOK || secrets.DISCORD_DEV_PRIVATE_CHANNEL_WEBHOOK }}
|
||||||
run: |
|
run: |
|
||||||
TEMPLATE='${{ vars.DISCOURSE_GENERAL_UPDATE_NOTICE }}'
|
TEMPLATE='${{ vars.DISCORD_GENERAL_UPDATE_NOTICE }}'
|
||||||
export VERSION="${{ needs.prepare_strategy.outputs.version }}"
|
export EXTRA_VERSION_IDENTIFIER="${{ needs.build.outputs.extra_version_identifier }}"
|
||||||
export branch_name="${{ env.SOURCE_BRANCH }}"
|
export VERSION="${{ needs.build.outputs.version }}"
|
||||||
export new_branch="${{ needs.prepare_strategy.outputs.new_branch }}"
|
export branch_name=${{ github.head_ref || github.ref_name }}
|
||||||
export commit_sha="${{ github.sha }}"
|
export new_branch=${{ needs.build.outputs.new_branch }}
|
||||||
export commit_short_sha="${{ github.sha }}"
|
export extra_version_identifier=${{ needs.build.outputs.extra_version_identifier || github.run_number}}
|
||||||
export commit_short_sha="${commit_short_sha:0:7}"
|
echo ${TEMPLATE} | envsubst | jq -c '.' | tee payload.json
|
||||||
export extra_version_identifier="${{ needs.prepare_strategy.outputs.extra_version_identifier || github.run_number }}"
|
curl -X POST -H "Content-Type: application/json" -d @payload.json $DISCORD_WEBHOOK
|
||||||
export PUBLIC_REPO_URL="${{ env.PUBLIC_REPO_URL }}"
|
|
||||||
|
echo ""
|
||||||
MESSAGE=$(cat << 'EOF' | envsubst
|
echo "---- ℹ️ To update the list of branches that notify to dev-feedback -----"
|
||||||
${{ vars.DISCOURSE_GENERAL_UPDATE_NOTICE }}
|
echo ""
|
||||||
EOF
|
echo "1. Go to: ${{ github.server_url }}/${{ github.repository }}/settings/variables/actions/DEV_FEEDBACK_NOTIFICATION_BRANCHES"
|
||||||
)
|
echo "2. Current value: ${{ vars.DEV_FEEDBACK_NOTIFICATION_BRANCHES }}"
|
||||||
|
echo "3. Update as needed (JSON array with no spaces)"
|
||||||
{
|
shell: alpine.sh {0}
|
||||||
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}`);
|
|
||||||
+22
-83
@@ -1,65 +1,36 @@
|
|||||||
name: Build dev
|
name: Nightly Branch Reset and PR Squash
|
||||||
|
|
||||||
env:
|
env:
|
||||||
DEFAULT_SOURCE_BRANCH: "master"
|
DEFAULT_SOURCE_BRANCH: "master-new"
|
||||||
DEFAULT_TARGET_BRANCH: "master-dev"
|
DEFAULT_TARGET_BRANCH: "nightly"
|
||||||
|
PR_LABEL: "dev-c3"
|
||||||
LFS_URL: 'https://gitlab.com/sunnypilot/public/sunnypilot-new-lfs.git/info/lfs'
|
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'
|
LFS_PUSH_URL: 'ssh://git@gitlab.com/sunnypilot/public/sunnypilot-new-lfs.git'
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
pull_request_target:
|
|
||||||
types: [ labeled ]
|
|
||||||
branches:
|
|
||||||
- 'master'
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
source_branch:
|
source_branch:
|
||||||
description: 'Source branch to reset from'
|
description: 'Source branch to reset from'
|
||||||
required: true
|
required: true
|
||||||
default: 'master'
|
default: 'master-new'
|
||||||
type: string
|
type: string
|
||||||
target_branch:
|
target_branch:
|
||||||
description: 'Target branch to reset and squash into'
|
description: 'Target branch to reset and squash into'
|
||||||
required: true
|
required: true
|
||||||
default: 'master-dev'
|
default: 'master-dev-c3-new'
|
||||||
type: string
|
type: string
|
||||||
cancel_in_progress:
|
# schedule:
|
||||||
description: 'Cancel any in-progress runs of this workflow'
|
# - cron: '0 0 * * *' # Run at midnight UTC for nightly
|
||||||
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:
|
jobs:
|
||||||
reset-and-squash:
|
reset-and-squash:
|
||||||
runs-on: ubuntu-latest
|
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:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0 # Fetch all history for all branches
|
fetch-depth: 0 # Fetch all history for all branches
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
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
|
- name: Configure Git
|
||||||
run: |
|
run: |
|
||||||
@@ -118,25 +89,14 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
# Use GitHub API to get PRs with specific label, ordered by creation date
|
# Use GitHub API to get PRs with specific label, ordered by creation date
|
||||||
PR_LIST=$(gh api graphql -f query='
|
PR_LIST=$(gh api graphql -f query='
|
||||||
query($search_query:String!) {
|
query($label:String!) {
|
||||||
search(query: $search_query, type:ISSUE, first:40) {
|
search(query: $label, type:ISSUE, first:100) {
|
||||||
nodes {
|
nodes {
|
||||||
... on PullRequest {
|
... on PullRequest {
|
||||||
number
|
number
|
||||||
headRefName
|
headRefName
|
||||||
title
|
title
|
||||||
createdAt
|
createdAt
|
||||||
labels(last:10) {
|
|
||||||
nodes {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
headRepository {
|
|
||||||
name
|
|
||||||
nameWithOwner
|
|
||||||
url
|
|
||||||
isFork
|
|
||||||
}
|
|
||||||
commits(last: 1) {
|
commits(last: 1) {
|
||||||
nodes {
|
nodes {
|
||||||
commit {
|
commit {
|
||||||
@@ -149,9 +109,8 @@ jobs:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}' -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")
|
}' -F label="is:pr is:open label:${PR_LABEL} sort:created-asc")
|
||||||
|
|
||||||
PR_LIST=${PR_LIST//\'/}
|
|
||||||
echo "PR_LIST=${PR_LIST}" >> $GITHUB_OUTPUT
|
echo "PR_LIST=${PR_LIST}" >> $GITHUB_OUTPUT
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -174,38 +133,11 @@ jobs:
|
|||||||
echo ' pushurl = ${{ env.LFS_PUSH_URL }}' >> .lfsconfig
|
echo ' pushurl = ${{ env.LFS_PUSH_URL }}' >> .lfsconfig
|
||||||
echo ' locksverify = false' >> .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
|
- name: Push changes if there are diffs
|
||||||
id: push-changes
|
id: push-changes # Add an id so we can reference this step
|
||||||
run: |
|
run: |
|
||||||
TARGET_BRANCH="${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}"
|
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
|
# Fetch the latest from remote
|
||||||
git fetch origin $TARGET_BRANCH
|
git fetch origin $TARGET_BRANCH
|
||||||
|
|
||||||
@@ -216,7 +148,7 @@ jobs:
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Push with the authenticated origin
|
# If we get here, there are diffs, so push
|
||||||
if ! git push origin $TARGET_BRANCH --force; then
|
if ! git push origin $TARGET_BRANCH --force; then
|
||||||
echo "Failed to push changes to $TARGET_BRANCH"
|
echo "Failed to push changes to $TARGET_BRANCH"
|
||||||
exit 1
|
exit 1
|
||||||
@@ -229,15 +161,22 @@ jobs:
|
|||||||
if: steps.push-changes.outputs.has_changes == 'true'
|
if: steps.push-changes.outputs.has_changes == 'true'
|
||||||
run: |
|
run: |
|
||||||
echo "Triggering selfdrive tests..."
|
echo "Triggering selfdrive tests..."
|
||||||
gh workflow run tests.yaml --ref "${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}"
|
gh workflow run selfdrive_tests.yaml --ref "${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}"
|
||||||
|
|
||||||
echo "Sleeping for 120s to give plenty of time for the action to start and then we wait"
|
echo "Sleeping for 120s to give plenty of time for the action to start and then we wait"
|
||||||
sleep 120
|
sleep 120
|
||||||
|
|
||||||
echo "Getting latest run ID..."
|
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')
|
RUN_ID=$(gh run list --workflow=selfdrive_tests.yaml --branch="${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}" --limit=1 --json databaseId --jq '.[0].databaseId')
|
||||||
|
|
||||||
echo "Watching run ID: $RUN_ID"
|
echo "Watching run ID: $RUN_ID"
|
||||||
gh run watch "$RUN_ID"
|
gh run watch "$RUN_ID"
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Trigger prebuilt workflow
|
||||||
|
if: success() && steps.push-changes.outputs.has_changes == 'true'
|
||||||
|
run: |
|
||||||
|
gh workflow run sunnypilot-build-prebuilt.yaml --ref "${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}"
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
# Discourse Docs Sync — one-way push from docs_sp/ Markdown to Discourse API.
|
|
||||||
#
|
|
||||||
# WARNING: This workflow is strictly for Discourse API syncing.
|
|
||||||
# Do NOT add Zensical build steps, GitHub Pages deployment, or any
|
|
||||||
# static-site generation to this file. Those belong in docs.yaml.
|
|
||||||
|
|
||||||
name: Sync Docs to Discourse
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
# paths:
|
|
||||||
# - "docs_sp/**"
|
|
||||||
# - "zensical.toml"
|
|
||||||
pull_request:
|
|
||||||
# paths:
|
|
||||||
# - "docs_sp/**"
|
|
||||||
# - "zensical.toml"
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
smoke-test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup environment
|
|
||||||
run: ./tools/op.sh setup
|
|
||||||
|
|
||||||
- name: Smoke test - post one doc to Discourse
|
|
||||||
env:
|
|
||||||
DISCOURSE_URL: ${{ secrets.DISCOURSE_URL }}
|
|
||||||
DISCOURSE_API_KEY: ${{ secrets.DISCOURSE_API_KEY }}
|
|
||||||
DISCOURSE_API_USER: ${{ secrets.DISCOURSE_API_USER }}
|
|
||||||
DISCOURSE_CATEGORY_MAP: '{"getting-started": 133}'
|
|
||||||
run: |
|
|
||||||
uv run --python 3.12 python -c "
|
|
||||||
import os, sys
|
|
||||||
sys.path.insert(0, 'docs_sp/tools')
|
|
||||||
from pathlib import Path
|
|
||||||
from converter import convert
|
|
||||||
from discourse_client import DiscourseClient, DiscourseConfig
|
|
||||||
|
|
||||||
DOC_PATH = 'getting-started/what-is-sunnypilot.md'
|
|
||||||
GITHUB_BRANCH = os.environ.get('GITHUB_REF_NAME', 'master')
|
|
||||||
|
|
||||||
raw = (Path('docs_sp') / DOC_PATH).read_text()
|
|
||||||
body = convert(raw, file_path=DOC_PATH)
|
|
||||||
|
|
||||||
# Append sync footer
|
|
||||||
gh_url = f'https://github.com/sunnypilot/sunnypilot/blob/{GITHUB_BRANCH}/docs_sp/{DOC_PATH}'
|
|
||||||
body = body.rstrip('\n') + f'\n\n---\n<small>This document is version-controlled. Suggest changes [on GitHub]({gh_url}).</small>\n\n[^docs-sync]: docs-sync-id: {DOC_PATH}\n'
|
|
||||||
|
|
||||||
# Extract title from front matter or first heading
|
|
||||||
title = None
|
|
||||||
for line in raw.splitlines():
|
|
||||||
s = line.strip()
|
|
||||||
if s.startswith('title:'):
|
|
||||||
title = s[len('title:'):].strip().strip('\"').strip(\"'\")
|
|
||||||
break
|
|
||||||
if s.startswith('# '):
|
|
||||||
title = s[2:].strip()
|
|
||||||
break
|
|
||||||
title = f'{title or \"What is sunnypilot?\"} - sunnypilot Docs'
|
|
||||||
|
|
||||||
print(f'Title: {title}')
|
|
||||||
print(f'Body: {len(body)} chars')
|
|
||||||
|
|
||||||
config = DiscourseConfig.from_env()
|
|
||||||
client = DiscourseClient(config)
|
|
||||||
category_id = config.category_id_for(DOC_PATH)
|
|
||||||
print(f'Category ID: {category_id}')
|
|
||||||
|
|
||||||
existing = client.find_topic_by_sync_id(DOC_PATH)
|
|
||||||
if existing is not None:
|
|
||||||
topic_id = existing['id']
|
|
||||||
post_id = client.first_post_id(topic_id)
|
|
||||||
if post_id is None:
|
|
||||||
print(f'ERROR: No first post for topic {topic_id}')
|
|
||||||
sys.exit(1)
|
|
||||||
result = client.update_post(post_id, body, edit_reason='CI smoke test')
|
|
||||||
if result is None:
|
|
||||||
print('ERROR: Failed to update post')
|
|
||||||
sys.exit(1)
|
|
||||||
print(f'Updated: {config.base_url}/t/{topic_id}')
|
|
||||||
else:
|
|
||||||
result = client.create_topic(title=title, raw=body, category_id=category_id, tags=['docs-auto-sync'])
|
|
||||||
if result is None:
|
|
||||||
print('ERROR: Failed to create topic')
|
|
||||||
sys.exit(1)
|
|
||||||
print(f'Created: {config.base_url}/t/{result.get(\"topic_id\", \"?\")}')
|
|
||||||
|
|
||||||
print('Smoke test passed!')
|
|
||||||
"
|
|
||||||
@@ -1,238 +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:
|
|
||||||
CI: 1
|
|
||||||
PYTHONPATH: ${{ github.workspace }}
|
|
||||||
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"]')
|
|
||||||
|| fromJSON('["ubuntu-24.04"]') }}
|
|
||||||
env:
|
|
||||||
STRIPPED_DIR: /tmp/releasepilot
|
|
||||||
PYTHONPATH: /tmp/releasepilot
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
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
|
|
||||||
- run: ./tools/op.sh setup
|
|
||||||
- name: Build openpilot and run checks
|
|
||||||
timeout-minutes: 30
|
|
||||||
working-directory: ${{ env.STRIPPED_DIR }}
|
|
||||||
run: python3 system/manager/build.py
|
|
||||||
- name: Run tests
|
|
||||||
timeout-minutes: 1
|
|
||||||
working-directory: ${{ env.STRIPPED_DIR }}
|
|
||||||
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_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' }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
- name: Remove Homebrew from environment
|
|
||||||
run: |
|
|
||||||
FILTERED=$(echo "$PATH" | tr ':' '\n' | grep -v '/opt/homebrew' | tr '\n' ':')
|
|
||||||
echo "PATH=${FILTERED}/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin" >> $GITHUB_ENV
|
|
||||||
- run: ./tools/op.sh setup
|
|
||||||
- name: Building openpilot
|
|
||||||
run: scons
|
|
||||||
|
|
||||||
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"]')
|
|
||||||
|| fromJSON('["ubuntu-24.04"]') }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
- run: ./tools/op.sh setup
|
|
||||||
- name: Static analysis
|
|
||||||
timeout-minutes: 1
|
|
||||||
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"]')
|
|
||||||
|| fromJSON('["ubuntu-24.04"]') }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
- run: ./tools/op.sh setup
|
|
||||||
- name: Build openpilot
|
|
||||||
run: scons -j$(nproc)
|
|
||||||
- name: Run unit tests
|
|
||||||
timeout-minutes: ${{ contains(runner.name, 'nsc') && 2 || 999 }}
|
|
||||||
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'
|
|
||||||
|
|
||||||
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"]')
|
|
||||||
|| fromJSON('["ubuntu-24.04"]') }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
- run: ./tools/op.sh setup
|
|
||||||
- name: Build openpilot
|
|
||||||
run: scons -j$(nproc)
|
|
||||||
- name: Run replay
|
|
||||||
timeout-minutes: ${{ contains(runner.name, 'nsc') && 2 || 20 }}
|
|
||||||
continue-on-error: ${{ github.ref == 'refs/heads/master' }}
|
|
||||||
run: selfdrive/test/process_replay/test_processes.py -j$(nproc)
|
|
||||||
- name: Print diff
|
|
||||||
id: print-diff
|
|
||||||
if: always()
|
|
||||||
run: cat selfdrive/test/process_replay/diff.txt
|
|
||||||
- uses: actions/upload-artifact@v6
|
|
||||||
if: always()
|
|
||||||
continue-on-error: true
|
|
||||||
with:
|
|
||||||
name: process_replay_diff.txt
|
|
||||||
path: selfdrive/test/process_replay/diff.txt
|
|
||||||
- name: Checkout ci-artifacts
|
|
||||||
if: github.repository == 'commaai/openpilot' && github.ref == 'refs/heads/master'
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
repository: commaai/ci-artifacts
|
|
||||||
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
|
||||||
path: ${{ github.workspace }}/ci-artifacts
|
|
||||||
- name: Push refs
|
|
||||||
if: github.repository == 'commaai/openpilot' && github.ref == 'refs/heads/master'
|
|
||||||
working-directory: ${{ github.workspace }}/ci-artifacts
|
|
||||||
run: |
|
|
||||||
git config user.name "GitHub Actions Bot"
|
|
||||||
git config user.email "<>"
|
|
||||||
git fetch origin process-replay || true
|
|
||||||
git checkout process-replay 2>/dev/null || git checkout --orphan process-replay
|
|
||||||
cp ${{ github.workspace }}/selfdrive/test/process_replay/fakedata/*.zst .
|
|
||||||
echo "${{ github.sha }}" > ref_commit
|
|
||||||
git add .
|
|
||||||
git commit -m "process-replay refs for ${{ github.repository }}@${{ github.sha }}" || echo "No changes to commit"
|
|
||||||
git push origin process-replay
|
|
||||||
- name: Run regen
|
|
||||||
if: false
|
|
||||||
timeout-minutes: 4
|
|
||||||
env:
|
|
||||||
ONNXCPU: 1
|
|
||||||
run: $PYTEST selfdrive/test/process_replay/test_regen.py
|
|
||||||
|
|
||||||
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"]')
|
|
||||||
|| fromJSON('["ubuntu-24.04"]') }}
|
|
||||||
if: false # FIXME: Started to timeout recently
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
- run: ./tools/op.sh setup
|
|
||||||
- name: Build openpilot
|
|
||||||
run: scons -j$(nproc)
|
|
||||||
- name: Driving test
|
|
||||||
timeout-minutes: 2
|
|
||||||
run: |
|
|
||||||
source selfdrive/test/setup_xvfb.sh
|
|
||||||
pytest -s tools/sim/tests/test_metadrive_bridge.py
|
|
||||||
|
|
||||||
create_ui_report:
|
|
||||||
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'))
|
|
||||||
&& fromJSON('["namespace-profile-amd64-8x16"]')
|
|
||||||
|| fromJSON('["ubuntu-24.04"]') }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
- run: ./tools/op.sh setup
|
|
||||||
- name: Build openpilot
|
|
||||||
run: scons -j$(nproc)
|
|
||||||
- name: Create UI Report
|
|
||||||
run: |
|
|
||||||
source selfdrive/test/setup_xvfb.sh
|
|
||||||
python3 selfdrive/ui/tests/diff/replay.py
|
|
||||||
python3 selfdrive/ui/tests/diff/replay.py --big
|
|
||||||
- name: Upload UI Report
|
|
||||||
uses: actions/upload-artifact@v6
|
|
||||||
with:
|
|
||||||
name: ui-report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
|
||||||
path: selfdrive/ui/tests/diff/report
|
|
||||||
@@ -3,25 +3,21 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
- master-new
|
||||||
pull_request_target:
|
pull_request_target:
|
||||||
types: [assigned, opened, synchronize, reopened, edited]
|
types: [assigned, opened, synchronize, reopened, edited]
|
||||||
branches:
|
branches:
|
||||||
- 'master'
|
- 'master'
|
||||||
|
- 'master-new'
|
||||||
paths:
|
paths:
|
||||||
- 'selfdrive/assets/**'
|
|
||||||
- 'selfdrive/ui/**'
|
- 'selfdrive/ui/**'
|
||||||
- 'system/ui/**'
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
UI_JOB_NAME: "Create UI Report"
|
UI_JOB_NAME: "Create UI Report"
|
||||||
REPORT_NAME: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
REPORT_NAME: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/master-new') && 'master' || github.event.number }}
|
||||||
SHA: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.sha || github.event.pull_request.head.sha }}
|
SHA: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/master-new') && github.sha || github.event.pull_request.head.sha }}
|
||||||
BRANCH_NAME: "openpilot/pr-${{ github.event.number }}-ui-preview"
|
BRANCH_NAME: "openpilot/pr-${{ github.event.number }}"
|
||||||
REPORT_FILES_BRANCH_NAME: "mici-raylib-ui-reports"
|
|
||||||
|
|
||||||
# variant:video_prefix:master_branch
|
|
||||||
VARIANTS: "mici:mici_ui_replay:openpilot_master_ui_mici_raylib big:tizi_ui_replay:openpilot_master_ui_big_raylib"
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
preview:
|
preview:
|
||||||
@@ -34,9 +30,8 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
actions: read
|
actions: read
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- name: Waiting for ui generation to start
|
||||||
with:
|
run: sleep 30
|
||||||
submodules: true
|
|
||||||
|
|
||||||
- name: Waiting for ui generation to end
|
- name: Waiting for ui generation to end
|
||||||
uses: lewagon/wait-on-check-action@v1.3.4
|
uses: lewagon/wait-on-check-action@v1.3.4
|
||||||
@@ -53,93 +48,110 @@ jobs:
|
|||||||
echo "run_id=$(curl https://api.github.com/repos/${{ github.repository }}/commits/${{ env.SHA }}/check-runs | jq -r '.check_runs[] | select(.name == "${{ env.UI_JOB_NAME }}") | .html_url | capture("(?<number>[0-9]+)") | .number')" >> $GITHUB_OUTPUT
|
echo "run_id=$(curl https://api.github.com/repos/${{ github.repository }}/commits/${{ env.SHA }}/check-runs | jq -r '.check_runs[] | select(.name == "${{ env.UI_JOB_NAME }}") | .html_url | capture("(?<number>[0-9]+)") | .number')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Getting proposed ui
|
- name: Getting proposed ui
|
||||||
|
id: download-artifact
|
||||||
uses: dawidd6/action-download-artifact@v6
|
uses: dawidd6/action-download-artifact@v6
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run_id: ${{ steps.get_run_id.outputs.run_id }}
|
run_id: ${{ steps.get_run_id.outputs.run_id }}
|
||||||
search_artifacts: true
|
search_artifacts: true
|
||||||
name: ui-report-1-${{ env.REPORT_NAME }}
|
name: report-1-${{ env.REPORT_NAME }}
|
||||||
path: ${{ github.workspace }}/pr_ui
|
path: ${{ github.workspace }}/pr_ui
|
||||||
|
|
||||||
- name: Getting mici master ui
|
- name: Getting master ui
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: sunnypilot/ci-artifacts
|
repository: sunnypilot/ci-artifacts
|
||||||
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
||||||
path: ${{ github.workspace }}/master_mici
|
path: ${{ github.workspace }}/master_ui
|
||||||
ref: openpilot_master_ui_mici_raylib
|
ref: openpilot_master_ui
|
||||||
|
|
||||||
- name: Getting big master ui
|
|
||||||
uses: actions/checkout@v6
|
|
||||||
with:
|
|
||||||
repository: sunnypilot/ci-artifacts
|
|
||||||
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
|
||||||
path: ${{ github.workspace }}/master_big
|
|
||||||
ref: openpilot_master_ui_big_raylib
|
|
||||||
|
|
||||||
- name: Saving new master ui
|
- name: Saving new master ui
|
||||||
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
|
if: (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/master-new') && github.event_name == 'push'
|
||||||
|
working-directory: ${{ github.workspace }}/master_ui
|
||||||
run: |
|
run: |
|
||||||
for variant in $VARIANTS; do
|
git checkout --orphan=new_master_ui
|
||||||
IFS=':' read -r name video branch <<< "$variant"
|
git rm -rf *
|
||||||
master_dir="${{ github.workspace }}/master_${name}"
|
git branch -D openpilot_master_ui
|
||||||
cd "$master_dir"
|
git branch -m openpilot_master_ui
|
||||||
git checkout --orphan=new_branch
|
git config user.name "GitHub Actions Bot"
|
||||||
git rm -rf *
|
git config user.email "<>"
|
||||||
git branch -D "$branch"
|
mv ${{ github.workspace }}/pr_ui/*.png .
|
||||||
git branch -m "$branch"
|
git add .
|
||||||
git config user.name "GitHub Actions Bot"
|
git commit -m "screenshots for commit ${{ env.SHA }}"
|
||||||
git config user.email "<>"
|
git push origin openpilot_master_ui --force
|
||||||
cp "${{ github.workspace }}/pr_ui/${video}.mp4" .
|
|
||||||
git add .
|
|
||||||
git commit -m "${name} video for commit ${{ env.SHA }}"
|
|
||||||
git push origin "$branch" --force
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: Setup FFmpeg
|
- name: Finding diff
|
||||||
uses: AnimMouse/setup-ffmpeg@ae28d57dabbb148eff63170b6bf7f2b60062cbae
|
|
||||||
|
|
||||||
- name: Finding diffs
|
|
||||||
if: github.event_name == 'pull_request_target'
|
if: github.event_name == 'pull_request_target'
|
||||||
id: find_diff
|
id: find_diff
|
||||||
run: |
|
run: >-
|
||||||
export PYTHONPATH=${{ github.workspace }}
|
sudo apt-get update && sudo apt-get install -y imagemagick
|
||||||
baseurl="https://github.com/sunnypilot/ci-artifacts/raw/refs/heads/${{ env.BRANCH_NAME }}"
|
|
||||||
|
|
||||||
COMMENT=""
|
scenes=$(find ${{ github.workspace }}/pr_ui/*.png -type f -printf "%f\n" | cut -d '.' -f 1 | grep -v 'pair_device')
|
||||||
for variant in $VARIANTS; do
|
A=($scenes)
|
||||||
IFS=':' read -r name video _ <<< "$variant"
|
|
||||||
diff_name="${name}_diff"
|
|
||||||
|
|
||||||
mv "${{ github.workspace }}/pr_ui/${video}.mp4" "${{ github.workspace }}/pr_ui/${video}_proposed.mp4"
|
DIFF=""
|
||||||
cp "${{ github.workspace }}/master_${name}/${video}.mp4" "${{ github.workspace }}/pr_ui/${video}_master.mp4"
|
TABLE="<details><summary>All Screenshots</summary>"
|
||||||
|
TABLE="${TABLE}<table>"
|
||||||
|
|
||||||
diff_exit_code=0
|
for ((i=0; i<${#A[*]}; i=i+1));
|
||||||
python3 ${{ github.workspace }}/selfdrive/ui/tests/diff/diff.py \
|
do
|
||||||
"${{ github.workspace }}/pr_ui/${video}_master.mp4" \
|
# Check if the master file exists
|
||||||
"${{ github.workspace }}/pr_ui/${video}_proposed.mp4" \
|
if [ ! -f "${{ github.workspace }}/master_ui/${A[$i]}.png" ]; then
|
||||||
"${diff_name}.html" --basedir "$baseurl" --no-open || diff_exit_code=$?
|
# 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>"
|
||||||
|
|
||||||
cp "${{ github.workspace }}/selfdrive/ui/tests/diff/report/${diff_name}.html" "${{ github.workspace }}/pr_ui/"
|
DIFF="${DIFF}<tr>"
|
||||||
cp "${{ github.workspace }}/selfdrive/ui/tests/diff/report/${diff_name}.mp4" "${{ github.workspace }}/pr_ui/"
|
DIFF="${DIFF} <td> <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}.png\"> </td>"
|
||||||
|
DIFF="${DIFF}</tr>"
|
||||||
|
|
||||||
REPORT_URL="https://sunnypilot.github.io/ci-artifacts/${diff_name}_pr_${{ github.event.number }}.html"
|
DIFF="${DIFF}</table>"
|
||||||
if [ $diff_exit_code -eq 0 ]; then
|
DIFF="${DIFF}</details>"
|
||||||
COMMENT+="**${name}**: Videos are identical! [View Diff Report]($REPORT_URL)"$'\n'
|
elif ! 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/${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/${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}</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}</tr>"
|
||||||
|
|
||||||
|
DIFF="${DIFF}</table>"
|
||||||
|
DIFF="${DIFF}</details>"
|
||||||
else
|
else
|
||||||
COMMENT+="**${name}**: ⚠️ <strong>Videos differ!</strong> [View Diff Report]($REPORT_URL)"$'\n'
|
rm -f ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png
|
||||||
|
fi
|
||||||
|
|
||||||
|
INDEX=$(($i % 2))
|
||||||
|
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>"
|
||||||
|
if [[ $INDEX -eq 1 || $(($i + 1)) -eq ${#A[*]} ]]; then
|
||||||
|
TABLE="${TABLE}</tr>"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
{
|
TABLE="${TABLE}</table></details>"
|
||||||
echo "COMMENT<<EOF"
|
|
||||||
echo "$COMMENT"
|
echo "DIFF=$DIFF$TABLE" >> "$GITHUB_OUTPUT"
|
||||||
echo "EOF"
|
|
||||||
} >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
- name: Saving proposed ui
|
- name: Saving proposed ui
|
||||||
if: github.event_name == 'pull_request_target'
|
if: github.event_name == 'pull_request_target'
|
||||||
working-directory: ${{ github.workspace }}/master_mici
|
working-directory: ${{ github.workspace }}/master_ui
|
||||||
run: |
|
run: |
|
||||||
git config user.name "GitHub Actions Bot"
|
git config user.name "GitHub Actions Bot"
|
||||||
git config user.email "<>"
|
git config user.email "<>"
|
||||||
@@ -147,29 +159,17 @@ jobs:
|
|||||||
git rm -rf *
|
git rm -rf *
|
||||||
mv ${{ github.workspace }}/pr_ui/* .
|
mv ${{ github.workspace }}/pr_ui/* .
|
||||||
git add .
|
git add .
|
||||||
git commit -m "ui videos for PR #${{ github.event.number }}"
|
git commit -m "screenshots for PR #${{ github.event.number }}"
|
||||||
git push origin ${{ env.BRANCH_NAME }} --force
|
git push origin ${{ env.BRANCH_NAME }} --force
|
||||||
|
|
||||||
# Append diff reports to report files branch
|
- name: Comment Screenshots on PR
|
||||||
git fetch origin ${{ env.REPORT_FILES_BRANCH_NAME }}
|
|
||||||
git checkout ${{ env.REPORT_FILES_BRANCH_NAME }}
|
|
||||||
for variant in $VARIANTS; do
|
|
||||||
IFS=':' read -r name _ _ <<< "$variant"
|
|
||||||
diff_name="${name}_diff"
|
|
||||||
cp "${{ github.workspace }}/selfdrive/ui/tests/diff/report/${diff_name}.html" "${diff_name}_pr_${{ github.event.number }}.html"
|
|
||||||
git add "${diff_name}_pr_${{ github.event.number }}.html"
|
|
||||||
done
|
|
||||||
git commit -m "ui diff reports for PR #${{ github.event.number }}" || echo "No changes to commit"
|
|
||||||
git push origin ${{ env.REPORT_FILES_BRANCH_NAME }}
|
|
||||||
|
|
||||||
- name: Comment on PR
|
|
||||||
if: github.event_name == 'pull_request_target'
|
if: github.event_name == 'pull_request_target'
|
||||||
uses: thollander/actions-comment-pull-request@v2
|
uses: thollander/actions-comment-pull-request@v2
|
||||||
with:
|
with:
|
||||||
message: |
|
message: |
|
||||||
<!-- _(run_id_ui_preview **${{ github.run_id }}**)_ -->
|
<!-- _(run_id_screenshots **${{ github.run_id }}**)_ -->
|
||||||
## UI Preview
|
## UI Preview
|
||||||
${{ steps.find_diff.outputs.COMMENT }}
|
${{ steps.find_diff.outputs.DIFF }}
|
||||||
comment_tag: run_id_ui_preview
|
comment_tag: run_id_screenshots
|
||||||
pr_number: ${{ github.event.number }}
|
pr_number: ${{ github.event.number }}
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
@@ -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 }}
|
|
||||||
+18
-13
@@ -10,15 +10,13 @@ venv/
|
|||||||
.overlay_init
|
.overlay_init
|
||||||
.overlay_consistent
|
.overlay_consistent
|
||||||
.sconsign.dblite
|
.sconsign.dblite
|
||||||
|
model2.png
|
||||||
a.out
|
a.out
|
||||||
.hypothesis
|
.hypothesis
|
||||||
.cache/
|
|
||||||
|
|
||||||
/docs_site/
|
/docs_site/
|
||||||
/docs_site_sp/
|
/docs_sp_site/
|
||||||
/.discourse_sync_cache/
|
|
||||||
|
|
||||||
*.mp4
|
|
||||||
*.dylib
|
*.dylib
|
||||||
*.DSYM
|
*.DSYM
|
||||||
*.d
|
*.d
|
||||||
@@ -38,23 +36,32 @@ a.out
|
|||||||
*.class
|
*.class
|
||||||
*.pyxbldc
|
*.pyxbldc
|
||||||
*.vcd
|
*.vcd
|
||||||
*.mo
|
*.qm
|
||||||
*_pyx.cpp
|
*_pyx.cpp
|
||||||
*.stats
|
|
||||||
config.json
|
config.json
|
||||||
clcache
|
clcache
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
compare_runtime*.html
|
compare_runtime*.html
|
||||||
|
|
||||||
|
persist
|
||||||
selfdrive/pandad/pandad
|
selfdrive/pandad/pandad
|
||||||
cereal/services.h
|
cereal/services.h
|
||||||
cereal/gen
|
cereal/gen
|
||||||
cereal/messaging/bridge
|
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/ui/translations/tmp
|
||||||
|
selfdrive/test/longitudinal_maneuvers/out
|
||||||
selfdrive/car/tests/cars_dump
|
selfdrive/car/tests/cars_dump
|
||||||
system/camerad/camerad
|
system/camerad/camerad
|
||||||
system/camerad/test/ae_gray_test
|
system/camerad/test/ae_gray_test
|
||||||
|
|
||||||
|
notebooks
|
||||||
|
hyperthneed
|
||||||
|
provisioning
|
||||||
|
|
||||||
.coverage*
|
.coverage*
|
||||||
coverage.xml
|
coverage.xml
|
||||||
htmlcov
|
htmlcov
|
||||||
@@ -66,10 +73,13 @@ flycheck_*
|
|||||||
cppcheck_report.txt
|
cppcheck_report.txt
|
||||||
comma*.sh
|
comma*.sh
|
||||||
|
|
||||||
selfdrive/modeld/models/*.pkl*
|
selfdrive/modeld/thneed/compile
|
||||||
|
selfdrive/modeld/models/*.thneed
|
||||||
|
selfdrive/modeld/models/*.pkl
|
||||||
|
sunnypilot/modeld*/thneed/compile
|
||||||
|
sunnypilot/modeld*/models/*.thneed
|
||||||
sunnypilot/modeld*/models/*.pkl
|
sunnypilot/modeld*/models/*.pkl
|
||||||
|
|
||||||
# openpilot log files
|
|
||||||
*.bz2
|
*.bz2
|
||||||
*.zst
|
*.zst
|
||||||
|
|
||||||
@@ -99,11 +109,6 @@ Pipfile
|
|||||||
.history
|
.history
|
||||||
.ionide
|
.ionide
|
||||||
|
|
||||||
.claude/
|
|
||||||
.context/
|
|
||||||
PLAN.md
|
|
||||||
TASK.md
|
|
||||||
|
|
||||||
### JetBrains ###
|
### JetBrains ###
|
||||||
!.idea/customTargets.xml
|
!.idea/customTargets.xml
|
||||||
!.idea/tools/*
|
!.idea/tools/*
|
||||||
|
|||||||
+2
-2
@@ -6,7 +6,7 @@
|
|||||||
url = https://github.com/sunnypilot/opendbc.git
|
url = https://github.com/sunnypilot/opendbc.git
|
||||||
[submodule "msgq"]
|
[submodule "msgq"]
|
||||||
path = msgq_repo
|
path = msgq_repo
|
||||||
url = https://github.com/commaai/msgq.git
|
url = https://github.com/sunnypilot/msgq.git
|
||||||
[submodule "rednose_repo"]
|
[submodule "rednose_repo"]
|
||||||
path = rednose_repo
|
path = rednose_repo
|
||||||
url = https://github.com/commaai/rednose.git
|
url = https://github.com/commaai/rednose.git
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
url = https://github.com/commaai/teleoprtc
|
url = https://github.com/commaai/teleoprtc
|
||||||
[submodule "tinygrad"]
|
[submodule "tinygrad"]
|
||||||
path = tinygrad_repo
|
path = tinygrad_repo
|
||||||
url = https://github.com/sunnypilot/tinygrad.git
|
url = https://github.com/commaai/tinygrad.git
|
||||||
[submodule "sunnypilot/neural_network_data"]
|
[submodule "sunnypilot/neural_network_data"]
|
||||||
path = sunnypilot/neural_network_data
|
path = sunnypilot/neural_network_data
|
||||||
url = https://github.com/sunnypilot/neural-network-data.git
|
url = https://github.com/sunnypilot/neural-network-data.git
|
||||||
|
|||||||
Generated
+2
-2
@@ -2,7 +2,7 @@
|
|||||||
<tool name="uv Scons Build Debug" showInMainMenu="false" showInEditor="false" showInProject="false" showInSearchPopup="false" disabled="false" useConsole="true" showConsoleOnStdOut="false" showConsoleOnStdErr="false" synchronizeAfterRun="true">
|
<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>
|
<exec>
|
||||||
<option name="COMMAND" value="bash" />
|
<option name="COMMAND" value="bash" />
|
||||||
<option name="PARAMETERS" value="-c "source .venv/bin/activate && scons -u -j$(nproc) --ccflags=\"-fno-inline\""" />
|
<option name="PARAMETERS" value="-c "source .venv/bin/activate && scons -u -j$(nproc) --compile_db --ccflags=\"-fno-inline\""" />
|
||||||
<option name="WORKING_DIRECTORY" value="$ProjectFileDir$" />
|
<option name="WORKING_DIRECTORY" value="$ProjectFileDir$" />
|
||||||
</exec>
|
</exec>
|
||||||
</tool>
|
</tool>
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
<tool name="uv Scons Build Release" showInMainMenu="false" showInEditor="false" showInProject="false" showInSearchPopup="false" disabled="false" useConsole="true" showConsoleOnStdOut="false" showConsoleOnStdErr="false" synchronizeAfterRun="true">
|
<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>
|
<exec>
|
||||||
<option name="COMMAND" value="bash" />
|
<option name="COMMAND" value="bash" />
|
||||||
<option name="PARAMETERS" value="-c "source .venv/bin/activate && scons -u -j$(nproc)" " />
|
<option name="PARAMETERS" value="-c "source .venv/bin/activate && scons -u -j$(nproc) --compile_db" " />
|
||||||
<option name="WORKING_DIRECTORY" value="$ProjectFileDir$" />
|
<option name="WORKING_DIRECTORY" value="$ProjectFileDir$" />
|
||||||
</exec>
|
</exec>
|
||||||
</tool>
|
</tool>
|
||||||
|
|||||||
@@ -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>
|
|
||||||
Vendored
+1
-40
@@ -23,11 +23,6 @@
|
|||||||
"id": "args",
|
"id": "args",
|
||||||
"description": "Arguments to pass to the process",
|
"description": "Arguments to pass to the process",
|
||||||
"type": "promptString"
|
"type": "promptString"
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "replayArg",
|
|
||||||
"type": "promptString",
|
|
||||||
"description": "Enter route or segment to replay."
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"configurations": [
|
"configurations": [
|
||||||
@@ -45,41 +40,7 @@
|
|||||||
"type": "cppdbg",
|
"type": "cppdbg",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${workspaceFolder}/${input:cpp_process}",
|
"program": "${workspaceFolder}/${input:cpp_process}",
|
||||||
"cwd": "${workspaceFolder}"
|
"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"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
+5
-30
@@ -1,38 +1,13 @@
|
|||||||
FROM ubuntu:24.04
|
FROM ghcr.io/commaai/openpilot-base:latest
|
||||||
|
|
||||||
ENV PYTHONUNBUFFERED=1
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
ENV OPENPILOT_PATH=/home/batman/openpilot
|
||||||
RUN apt-get update && \
|
ENV PYTHONPATH=${OPENPILOT_PATH}:${PYTHONPATH}
|
||||||
apt-get install -y --no-install-recommends sudo tzdata locales && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
|
|
||||||
ENV LANG=en_US.UTF-8
|
|
||||||
ENV LANGUAGE=en_US:en
|
|
||||||
ENV LC_ALL=en_US.UTF-8
|
|
||||||
|
|
||||||
ENV NVIDIA_VISIBLE_DEVICES=all
|
|
||||||
ENV NVIDIA_DRIVER_CAPABILITIES=graphics,utility,compute
|
|
||||||
|
|
||||||
ARG USER=batman
|
|
||||||
ARG USER_UID=1001
|
|
||||||
RUN useradd -m -s /bin/bash -u $USER_UID $USER
|
|
||||||
RUN usermod -aG sudo $USER
|
|
||||||
RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
|
|
||||||
USER $USER
|
|
||||||
|
|
||||||
ENV OPENPILOT_PATH=/home/$USER/openpilot
|
|
||||||
RUN mkdir -p ${OPENPILOT_PATH}
|
RUN mkdir -p ${OPENPILOT_PATH}
|
||||||
WORKDIR ${OPENPILOT_PATH}
|
WORKDIR ${OPENPILOT_PATH}
|
||||||
|
|
||||||
COPY --chown=$USER . ${OPENPILOT_PATH}/
|
COPY . ${OPENPILOT_PATH}/
|
||||||
|
|
||||||
ENV UV_BIN="/home/$USER/.local/bin/"
|
RUN scons --cache-readonly -j$(nproc)
|
||||||
ENV VIRTUAL_ENV=${OPENPILOT_PATH}/.venv
|
|
||||||
ENV PATH="$UV_BIN:$VIRTUAL_ENV/bin:$PATH"
|
|
||||||
RUN tools/setup_dependencies.sh && \
|
|
||||||
sudo rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
USER root
|
|
||||||
RUN git config --global --add safe.directory '*'
|
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
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 \
|
||||||
|
&& 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
|
||||||
|
|
||||||
|
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
|
||||||
Vendored
+38
-17
@@ -22,7 +22,7 @@ shopt -s huponexit # kill all child processes when the shell exits
|
|||||||
|
|
||||||
export CI=1
|
export CI=1
|
||||||
export PYTHONWARNINGS=error
|
export PYTHONWARNINGS=error
|
||||||
#export LOGPRINT=debug # this has gotten too spammy...
|
export LOGPRINT=debug
|
||||||
export TEST_DIR=${env.TEST_DIR}
|
export TEST_DIR=${env.TEST_DIR}
|
||||||
export SOURCE_DIR=${env.SOURCE_DIR}
|
export SOURCE_DIR=${env.SOURCE_DIR}
|
||||||
export GIT_BRANCH=${env.GIT_BRANCH}
|
export GIT_BRANCH=${env.GIT_BRANCH}
|
||||||
@@ -167,7 +167,7 @@ node {
|
|||||||
env.GIT_COMMIT = checkout(scm).GIT_COMMIT
|
env.GIT_COMMIT = checkout(scm).GIT_COMMIT
|
||||||
|
|
||||||
def excludeBranches = ['__nightly', 'devel', 'devel-staging', 'release3', 'release3-staging',
|
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('\\*', '.*')
|
def excludeRegex = excludeBranches.join('|').replaceAll('\\*', '.*')
|
||||||
|
|
||||||
if (env.BRANCH_NAME != 'master' && !env.BRANCH_NAME.contains('__jenkins_loop_')) {
|
if (env.BRANCH_NAME != 'master' && !env.BRANCH_NAME.contains('__jenkins_loop_')) {
|
||||||
@@ -178,20 +178,20 @@ node {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (env.BRANCH_NAME == 'devel-staging') {
|
if (env.BRANCH_NAME == 'devel-staging') {
|
||||||
deviceStage("build release-tizi-staging", "tizi-needs-can", [], [
|
deviceStage("build release3-staging", "tici-needs-can", [], [
|
||||||
step("build release-tizi-staging", "RELEASE_BRANCH=release-tizi-staging $SOURCE_DIR/release/build_release.sh"),
|
step("build release3-staging", "RELEASE_BRANCH=release3-staging $SOURCE_DIR/release/build_release.sh"),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (env.BRANCH_NAME == '__nightly') {
|
if (env.BRANCH_NAME == '__nightly') {
|
||||||
parallel (
|
parallel (
|
||||||
'nightly': {
|
'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"),
|
step("build nightly", "RELEASE_BRANCH=nightly $SOURCE_DIR/release/build_release.sh"),
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
'nightly-dev': {
|
'nightly-dev': {
|
||||||
deviceStage("build nightly-dev", "tizi-needs-can", [], [
|
deviceStage("build nightly-dev", "tici-needs-can", [], [
|
||||||
step("build nightly-dev", "PANDA_DEBUG_BUILD=1 RELEASE_BRANCH=nightly-dev $SOURCE_DIR/release/build_release.sh"),
|
step("build nightly-dev", "PANDA_DEBUG_BUILD=1 RELEASE_BRANCH=nightly-dev $SOURCE_DIR/release/build_release.sh"),
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
@@ -200,43 +200,63 @@ node {
|
|||||||
|
|
||||||
if (!env.BRANCH_NAME.matches(excludeRegex)) {
|
if (!env.BRANCH_NAME.matches(excludeRegex)) {
|
||||||
parallel (
|
parallel (
|
||||||
|
// tici tests
|
||||||
'onroad 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("build openpilot", "cd system/manager && ./build.py"),
|
||||||
step("check dirty", "release/check-dirty.sh"),
|
step("check dirty", "release/check-dirty.sh"),
|
||||||
step("onroad tests", "pytest selfdrive/test/test_onroad.py -s", [timeout: 60]),
|
step("onroad tests", "pytest selfdrive/test/test_onroad.py -s", [timeout: 60]),
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
'HW + Unit Tests': {
|
'HW + Unit Tests': {
|
||||||
deviceStage("tizi-hardware", "tizi-common", ["UNSAFE=1"], [
|
deviceStage("tici-hardware", "tici-common", ["UNSAFE=1"], [
|
||||||
step("build", "cd system/manager && ./build.py"),
|
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 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 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"),
|
step("test manager", "pytest system/manager/test/test_manager.py"),
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
'camerad OX03C10': {
|
'loopback': {
|
||||||
deviceStage("OX03C10", "tizi-ox03c10", ["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("build", "cd system/manager && ./build.py"),
|
||||||
step("test pandad", "pytest selfdrive/pandad/tests/test_pandad.py", [diffPaths: ["panda", "selfdrive/pandad/"]]),
|
step("test camerad", "pytest system/camerad/test/test_camerad.py", [timeout: 60]),
|
||||||
step("test camerad", "pytest system/camerad/test/test_camerad.py", [timeout: 90]),
|
step("test exposure", "pytest system/camerad/test/test_exposure.py"),
|
||||||
|
])
|
||||||
|
},
|
||||||
|
'camerad OX03C10': {
|
||||||
|
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"),
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
'camerad OS04C10': {
|
'camerad OS04C10': {
|
||||||
deviceStage("OS04C10", "tici-os04c10", ["UNSAFE=1"], [
|
deviceStage("OS04C10", "tici-os04c10", ["UNSAFE=1"], [
|
||||||
step("build", "cd system/manager && ./build.py"),
|
step("build", "cd system/manager && ./build.py"),
|
||||||
step("test pandad", "pytest selfdrive/pandad/tests/test_pandad.py", [diffPaths: ["panda", "selfdrive/pandad/"]]),
|
step("test camerad", "pytest system/camerad/test/test_camerad.py", [timeout: 60]),
|
||||||
step("test camerad", "pytest system/camerad/test/test_camerad.py", [timeout: 90]),
|
step("test exposure", "pytest system/camerad/test/test_exposure.py"),
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
'sensord': {
|
'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("build", "cd system/manager && ./build.py"),
|
||||||
step("test sensord", "pytest system/sensord/tests/test_sensord.py"),
|
step("test sensord", "pytest system/sensord/tests/test_sensord.py"),
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
'replay': {
|
'replay': {
|
||||||
deviceStage("model-replay", "tizi-replay", ["UNSAFE=1"], [
|
deviceStage("model-replay", "tici-replay", ["UNSAFE=1"], [
|
||||||
step("build", "cd system/manager && ./build.py", [diffPaths: ["selfdrive/modeld/", "tinygrad_repo", "selfdrive/test/process_replay/model_replay.py"]]),
|
step("build", "cd system/manager && ./build.py", [diffPaths: ["selfdrive/modeld/", "tinygrad_repo", "selfdrive/test/process_replay/model_replay.py"]]),
|
||||||
step("model replay", "selfdrive/test/process_replay/model_replay.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"]]),
|
||||||
])
|
])
|
||||||
@@ -244,8 +264,9 @@ node {
|
|||||||
'tizi': {
|
'tizi': {
|
||||||
deviceStage("tizi", "tizi", ["UNSAFE=1"], [
|
deviceStage("tizi", "tizi", ["UNSAFE=1"], [
|
||||||
step("build openpilot", "cd system/manager && ./build.py"),
|
step("build openpilot", "cd system/manager && ./build.py"),
|
||||||
step("test pandad loopback", "pytest selfdrive/pandad/tests/test_pandad_loopback.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 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"),
|
step("test amp", "pytest system/hardware/tici/tests/test_amplifier.py"),
|
||||||
step("test qcomgpsd", "pytest system/qcomgpsd/tests/test_qcomgpsd.py", [diffPaths: ["system/qcomgpsd/"]]),
|
step("test qcomgpsd", "pytest system/qcomgpsd/tests/test_qcomgpsd.py", [diffPaths: ["system/qcomgpsd/"]]),
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -3,23 +3,51 @@
|
|||||||
## 🌞 What is sunnypilot?
|
## 🌞 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.
|
[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.
|
||||||
|
|
||||||
## 💭 Join our Community Forum
|
## 💭 Join our Discord
|
||||||
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!
|
Join the official sunnypilot Discord server to stay up to date with all the latest features and be a part of shaping the future of sunnypilot!
|
||||||
* https://community.sunnypilot.ai/
|
* https://discord.gg/sunnypilot
|
||||||
|
|
||||||
|
 
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
https://docs.sunnypilot.ai/ is your one stop shop for everything from features to installation to FAQ about the sunnypilot
|
https://docs.sunnypilot.ai/ is your one stop shop for everything from features to installation to FAQ about the sunnypilot
|
||||||
|
|
||||||
## 🚘 Running on a dedicated device in a car
|
## 🚘 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).
|
* A supported device to run this software
|
||||||
|
* a [comma three](https://comma.ai/shop/products/three) or a [C3X](https://comma.ai/shop/comma-3x)
|
||||||
|
* This software
|
||||||
|
* One of [the 300+ supported cars](https://github.com/commaai/openpilot/blob/master/docs/CARS.md). We support Honda, Toyota, Hyundai, Nissan, Kia, Chrysler, Lexus, Acura, Audi, VW, Ford and more. If your car is not supported but has adaptive cruise control and lane-keeping assist, it's likely able to run sunnypilot.
|
||||||
|
* A [car harness](https://comma.ai/shop/products/car-harness) to connect to your car
|
||||||
|
|
||||||
|
Detailed instructions for [how to mount the device in a car](https://comma.ai/setup).
|
||||||
|
|
||||||
## Installation
|
## 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).
|
Please refer to [Recommended Branches](#-recommended-branches) to find your preferred/supported branch. This guide will assume you want to install the latest `release-c3` branch.
|
||||||
|
|
||||||
|
* sunnypilot not installed or you installed a version before 0.8.17?
|
||||||
|
1. [Factory reset/uninstall](https://github.com/commaai/openpilot/wiki/FAQ#how-can-i-reset-the-device) the previous software if you have another software/fork installed.
|
||||||
|
2. After factory reset/uninstall and upon reboot, select `Custom Software` when given the option.
|
||||||
|
3. Input the installation URL per [Recommended Branches](#-recommended-branches). Example: ```release-c3.sunnypilot.ai```.
|
||||||
|
4. Complete the rest of the installation following the onscreen instructions.
|
||||||
|
|
||||||
|
* sunnypilot already installed and you installed a version after 0.8.17?
|
||||||
|
1. On the comma three, go to `Settings` ▶️ `Software`.
|
||||||
|
2. At the `Download` option, press `CHECK`. This will fetch the list of latest branches from sunnypilot.
|
||||||
|
3. At the `Target Branch` option, press `SELECT` to open the Target Branch selector.
|
||||||
|
4. Scroll to select the desired branch per Recommended Branches (see below). Example: `release-c3`
|
||||||
|
|
||||||
|
| Branch | Installation URL |
|
||||||
|
|:------------:|:--------------------------------:|
|
||||||
|
| `release-c3` | https://release-c3.sunnypilot.ai |
|
||||||
|
| `staging-c3` | https://staging-c3.sunnypilot.ai |
|
||||||
|
| `dev-c3` | https://dev-c3.sunnypilot.ai |
|
||||||
|
|
||||||
|
Requires further assistance with software installation? Join the [sunnypilot Discord server](https://discord.sunnypilot.com) and message us in the `#installation-help` channel.
|
||||||
|
|
||||||
## 🎆 Pull Requests
|
## 🎆 Pull Requests
|
||||||
We welcome both pull requests and issues on GitHub. Bug fixes are encouraged.
|
We welcome both pull requests and issues on GitHub. Bug fixes are encouraged.
|
||||||
|
|
||||||
Pull requests should be against the most current `master` branch.
|
Pull requests should be against the most current `master-new` branch.
|
||||||
|
|
||||||
## 📊 User Data
|
## 📊 User Data
|
||||||
|
|
||||||
@@ -28,7 +56,7 @@ By default, sunnypilot uploads the driving data to comma servers. You can also a
|
|||||||
sunnypilot is open source software. The user is free to disable data collection if they wish to do so.
|
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.
|
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.
|
The driver-facing camera is only logged if you explicitly opt-in in settings. The microphone is not recorded.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
@@ -38,12 +66,12 @@ sunnypilot is released under the [MIT License](LICENSE). This repository include
|
|||||||
|
|
||||||
The original openpilot license notice, including comma.ai’s indemnification and alpha software disclaimer, is reproduced below as required:
|
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.
|
> 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.
|
> 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.
|
> **THIS IS ALPHA QUALITY SOFTWARE FOR RESEARCH PURPOSES ONLY. THIS IS NOT A PRODUCT.
|
||||||
> YOU ARE RESPONSIBLE FOR COMPLYING WITH LOCAL LAWS AND REGULATIONS.
|
> YOU ARE RESPONSIBLE FOR COMPLYING WITH LOCAL LAWS AND REGULATIONS.
|
||||||
> NO WARRANTY EXPRESSED OR IMPLIED.**
|
> NO WARRANTY EXPRESSED OR IMPLIED.**
|
||||||
|
|
||||||
For full license terms, please see the [`LICENSE`](LICENSE) file.
|
For full license terms, please see the [`LICENSE`](LICENSE) file.
|
||||||
|
|||||||
+4
-56
@@ -1,62 +1,10 @@
|
|||||||
Version 0.10.4 (2026-02-17)
|
Version 0.9.9 (2025-04-30)
|
||||||
========================
|
|
||||||
* Kia K7 2017 support thanks to royjr!
|
|
||||||
* Lexus LS 2018 support thanks to Hacheoy!
|
|
||||||
* Reduce comma four standby power usage by 77% to 52 mW
|
|
||||||
|
|
||||||
Version 0.10.3 (2025-12-17)
|
|
||||||
========================
|
|
||||||
* 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)
|
|
||||||
========================
|
========================
|
||||||
* New driving model
|
* 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!
|
* 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)
|
Version 0.9.8 (2025-02-28)
|
||||||
========================
|
========================
|
||||||
|
|||||||
+281
-125
@@ -3,104 +3,230 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
import sysconfig
|
import sysconfig
|
||||||
import platform
|
import platform
|
||||||
import shlex
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
import SCons.Errors
|
import SCons.Errors
|
||||||
|
|
||||||
SCons.Warnings.warningAsException(True)
|
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')
|
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('--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('--verbose', action='store_true', default=False, help='show full build commands')
|
|
||||||
AddOption('--minimal',
|
AddOption('--minimal',
|
||||||
action='store_false',
|
action='store_false',
|
||||||
dest='extras',
|
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.')
|
help='the minimum build to run openpilot. no tests, tools, etc.')
|
||||||
|
|
||||||
# Detect platform
|
AddOption('--stock-ui',
|
||||||
arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip()
|
action='store_true',
|
||||||
|
dest='stock_ui',
|
||||||
|
default=False,
|
||||||
|
help='Build stock openpilot UI instead of sunnypilot UI')
|
||||||
|
|
||||||
|
## 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":
|
if platform.system() == "Darwin":
|
||||||
arch = "Darwin"
|
arch = "Darwin"
|
||||||
elif arch == "aarch64" and os.path.isfile('/TICI'):
|
brew_prefix = subprocess.check_output(['brew', '--prefix'], encoding='utf8').strip()
|
||||||
|
elif arch == "aarch64" and AGNOS:
|
||||||
arch = "larch64"
|
arch = "larch64"
|
||||||
assert arch in [
|
assert arch in ["larch64", "aarch64", "x86_64", "Darwin"]
|
||||||
"larch64", # linux tici arm64
|
|
||||||
"aarch64", # linux pc arm64
|
|
||||||
"x86_64", # linux pc x64
|
|
||||||
"Darwin", # macOS arm64 (x86 not supported)
|
|
||||||
]
|
|
||||||
|
|
||||||
if arch != "larch64":
|
lenv = {
|
||||||
import bzip2
|
"PATH": os.environ['PATH'],
|
||||||
import capnproto
|
"LD_LIBRARY_PATH": [Dir(f"#third_party/acados/{arch}/lib").abspath],
|
||||||
import eigen
|
"PYTHONPATH": Dir("#").abspath + ':' + Dir(f"#third_party/acados").abspath,
|
||||||
import ffmpeg as ffmpeg_pkg
|
|
||||||
import libjpeg
|
"ACADOS_SOURCE_DIR": Dir("#third_party/acados").abspath,
|
||||||
import libyuv
|
"ACADOS_PYTHON_INTERFACE_PATH": Dir("#third_party/acados/acados_template").abspath,
|
||||||
import ncurses
|
"TERA_PATH": Dir("#").abspath + f"/third_party/acados/{arch}/t_renderer"
|
||||||
import python3_dev
|
}
|
||||||
import zeromq
|
|
||||||
import zstd
|
rpath = lenv["LD_LIBRARY_PATH"].copy()
|
||||||
pkgs = [bzip2, capnproto, eigen, ffmpeg_pkg, libjpeg, libyuv, ncurses, zeromq, zstd]
|
|
||||||
py_include = python3_dev.INCLUDE_DIR
|
if arch == "larch64":
|
||||||
|
cpppath = [
|
||||||
|
"#third_party/opencl/include",
|
||||||
|
]
|
||||||
|
|
||||||
|
libpath = [
|
||||||
|
"/usr/local/lib",
|
||||||
|
"/system/vendor/lib64",
|
||||||
|
f"#third_party/acados/{arch}/lib",
|
||||||
|
]
|
||||||
|
|
||||||
|
libpath += [
|
||||||
|
"#third_party/snpe/larch64",
|
||||||
|
"#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:
|
else:
|
||||||
# TODO: remove when AGNOS has our new vendor pkgs
|
cflags = []
|
||||||
pkgs = []
|
cxxflags = []
|
||||||
py_include = sysconfig.get_paths()['include']
|
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 arch == "x86_64":
|
||||||
|
libpath += [
|
||||||
|
f"#third_party/snpe/{arch}"
|
||||||
|
]
|
||||||
|
rpath += [
|
||||||
|
Dir(f"#third_party/snpe/{arch}").abspath,
|
||||||
|
]
|
||||||
|
|
||||||
|
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"]
|
||||||
|
|
||||||
|
if not GetOption('stock_ui'):
|
||||||
|
cflags += ["-DSUNNYPILOT"]
|
||||||
|
cxxflags += ["-DSUNNYPILOT"]
|
||||||
|
|
||||||
|
ccflags_option = GetOption('ccflags')
|
||||||
|
if ccflags_option:
|
||||||
|
ccflags += ccflags_option.split(' ')
|
||||||
|
|
||||||
env = Environment(
|
env = Environment(
|
||||||
ENV={
|
ENV=lenv,
|
||||||
"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"
|
|
||||||
},
|
|
||||||
CCFLAGS=[
|
CCFLAGS=[
|
||||||
"-g",
|
"-g",
|
||||||
"-fPIC",
|
"-fPIC",
|
||||||
"-O2",
|
"-O2",
|
||||||
"-Wunused",
|
"-Wunused",
|
||||||
"-Werror",
|
"-Werror",
|
||||||
"-Wshadow" if arch in ("Darwin", "larch64") else "-Wshadow=local",
|
"-Wshadow",
|
||||||
"-Wno-unknown-warning-option",
|
"-Wno-unknown-warning-option",
|
||||||
"-Wno-inconsistent-missing-override",
|
"-Wno-inconsistent-missing-override",
|
||||||
"-Wno-c99-designator",
|
"-Wno-c99-designator",
|
||||||
"-Wno-reorder-init-list",
|
"-Wno-reorder-init-list",
|
||||||
"-Wno-vla-cxx-extension",
|
"-Wno-vla-cxx-extension",
|
||||||
],
|
] + cflags + ccflags,
|
||||||
CFLAGS=["-std=gnu11"],
|
|
||||||
CXXFLAGS=["-std=c++1z"],
|
CPPPATH=cpppath + [
|
||||||
CPPPATH=[
|
|
||||||
"#",
|
"#",
|
||||||
"#msgq",
|
|
||||||
"#third_party",
|
|
||||||
"#third_party/json11",
|
|
||||||
"#third_party/linux/include",
|
|
||||||
"#third_party/acados/include",
|
"#third_party/acados/include",
|
||||||
"#third_party/acados/include/blasfeo/include",
|
"#third_party/acados/include/blasfeo/include",
|
||||||
"#third_party/acados/include/hpipm/include",
|
"#third_party/acados/include/hpipm/include",
|
||||||
"#third_party/catch2/include",
|
"#third_party/catch2/include",
|
||||||
[x.INCLUDE_DIR for x in pkgs],
|
"#third_party/libyuv/include",
|
||||||
|
"#third_party/json11",
|
||||||
|
"#third_party/linux/include",
|
||||||
|
"#third_party/snpe/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",
|
"#msgq_repo",
|
||||||
"#third_party",
|
"#third_party",
|
||||||
"#selfdrive/pandad",
|
"#selfdrive/pandad",
|
||||||
|
"#common",
|
||||||
"#rednose/helpers",
|
"#rednose/helpers",
|
||||||
f"#third_party/acados/{arch}/lib",
|
|
||||||
[x.LIB_DIR for x in pkgs],
|
|
||||||
],
|
],
|
||||||
RPATH=[],
|
|
||||||
CYTHONCFILESUFFIX=".cpp",
|
CYTHONCFILESUFFIX=".cpp",
|
||||||
COMPILATIONDB_USE_ABSPATH=True,
|
COMPILATIONDB_USE_ABSPATH=True,
|
||||||
REDNOSE_ROOT="#",
|
REDNOSE_ROOT="#",
|
||||||
@@ -108,102 +234,122 @@ env = Environment(
|
|||||||
toolpath=["#site_scons/site_tools", "#rednose_repo/site_scons/site_tools"],
|
toolpath=["#site_scons/site_tools", "#rednose_repo/site_scons/site_tools"],
|
||||||
)
|
)
|
||||||
|
|
||||||
# Arch-specific flags and paths
|
if arch == "Darwin":
|
||||||
if arch == "larch64":
|
# RPATH is not supported on macOS, instead use the linker flags
|
||||||
env["CC"] = "clang"
|
darwin_rpath_link_flags = [f"-Wl,-rpath,{path}" for path in env["RPATH"]]
|
||||||
env["CXX"] = "clang++"
|
env["LINKFLAGS"] += darwin_rpath_link_flags
|
||||||
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=[
|
|
||||||
"/System/Library/Frameworks/OpenGL.framework/Libraries",
|
|
||||||
])
|
|
||||||
env.Append(CCFLAGS=["-DGL_SILENCE_DEPRECATION"])
|
|
||||||
env.Append(CXXFLAGS=["-DGL_SILENCE_DEPRECATION"])
|
|
||||||
else:
|
|
||||||
env.Append(LIBPATH=[
|
|
||||||
"/usr/lib",
|
|
||||||
"/usr/local/lib",
|
|
||||||
])
|
|
||||||
|
|
||||||
# Sanitizers and extra CCFLAGS from CLI
|
if GetOption('compile_db'):
|
||||||
if GetOption('asan'):
|
env.CompilationDatabase('compile_commands.json')
|
||||||
env.Append(CCFLAGS=["-fsanitize=address", "-fno-omit-frame-pointer"])
|
|
||||||
env.Append(LINKFLAGS=["-fsanitize=address"])
|
|
||||||
elif GetOption('ubsan'):
|
|
||||||
env.Append(CCFLAGS=["-fsanitize=undefined"])
|
|
||||||
env.Append(LINKFLAGS=["-fsanitize=undefined"])
|
|
||||||
|
|
||||||
_extra_cc = shlex.split(GetOption('ccflags') or '')
|
# Setup cache dir
|
||||||
if _extra_cc:
|
default_cache_dir = '/data/scons_cache' if AGNOS else '/tmp/scons_cache'
|
||||||
env.Append(CCFLAGS=_extra_cc)
|
cache_dir = ARGUMENTS.get('cache_dir', default_cache_dir)
|
||||||
|
CacheDir(cache_dir)
|
||||||
|
Clean(["."], cache_dir)
|
||||||
|
|
||||||
# no --as-needed on mac linker
|
|
||||||
if arch != "Darwin":
|
|
||||||
env.Append(LINKFLAGS=["-Wl,--as-needed", "-Wl,--no-undefined"])
|
|
||||||
|
|
||||||
# Shorter build output: show brief descriptions instead of full commands.
|
|
||||||
# Full command lines are still printed on failure by scons.
|
|
||||||
if not GetOption('verbose'):
|
|
||||||
for action, short in (
|
|
||||||
("CC", "CC"),
|
|
||||||
("CXX", "CXX"),
|
|
||||||
("LINK", "LINK"),
|
|
||||||
("SHCC", "CC"),
|
|
||||||
("SHCXX", "CXX"),
|
|
||||||
("SHLINK", "LINK"),
|
|
||||||
("AR", "AR"),
|
|
||||||
("RANLIB", "RANLIB"),
|
|
||||||
("AS", "AS"),
|
|
||||||
):
|
|
||||||
env[f"{action}COMSTR"] = f" [{short}] $TARGET"
|
|
||||||
|
|
||||||
# progress output
|
|
||||||
node_interval = 5
|
node_interval = 5
|
||||||
node_count = 0
|
node_count = 0
|
||||||
def progress_function(node):
|
def progress_function(node):
|
||||||
global node_count
|
global node_count
|
||||||
node_count += node_interval
|
node_count += node_interval
|
||||||
sys.stderr.write("progress: %d\n" % node_count)
|
sys.stderr.write("progress: %d\n" % node_count)
|
||||||
|
|
||||||
if os.environ.get('SCONS_PROGRESS'):
|
if os.environ.get('SCONS_PROGRESS'):
|
||||||
Progress(progress_function, interval=node_interval)
|
Progress(progress_function, interval=node_interval)
|
||||||
|
|
||||||
# ********** Cython build environment **********
|
# Cython build environment
|
||||||
|
py_include = sysconfig.get_paths()['include']
|
||||||
envCython = env.Clone()
|
envCython = env.Clone()
|
||||||
envCython["CPPPATH"] += [py_include, np.get_include()]
|
envCython["CPPPATH"] += [py_include, np.get_include()]
|
||||||
envCython["CCFLAGS"] += ["-Wno-#warnings", "-Wno-cpp", "-Wno-shadow", "-Wno-deprecated-declarations"]
|
envCython["CCFLAGS"] += ["-Wno-#warnings", "-Wno-shadow", "-Wno-deprecated-declarations"]
|
||||||
envCython["CCFLAGS"].remove("-Werror")
|
envCython["CCFLAGS"].remove("-Werror")
|
||||||
|
|
||||||
envCython["LIBS"] = []
|
envCython["LIBS"] = []
|
||||||
if arch == "Darwin":
|
if arch == "Darwin":
|
||||||
envCython["LINKFLAGS"] = env["LINKFLAGS"] + ["-bundle", "-undefined", "dynamic_lookup"]
|
envCython["LINKFLAGS"] = ["-bundle", "-undefined", "dynamic_lookup"] + darwin_rpath_link_flags
|
||||||
else:
|
else:
|
||||||
envCython["LINKFLAGS"] = ["-pthread", "-shared"]
|
envCython["LINKFLAGS"] = ["-pthread", "-shared"]
|
||||||
|
|
||||||
np_version = SCons.Script.Value(np.__version__)
|
np_version = SCons.Script.Value(np.__version__)
|
||||||
Export('envCython', 'np_version')
|
Export('envCython', 'np_version')
|
||||||
|
|
||||||
Export('env', 'arch')
|
# Qt build environment
|
||||||
|
qt_env = env.Clone()
|
||||||
|
qt_modules = ["Widgets", "Gui", "Core", "Network", "Concurrent", "DBus", "Xml"]
|
||||||
|
|
||||||
# Setup cache dir
|
qt_libs = []
|
||||||
default_cache_dir = '/data/scons_cache' if arch == "larch64" else '/tmp/scons_cache'
|
if arch == "Darwin":
|
||||||
cache_dir = ARGUMENTS.get('cache_dir', default_cache_dir)
|
qt_env['QTDIR'] = f"{brew_prefix}/opt/qt@5"
|
||||||
CacheDir(cache_dir)
|
qt_dirs = [
|
||||||
Clean(["."], cache_dir)
|
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
|
# Build common module
|
||||||
SConscript(['common/SConscript'])
|
SConscript(['common/SConscript'])
|
||||||
Import('_common')
|
Import('_common', '_gpucommon')
|
||||||
|
|
||||||
common = [_common, 'json11', 'zmq']
|
common = [_common, 'json11', 'zmq']
|
||||||
Export('common')
|
gpucommon = [_gpucommon]
|
||||||
|
|
||||||
|
Export('common', 'gpucommon')
|
||||||
|
|
||||||
# Build messaging (cereal + msgq + socketmaster + their dependencies)
|
# Build messaging (cereal + msgq + socketmaster + their dependencies)
|
||||||
# Enable swaglog include in submodules
|
# Enable swaglog include in submodules
|
||||||
@@ -227,8 +373,15 @@ SConscript(['rednose/SConscript'])
|
|||||||
|
|
||||||
# Build system services
|
# Build system services
|
||||||
SConscript([
|
SConscript([
|
||||||
|
'system/proclogd/SConscript',
|
||||||
|
'system/ubloxd/SConscript',
|
||||||
'system/loggerd/SConscript',
|
'system/loggerd/SConscript',
|
||||||
])
|
])
|
||||||
|
if arch != "Darwin":
|
||||||
|
SConscript([
|
||||||
|
'system/sensord/SConscript',
|
||||||
|
'system/logcatd/SConscript',
|
||||||
|
])
|
||||||
|
|
||||||
if arch == "larch64":
|
if arch == "larch64":
|
||||||
SConscript(['system/camerad/SConscript'])
|
SConscript(['system/camerad/SConscript'])
|
||||||
@@ -240,8 +393,11 @@ SConscript(['selfdrive/SConscript'])
|
|||||||
|
|
||||||
SConscript(['sunnypilot/SConscript'])
|
SConscript(['sunnypilot/SConscript'])
|
||||||
|
|
||||||
if Dir('#tools/cabana/').exists() and arch != "larch64":
|
if Dir('#tools/cabana/').exists() and GetOption('extras'):
|
||||||
SConscript(['tools/cabana/SConscript'])
|
SConscript(['tools/replay/SConscript'])
|
||||||
|
if arch != "larch64":
|
||||||
|
SConscript(['tools/cabana/SConscript'])
|
||||||
|
|
||||||
|
external_sconscript = GetOption('external_sconscript')
|
||||||
env.CompilationDatabase('compile_commands.json')
|
if external_sconscript:
|
||||||
|
SConscript([external_sconscript])
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@ cereal = env.Library('cereal', [f'gen/cpp/{s}.c++' for s in schema_files])
|
|||||||
|
|
||||||
# Build messaging
|
# Build messaging
|
||||||
services_h = env.Command(['services.h'], ['services.py'], 'python3 ' + cereal_dir.path + '/services.py > $TARGET')
|
services_h = env.Command(['services.h'], ['services.py'], 'python3 ' + cereal_dir.path + '/services.py > $TARGET')
|
||||||
env.Program('messaging/bridge', ['messaging/bridge.cc', 'messaging/msgq_to_zmq.cc', 'messaging/bridge_zmq.cc'], LIBS=[msgq, common, 'pthread'])
|
env.Program('messaging/bridge', ['messaging/bridge.cc', 'messaging/msgq_to_zmq.cc'], LIBS=[msgq, common, 'pthread'])
|
||||||
|
|
||||||
socketmaster = env.Library('socketmaster', ['messaging/socketmaster.cc'])
|
socketmaster = env.Library('socketmaster', ['messaging/socketmaster.cc'])
|
||||||
|
|
||||||
|
|||||||
+4
-6
@@ -1,11 +1,9 @@
|
|||||||
import os
|
import os
|
||||||
import capnp
|
import capnp
|
||||||
from importlib.resources import as_file, files
|
|
||||||
|
|
||||||
|
CEREAL_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||||
capnp.remove_import_hook()
|
capnp.remove_import_hook()
|
||||||
|
|
||||||
with as_file(files("cereal")) as fspath:
|
log = capnp.load(os.path.join(CEREAL_PATH, "log.capnp"))
|
||||||
CEREAL_PATH = fspath.as_posix()
|
car = capnp.load(os.path.join(CEREAL_PATH, "car.capnp"))
|
||||||
log = capnp.load(os.path.join(CEREAL_PATH, "log.capnp"))
|
custom = capnp.load(os.path.join(CEREAL_PATH, "custom.capnp"))
|
||||||
car = capnp.load(os.path.join(CEREAL_PATH, "car.capnp"))
|
|
||||||
custom = capnp.load(os.path.join(CEREAL_PATH, "custom.capnp"))
|
|
||||||
|
|||||||
+21
-265
@@ -25,92 +25,8 @@ struct ModularAssistiveDrivingSystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# 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 SelfdriveStateSP @0x81c2f05a394cf4af {
|
struct SelfdriveStateSP @0x81c2f05a394cf4af {
|
||||||
mads @0 :ModularAssistiveDrivingSystem;
|
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 ModelManagerSP @0xaedffd8f31e7b55d {
|
struct ModelManagerSP @0xaedffd8f31e7b55d {
|
||||||
@@ -123,6 +39,20 @@ struct ModelManagerSP @0xaedffd8f31e7b55d {
|
|||||||
sha256 @1 :Text;
|
sha256 @1 :Text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Type {
|
||||||
|
drive @0;
|
||||||
|
navigation @1;
|
||||||
|
metadata @2;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Model {
|
||||||
|
fullName @0 :Text;
|
||||||
|
fileName @1 :Text;
|
||||||
|
downloadUri @2 :DownloadUri;
|
||||||
|
downloadProgress @3 :DownloadProgress;
|
||||||
|
type @4 :Type;
|
||||||
|
}
|
||||||
|
|
||||||
enum DownloadStatus {
|
enum DownloadStatus {
|
||||||
notDownloading @0;
|
notDownloading @0;
|
||||||
downloading @1;
|
downloading @1;
|
||||||
@@ -137,37 +67,12 @@ struct ModelManagerSP @0xaedffd8f31e7b55d {
|
|||||||
eta @2 :UInt32;
|
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;
|
|
||||||
offPolicy @4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Runner {
|
enum Runner {
|
||||||
snpe @0;
|
snpe @0;
|
||||||
tinygrad @1;
|
tinygrad @1;
|
||||||
stock @2;
|
stock @2;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Override {
|
|
||||||
key @0 :Text;
|
|
||||||
value @1 :Text;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ModelBundle {
|
struct ModelBundle {
|
||||||
index @0 :UInt32;
|
index @0 :UInt32;
|
||||||
internalName @1 :Text;
|
internalName @1 :Text;
|
||||||
@@ -178,21 +83,11 @@ struct ModelManagerSP @0xaedffd8f31e7b55d {
|
|||||||
environment @6 :Text;
|
environment @6 :Text;
|
||||||
runner @7 :Runner;
|
runner @7 :Runner;
|
||||||
is20hz @8 :Bool;
|
is20hz @8 :Bool;
|
||||||
ref @9 :Text;
|
|
||||||
minimumSelectorVersion @10 :UInt32;
|
|
||||||
overrides @11 :List(Override);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LongitudinalPlanSP @0xf35cc4560bbf6ec2 {
|
struct LongitudinalPlanSP @0xf35cc4560bbf6ec2 {
|
||||||
dec @0 :DynamicExperimentalControl;
|
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 {
|
struct DynamicExperimentalControl {
|
||||||
state @0 :DynamicExperimentalControlState;
|
state @0 :DynamicExperimentalControlState;
|
||||||
@@ -204,97 +99,6 @@ struct LongitudinalPlanSP @0xf35cc4560bbf6ec2 {
|
|||||||
blended @1;
|
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 OnroadEventSP @0xda96579883444c35 {
|
struct OnroadEventSP @0xda96579883444c35 {
|
||||||
@@ -332,24 +136,12 @@ struct OnroadEventSP @0xda96579883444c35 {
|
|||||||
controlsMismatchLateral @12;
|
controlsMismatchLateral @12;
|
||||||
hyundaiRadarTracksConfirmed @13;
|
hyundaiRadarTracksConfirmed @13;
|
||||||
experimentalModeSwitched @14;
|
experimentalModeSwitched @14;
|
||||||
wrongCarModeAlertOnly @15;
|
|
||||||
pedalPressedAlertOnly @16;
|
|
||||||
laneTurnLeft @17;
|
|
||||||
laneTurnRight @18;
|
|
||||||
speedLimitPreActive @19;
|
|
||||||
speedLimitActive @20;
|
|
||||||
speedLimitChanged @21;
|
|
||||||
speedLimitPending @22;
|
|
||||||
e2eChime @23;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CarParamsSP @0x80ae746ee2596b11 {
|
struct CarParamsSP @0x80ae746ee2596b11 {
|
||||||
flags @0 :UInt32; # flags for car specific quirks in sunnypilot
|
flags @0 :UInt32; # flags for car specific quirks in sunnypilot
|
||||||
safetyParam @1 : Int16; # flags for sunnypilot's custom safety flags
|
safetyParam @1 : Int16; # flags for sunnypilot's custom safety flags
|
||||||
pcmCruiseSpeed @3 :Bool;
|
|
||||||
intelligentCruiseButtonManagementAvailable @4 :Bool;
|
|
||||||
enableGasInterceptor @5 :Bool;
|
|
||||||
|
|
||||||
neuralNetworkLateralControl @2 :NeuralNetworkLateralControl;
|
neuralNetworkLateralControl @2 :NeuralNetworkLateralControl;
|
||||||
|
|
||||||
@@ -366,28 +158,6 @@ struct CarParamsSP @0x80ae746ee2596b11 {
|
|||||||
|
|
||||||
struct CarControlSP @0xa5cd762cd951a455 {
|
struct CarControlSP @0xa5cd762cd951a455 {
|
||||||
mads @0 :ModularAssistiveDrivingSystem;
|
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 BackupManagerSP @0xf98d843bfd7004a3 {
|
struct BackupManagerSP @0xf98d843bfd7004a3 {
|
||||||
@@ -398,14 +168,14 @@ struct BackupManagerSP @0xf98d843bfd7004a3 {
|
|||||||
lastError @4 :Text;
|
lastError @4 :Text;
|
||||||
currentBackup @5 :BackupInfo;
|
currentBackup @5 :BackupInfo;
|
||||||
backupHistory @6 :List(BackupInfo);
|
backupHistory @6 :List(BackupInfo);
|
||||||
|
|
||||||
enum Status {
|
enum Status {
|
||||||
idle @0;
|
idle @0;
|
||||||
inProgress @1;
|
inProgress @1;
|
||||||
completed @2;
|
completed @2;
|
||||||
failed @3;
|
failed @3;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Version {
|
struct Version {
|
||||||
major @0 :UInt16;
|
major @0 :UInt16;
|
||||||
minor @1 :UInt16;
|
minor @1 :UInt16;
|
||||||
@@ -413,13 +183,13 @@ struct BackupManagerSP @0xf98d843bfd7004a3 {
|
|||||||
build @3 :UInt16;
|
build @3 :UInt16;
|
||||||
branch @4 :Text;
|
branch @4 :Text;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MetadataEntry {
|
struct MetadataEntry {
|
||||||
key @0 :Text;
|
key @0 :Text;
|
||||||
value @1 :Text;
|
value @1 :Text;
|
||||||
tags @2 :List(Text);
|
tags @2 :List(Text);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct BackupInfo {
|
struct BackupInfo {
|
||||||
deviceId @0 :Text;
|
deviceId @0 :Text;
|
||||||
version @1 :UInt32;
|
version @1 :UInt32;
|
||||||
@@ -432,27 +202,13 @@ struct BackupManagerSP @0xf98d843bfd7004a3 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CarStateSP @0xb86e6369214c01c8 {
|
struct CustomReserved7 @0xb86e6369214c01c8 {
|
||||||
speedLimit @0 :Float32;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LiveMapDataSP @0xf416ec09499d9d19 {
|
struct CustomReserved8 @0xf416ec09499d9d19 {
|
||||||
speedLimitValid @0 :Bool;
|
|
||||||
speedLimit @1 :Float32;
|
|
||||||
speedLimitAheadValid @2 :Bool;
|
|
||||||
speedLimitAhead @3 :Float32;
|
|
||||||
speedLimitAheadDistance @4 :Float32;
|
|
||||||
roadName @5 :Text;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ModelDataV2SP @0xa1680744031fdb2d {
|
struct CustomReserved9 @0xa1680744031fdb2d {
|
||||||
laneTurnDirection @0 :TurnDirection;
|
|
||||||
|
|
||||||
enum TurnDirection {
|
|
||||||
none @0;
|
|
||||||
turnLeft @1;
|
|
||||||
turnRight @2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CustomReserved10 @0xcb9fd56c7057593a {
|
struct CustomReserved10 @0xcb9fd56c7057593a {
|
||||||
|
|||||||
+23
-63
@@ -48,7 +48,6 @@ struct OnroadEvent @0xc4fa6047f024e718 {
|
|||||||
preEnableStandstill @12; # added during pre-enable state with brake
|
preEnableStandstill @12; # added during pre-enable state with brake
|
||||||
gasPressedOverride @13; # added when user is pressing gas with no disengage on gas
|
gasPressedOverride @13; # added when user is pressing gas with no disengage on gas
|
||||||
steerOverride @14;
|
steerOverride @14;
|
||||||
steerDisengage @94; # exits active state
|
|
||||||
cruiseDisabled @15;
|
cruiseDisabled @15;
|
||||||
speedTooLow @16;
|
speedTooLow @16;
|
||||||
outOfSpace @17;
|
outOfSpace @17;
|
||||||
@@ -87,7 +86,6 @@ struct OnroadEvent @0xc4fa6047f024e718 {
|
|||||||
laneChange @50;
|
laneChange @50;
|
||||||
lowMemory @51;
|
lowMemory @51;
|
||||||
stockAeb @52;
|
stockAeb @52;
|
||||||
stockLkas @98;
|
|
||||||
ldw @53;
|
ldw @53;
|
||||||
carUnrecognized @54;
|
carUnrecognized @54;
|
||||||
invalidLkasSetting @55;
|
invalidLkasSetting @55;
|
||||||
@@ -128,9 +126,6 @@ struct OnroadEvent @0xc4fa6047f024e718 {
|
|||||||
espActive @90;
|
espActive @90;
|
||||||
personalityChanged @91;
|
personalityChanged @91;
|
||||||
aeb @92;
|
aeb @92;
|
||||||
userBookmark @95;
|
|
||||||
excessiveActuation @96;
|
|
||||||
audioFeedback @97;
|
|
||||||
|
|
||||||
soundsUnavailableDEPRECATED @47;
|
soundsUnavailableDEPRECATED @47;
|
||||||
}
|
}
|
||||||
@@ -493,14 +488,13 @@ struct DeviceState @0xa4d8b5af2aa492eb {
|
|||||||
# device thermals
|
# device thermals
|
||||||
cpuTempC @26 :List(Float32);
|
cpuTempC @26 :List(Float32);
|
||||||
gpuTempC @27 :List(Float32);
|
gpuTempC @27 :List(Float32);
|
||||||
dspTempC @49 :Float32;
|
|
||||||
memoryTempC @28 :Float32;
|
memoryTempC @28 :Float32;
|
||||||
|
nvmeTempC @35 :List(Float32);
|
||||||
modemTempC @36 :List(Float32);
|
modemTempC @36 :List(Float32);
|
||||||
pmicTempC @39 :List(Float32);
|
pmicTempC @39 :List(Float32);
|
||||||
intakeTempC @46 :Float32;
|
intakeTempC @46 :Float32;
|
||||||
exhaustTempC @47 :Float32;
|
exhaustTempC @47 :Float32;
|
||||||
gnssTempC @48 :Float32;
|
caseTempC @48 :Float32;
|
||||||
bottomSocTempC @50 :Float32;
|
|
||||||
maxTempC @44 :Float32; # max of other temps, used to control fan
|
maxTempC @44 :Float32; # max of other temps, used to control fan
|
||||||
thermalZones @38 :List(ThermalZone);
|
thermalZones @38 :List(ThermalZone);
|
||||||
thermalStatus @14 :ThermalStatus;
|
thermalStatus @14 :ThermalStatus;
|
||||||
@@ -571,7 +565,6 @@ struct DeviceState @0xa4d8b5af2aa492eb {
|
|||||||
chargingDisabledDEPRECATED @18 :Bool;
|
chargingDisabledDEPRECATED @18 :Bool;
|
||||||
usbOnlineDEPRECATED @12 :Bool;
|
usbOnlineDEPRECATED @12 :Bool;
|
||||||
ambientTempCDEPRECATED @30 :Float32;
|
ambientTempCDEPRECATED @30 :Float32;
|
||||||
nvmeTempCDEPRECATED @35 :List(Float32);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PandaState @0xa7649e2575e4591e {
|
struct PandaState @0xa7649e2575e4591e {
|
||||||
@@ -587,13 +580,13 @@ struct PandaState @0xa7649e2575e4591e {
|
|||||||
heartbeatLost @22 :Bool;
|
heartbeatLost @22 :Bool;
|
||||||
interruptLoad @25 :Float32;
|
interruptLoad @25 :Float32;
|
||||||
fanPower @28 :UInt8;
|
fanPower @28 :UInt8;
|
||||||
|
fanStallCount @34 :UInt8;
|
||||||
|
|
||||||
spiErrorCount @33 :UInt16;
|
spiChecksumErrorCount @33 :UInt16;
|
||||||
|
|
||||||
harnessStatus @21 :HarnessStatus;
|
harnessStatus @21 :HarnessStatus;
|
||||||
sbu1Voltage @35 :Float32;
|
sbu1Voltage @35 :Float32;
|
||||||
sbu2Voltage @36 :Float32;
|
sbu2Voltage @36 :Float32;
|
||||||
soundOutputLevel @37 :UInt16;
|
|
||||||
|
|
||||||
# can health
|
# can health
|
||||||
canState0 @29 :PandaCanState;
|
canState0 @29 :PandaCanState;
|
||||||
@@ -716,7 +709,6 @@ struct PandaState @0xa7649e2575e4591e {
|
|||||||
usbPowerModeDEPRECATED @12 :PeripheralState.UsbPowerModeDEPRECATED;
|
usbPowerModeDEPRECATED @12 :PeripheralState.UsbPowerModeDEPRECATED;
|
||||||
safetyParamDEPRECATED @20 :Int16;
|
safetyParamDEPRECATED @20 :Int16;
|
||||||
safetyParam2DEPRECATED @26 :UInt32;
|
safetyParam2DEPRECATED @26 :UInt32;
|
||||||
fanStallCountDEPRECATED @34 :UInt8;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PeripheralState {
|
struct PeripheralState {
|
||||||
@@ -921,8 +913,6 @@ struct ControlsState @0x97ff69c53601abf1 {
|
|||||||
saturated @7 :Bool;
|
saturated @7 :Bool;
|
||||||
actualLateralAccel @9 :Float32;
|
actualLateralAccel @9 :Float32;
|
||||||
desiredLateralAccel @10 :Float32;
|
desiredLateralAccel @10 :Float32;
|
||||||
desiredLateralJerk @11 :Float32;
|
|
||||||
version @12 :Int32;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LateralLQRState {
|
struct LateralLQRState {
|
||||||
@@ -1090,7 +1080,7 @@ struct ModelDataV2 {
|
|||||||
confidence @23: ConfidenceClass;
|
confidence @23: ConfidenceClass;
|
||||||
|
|
||||||
# Model perceived motion
|
# Model perceived motion
|
||||||
temporalPoseDEPRECATED @21 :Pose;
|
temporalPose @21 :Pose;
|
||||||
|
|
||||||
# e2e lateral planner
|
# e2e lateral planner
|
||||||
action @26: Action;
|
action @26: Action;
|
||||||
@@ -1184,8 +1174,6 @@ struct ModelDataV2 {
|
|||||||
|
|
||||||
struct Action {
|
struct Action {
|
||||||
desiredCurvature @0 :Float32;
|
desiredCurvature @0 :Float32;
|
||||||
desiredAcceleration @1 :Float32;
|
|
||||||
shouldStop @2 :Bool;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1480,11 +1468,6 @@ struct ProcLog {
|
|||||||
|
|
||||||
cmdline @15 :List(Text);
|
cmdline @15 :List(Text);
|
||||||
exe @16 :Text;
|
exe @16 :Text;
|
||||||
|
|
||||||
# from /proc/<pid>/smaps_rollup (proportional/private memory)
|
|
||||||
memPss @17 :UInt64; # Pss — shared pages split by mapper count
|
|
||||||
memPssAnon @18 :UInt64; # Pss_Anon — private anonymous (heap, stack)
|
|
||||||
memPssShmem @19 :UInt64; # Pss_Shmem — proportional MSGQ/tmpfs share
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CPUTimes {
|
struct CPUTimes {
|
||||||
@@ -2156,10 +2139,13 @@ struct Joystick {
|
|||||||
struct DriverStateV2 {
|
struct DriverStateV2 {
|
||||||
frameId @0 :UInt32;
|
frameId @0 :UInt32;
|
||||||
modelExecutionTime @1 :Float32;
|
modelExecutionTime @1 :Float32;
|
||||||
|
dspExecutionTimeDEPRECATED @2 :Float32;
|
||||||
gpuExecutionTime @8 :Float32;
|
gpuExecutionTime @8 :Float32;
|
||||||
rawPredictions @3 :Data;
|
rawPredictions @3 :Data;
|
||||||
|
|
||||||
|
poorVisionProb @4 :Float32;
|
||||||
wheelOnRightProb @5 :Float32;
|
wheelOnRightProb @5 :Float32;
|
||||||
|
|
||||||
leftDriverData @6 :DriverData;
|
leftDriverData @6 :DriverData;
|
||||||
rightDriverData @7 :DriverData;
|
rightDriverData @7 :DriverData;
|
||||||
|
|
||||||
@@ -2174,14 +2160,10 @@ struct DriverStateV2 {
|
|||||||
leftBlinkProb @7 :Float32;
|
leftBlinkProb @7 :Float32;
|
||||||
rightBlinkProb @8 :Float32;
|
rightBlinkProb @8 :Float32;
|
||||||
sunglassesProb @9 :Float32;
|
sunglassesProb @9 :Float32;
|
||||||
phoneProb @13 :Float32;
|
occludedProb @10 :Float32;
|
||||||
notReadyProbDEPRECATED @12 :List(Float32);
|
readyProb @11 :List(Float32);
|
||||||
occludedProbDEPRECATED @10 :Float32;
|
notReadyProb @12 :List(Float32);
|
||||||
readyProbDEPRECATED @11 :List(Float32);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dspExecutionTimeDEPRECATED @2 :Float32;
|
|
||||||
poorVisionProbDEPRECATED @4 :Float32;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DriverStateDEPRECATED @0xb83c6cc593ed0a00 {
|
struct DriverStateDEPRECATED @0xb83c6cc593ed0a00 {
|
||||||
@@ -2233,10 +2215,7 @@ struct DriverMonitoringState @0xb83cda094a1da284 {
|
|||||||
hiStdCount @14 :UInt32;
|
hiStdCount @14 :UInt32;
|
||||||
isActiveMode @16 :Bool;
|
isActiveMode @16 :Bool;
|
||||||
isRHD @4 :Bool;
|
isRHD @4 :Bool;
|
||||||
uncertainCount @19 :UInt32;
|
|
||||||
|
|
||||||
phoneProbOffsetDEPRECATED @20 :Float32;
|
|
||||||
phoneProbValidCountDEPRECATED @21 :UInt32;
|
|
||||||
isPreviewDEPRECATED @15 :Bool;
|
isPreviewDEPRECATED @15 :Bool;
|
||||||
rhdCheckedDEPRECATED @5 :Bool;
|
rhdCheckedDEPRECATED @5 :Bool;
|
||||||
eventsDEPRECATED @0 :List(Car.OnroadEventDEPRECATED);
|
eventsDEPRECATED @0 :List(Car.OnroadEventDEPRECATED);
|
||||||
@@ -2297,7 +2276,6 @@ struct LiveTorqueParametersData {
|
|||||||
points @10 :List(List(Float32));
|
points @10 :List(List(Float32));
|
||||||
version @11 :Int32;
|
version @11 :Int32;
|
||||||
useParams @12 :Bool;
|
useParams @12 :Bool;
|
||||||
calPerc @13 :Int8;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LiveDelayData {
|
struct LiveDelayData {
|
||||||
@@ -2306,9 +2284,7 @@ struct LiveDelayData {
|
|||||||
status @2 :Status;
|
status @2 :Status;
|
||||||
|
|
||||||
lateralDelayEstimate @3 :Float32;
|
lateralDelayEstimate @3 :Float32;
|
||||||
lateralDelayEstimateStd @5 :Float32;
|
|
||||||
points @4 :List(Float32);
|
points @4 :List(Float32);
|
||||||
calPerc @6 :Int8;
|
|
||||||
|
|
||||||
enum Status {
|
enum Status {
|
||||||
unestimated @0;
|
unestimated @0;
|
||||||
@@ -2483,27 +2459,16 @@ struct DebugAlert {
|
|||||||
alertText2 @1 :Text;
|
alertText2 @1 :Text;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UserBookmark @0xfe346a9de48d9b50 {
|
struct UserFlag {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SoundPressure @0xdc24138990726023 {
|
struct Microphone {
|
||||||
soundPressure @0 :Float32;
|
soundPressure @0 :Float32;
|
||||||
|
|
||||||
# uncalibrated, A-weighted
|
# uncalibrated, A-weighted
|
||||||
soundPressureWeighted @3 :Float32;
|
soundPressureWeighted @3 :Float32;
|
||||||
soundPressureWeightedDb @1 :Float32;
|
soundPressureWeightedDb @1 :Float32;
|
||||||
|
filteredSoundPressureWeightedDb @2 :Float32;
|
||||||
filteredSoundPressureWeightedDbDEPRECATED @2 :Float32;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AudioData {
|
|
||||||
data @0 :Data;
|
|
||||||
sampleRate @1 :UInt32;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AudioFeedback {
|
|
||||||
audio @0 :AudioData;
|
|
||||||
blockNum @1 :UInt16;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Touch {
|
struct Touch {
|
||||||
@@ -2532,10 +2497,13 @@ struct Event {
|
|||||||
controlsState @7 :ControlsState;
|
controlsState @7 :ControlsState;
|
||||||
selfdriveState @130 :SelfdriveState;
|
selfdriveState @130 :SelfdriveState;
|
||||||
gyroscope @99 :SensorEventData;
|
gyroscope @99 :SensorEventData;
|
||||||
|
gyroscope2 @100 :SensorEventData;
|
||||||
accelerometer @98 :SensorEventData;
|
accelerometer @98 :SensorEventData;
|
||||||
|
accelerometer2 @101 :SensorEventData;
|
||||||
magnetometer @95 :SensorEventData;
|
magnetometer @95 :SensorEventData;
|
||||||
lightSensor @96 :SensorEventData;
|
lightSensor @96 :SensorEventData;
|
||||||
temperatureSensor @97 :SensorEventData;
|
temperatureSensor @97 :SensorEventData;
|
||||||
|
temperatureSensor2 @123 :SensorEventData;
|
||||||
pandaStates @81 :List(PandaState);
|
pandaStates @81 :List(PandaState);
|
||||||
peripheralState @80 :PeripheralState;
|
peripheralState @80 :PeripheralState;
|
||||||
radarState @13 :RadarState;
|
radarState @13 :RadarState;
|
||||||
@@ -2580,8 +2548,7 @@ struct Event {
|
|||||||
livestreamDriverEncodeIdx @119 :EncodeIndex;
|
livestreamDriverEncodeIdx @119 :EncodeIndex;
|
||||||
|
|
||||||
# microphone data
|
# microphone data
|
||||||
soundPressure @103 :SoundPressure;
|
microphone @103 :Microphone;
|
||||||
rawAudioData @147 :AudioData;
|
|
||||||
|
|
||||||
# systems stuff
|
# systems stuff
|
||||||
androidLog @20 :AndroidLogEntry;
|
androidLog @20 :AndroidLogEntry;
|
||||||
@@ -2603,13 +2570,9 @@ struct Event {
|
|||||||
mapRenderState @105: MapRenderState;
|
mapRenderState @105: MapRenderState;
|
||||||
|
|
||||||
# UI services
|
# UI services
|
||||||
|
userFlag @93 :UserFlag;
|
||||||
uiDebug @102 :UIDebug;
|
uiDebug @102 :UIDebug;
|
||||||
|
|
||||||
# driving feedback
|
|
||||||
userBookmark @93 :UserBookmark;
|
|
||||||
bookmarkButton @148 :UserBookmark;
|
|
||||||
audioFeedback @149 :AudioFeedback;
|
|
||||||
|
|
||||||
# *********** debug ***********
|
# *********** debug ***********
|
||||||
testJoystick @52 :Joystick;
|
testJoystick @52 :Joystick;
|
||||||
roadEncodeData @86 :EncodeData;
|
roadEncodeData @86 :EncodeData;
|
||||||
@@ -2640,9 +2603,9 @@ struct Event {
|
|||||||
carParamsSP @111 :Custom.CarParamsSP;
|
carParamsSP @111 :Custom.CarParamsSP;
|
||||||
carControlSP @112 :Custom.CarControlSP;
|
carControlSP @112 :Custom.CarControlSP;
|
||||||
backupManagerSP @113 :Custom.BackupManagerSP;
|
backupManagerSP @113 :Custom.BackupManagerSP;
|
||||||
carStateSP @114 :Custom.CarStateSP;
|
customReserved7 @114 :Custom.CustomReserved7;
|
||||||
liveMapDataSP @115 :Custom.LiveMapDataSP;
|
customReserved8 @115 :Custom.CustomReserved8;
|
||||||
modelDataV2SP @116 :Custom.ModelDataV2SP;
|
customReserved9 @116 :Custom.CustomReserved9;
|
||||||
customReserved10 @136 :Custom.CustomReserved10;
|
customReserved10 @136 :Custom.CustomReserved10;
|
||||||
customReserved11 @137 :Custom.CustomReserved11;
|
customReserved11 @137 :Custom.CustomReserved11;
|
||||||
customReserved12 @138 :Custom.CustomReserved12;
|
customReserved12 @138 :Custom.CustomReserved12;
|
||||||
@@ -2695,11 +2658,8 @@ struct Event {
|
|||||||
lateralPlanDEPRECATED @64 :LateralPlan;
|
lateralPlanDEPRECATED @64 :LateralPlan;
|
||||||
navModelDEPRECATED @104 :NavModelData;
|
navModelDEPRECATED @104 :NavModelData;
|
||||||
uiPlanDEPRECATED @106 :UiPlan;
|
uiPlanDEPRECATED @106 :UiPlan;
|
||||||
liveLocationKalman @72 :LiveLocationKalman;
|
liveLocationKalmanDEPRECATED @72 :LiveLocationKalman;
|
||||||
liveTracksDEPRECATED @16 :List(LiveTracksDEPRECATED);
|
liveTracksDEPRECATED @16 :List(LiveTracksDEPRECATED);
|
||||||
onroadEventsDEPRECATED @68: List(Car.OnroadEventDEPRECATED);
|
onroadEventsDEPRECATED @68: List(Car.OnroadEventDEPRECATED);
|
||||||
gyroscope2DEPRECATED @100 :SensorEventData;
|
|
||||||
accelerometer2DEPRECATED @101 :SensorEventData;
|
|
||||||
temperatureSensor2DEPRECATED @123 :SensorEventData;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
# must be built with scons
|
# must be built with scons
|
||||||
from msgq import fake_event_handle, drain_sock_raw, MultiplePublishersError, IpcError, \
|
from msgq.ipc_pyx import Context, Poller, SubSocket, PubSocket, SocketEventHandle, toggle_fake_events, \
|
||||||
Context, Poller, SubSocket, PubSocket, SocketEventHandle, toggle_fake_events, \
|
set_fake_prefix, get_fake_prefix, delete_fake_prefix, wait_for_one_event
|
||||||
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, pub_sock, sub_sock, drain_sock_raw
|
||||||
import msgq
|
import msgq
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import capnp
|
import capnp
|
||||||
import time
|
import time
|
||||||
@@ -11,25 +13,11 @@ from typing import Optional, List, Union, Dict
|
|||||||
|
|
||||||
from cereal import log
|
from cereal import log
|
||||||
from cereal.services import SERVICE_LIST
|
from cereal.services import SERVICE_LIST
|
||||||
from openpilot.common.utils import MovingAverage
|
from openpilot.common.util import MovingAverage
|
||||||
|
|
||||||
NO_TRAVERSAL_LIMIT = 2**64-1
|
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():
|
def reset_context():
|
||||||
msgq.context = Context()
|
msgq.context = Context()
|
||||||
|
|
||||||
@@ -157,16 +145,12 @@ class SubMaster:
|
|||||||
self.updated = {s: False for s in services}
|
self.updated = {s: False for s in services}
|
||||||
self.recv_time = {s: 0. for s in services}
|
self.recv_time = {s: 0. for s in services}
|
||||||
self.recv_frame = {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.sock = {}
|
||||||
self.data = {}
|
self.data = {}
|
||||||
self.logMonoTime = {s: 0 for s in services}
|
self.valid = {}
|
||||||
|
self.logMonoTime = {}
|
||||||
# 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.freq_tracker: Dict[str, FrequencyTracker] = {}
|
self.freq_tracker: Dict[str, FrequencyTracker] = {}
|
||||||
self.poller = Poller()
|
self.poller = Poller()
|
||||||
@@ -193,6 +177,8 @@ class SubMaster:
|
|||||||
data = new_message(s, 0) # lists
|
data = new_message(s, 0) # lists
|
||||||
|
|
||||||
self.data[s] = getattr(data.as_reader(), s)
|
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)
|
self.freq_tracker[s] = FrequencyTracker(SERVICE_LIST[s].frequency, self.update_freq, s == poll)
|
||||||
|
|
||||||
def __getitem__(self, s: str) -> capnp.lib.capnp._DynamicStructReader:
|
def __getitem__(self, s: str) -> capnp.lib.capnp._DynamicStructReader:
|
||||||
@@ -229,10 +215,14 @@ class SubMaster:
|
|||||||
self.logMonoTime[s] = msg.logMonoTime
|
self.logMonoTime[s] = msg.logMonoTime
|
||||||
self.valid[s] = msg.valid
|
self.valid[s] = msg.valid
|
||||||
|
|
||||||
for s in self.static_freq_services:
|
for s in self.services:
|
||||||
# alive if delay is within 10x the expected frequency; checks relaxed in simulator
|
if SERVICE_LIST[s].frequency > 1e-5 and not self.simulation:
|
||||||
self.alive[s] = (cur_time - self.recv_time[s]) < (10. / SERVICE_LIST[s].frequency) or (self.seen[s] and self.simulation)
|
# alive if delay is within 10x the expected frequency
|
||||||
self.freq_ok[s] = self.freq_tracker[s].valid or self.simulation
|
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:
|
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)
|
return all(self.alive[s] for s in (service_list or self.services) if s not in self.ignore_alive)
|
||||||
|
|||||||
@@ -25,16 +25,15 @@ void msgq_to_zmq(const std::vector<std::string> &endpoints, const std::string &i
|
|||||||
}
|
}
|
||||||
|
|
||||||
void zmq_to_msgq(const std::vector<std::string> &endpoints, const std::string &ip) {
|
void zmq_to_msgq(const std::vector<std::string> &endpoints, const std::string &ip) {
|
||||||
auto poller = std::make_unique<BridgeZmqPoller>();
|
auto poller = std::make_unique<ZMQPoller>();
|
||||||
auto pub_context = std::make_unique<Context>();
|
auto pub_context = std::make_unique<MSGQContext>();
|
||||||
auto sub_context = std::make_unique<BridgeZmqContext>();
|
auto sub_context = std::make_unique<ZMQContext>();
|
||||||
std::map<BridgeZmqSubSocket *, PubSocket *> sub2pub;
|
std::map<SubSocket *, PubSocket *> sub2pub;
|
||||||
|
|
||||||
for (auto endpoint : endpoints) {
|
for (auto endpoint : endpoints) {
|
||||||
auto pub_sock = new PubSocket();
|
auto pub_sock = new MSGQPubSocket();
|
||||||
auto sub_sock = new BridgeZmqSubSocket();
|
auto sub_sock = new ZMQSubSocket();
|
||||||
size_t queue_size = services.at(endpoint).queue_size;
|
pub_sock->connect(pub_context.get(), endpoint);
|
||||||
pub_sock->connect(pub_context.get(), endpoint, true, queue_size);
|
|
||||||
sub_sock->connect(sub_context.get(), endpoint, ip, false);
|
sub_sock->connect(sub_context.get(), endpoint, ip, false);
|
||||||
|
|
||||||
poller->registerSocket(sub_sock);
|
poller->registerSocket(sub_sock);
|
||||||
|
|||||||
@@ -1,170 +0,0 @@
|
|||||||
#include "cereal/messaging/bridge_zmq.h"
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstring>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
static size_t fnv1a_hash(const std::string &str) {
|
|
||||||
const size_t fnv_prime = 0x100000001b3;
|
|
||||||
size_t hash_value = 0xcbf29ce484222325;
|
|
||||||
for (char c : str) {
|
|
||||||
hash_value ^= (unsigned char)c;
|
|
||||||
hash_value *= fnv_prime;
|
|
||||||
}
|
|
||||||
return hash_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: This is a hack to get the port number from the socket name, might have collisions.
|
|
||||||
static int get_port(std::string endpoint) {
|
|
||||||
size_t hash_value = fnv1a_hash(endpoint);
|
|
||||||
int start_port = 8023;
|
|
||||||
int max_port = 65535;
|
|
||||||
return start_port + (hash_value % (max_port - start_port));
|
|
||||||
}
|
|
||||||
|
|
||||||
BridgeZmqContext::BridgeZmqContext() {
|
|
||||||
context = zmq_ctx_new();
|
|
||||||
}
|
|
||||||
|
|
||||||
BridgeZmqContext::~BridgeZmqContext() {
|
|
||||||
if (context != nullptr) {
|
|
||||||
zmq_ctx_term(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BridgeZmqMessage::init(size_t sz) {
|
|
||||||
size = sz;
|
|
||||||
data = new char[size];
|
|
||||||
}
|
|
||||||
|
|
||||||
void BridgeZmqMessage::init(char *d, size_t sz) {
|
|
||||||
size = sz;
|
|
||||||
data = new char[size];
|
|
||||||
memcpy(data, d, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BridgeZmqMessage::close() {
|
|
||||||
if (size > 0) {
|
|
||||||
delete[] data;
|
|
||||||
}
|
|
||||||
data = nullptr;
|
|
||||||
size = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
BridgeZmqMessage::~BridgeZmqMessage() {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
int BridgeZmqSubSocket::connect(BridgeZmqContext *context, std::string endpoint, std::string address, bool conflate, bool check_endpoint) {
|
|
||||||
sock = zmq_socket(context->getRawContext(), ZMQ_SUB);
|
|
||||||
if (sock == nullptr) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
zmq_setsockopt(sock, ZMQ_SUBSCRIBE, "", 0);
|
|
||||||
|
|
||||||
if (conflate) {
|
|
||||||
int arg = 1;
|
|
||||||
zmq_setsockopt(sock, ZMQ_CONFLATE, &arg, sizeof(int));
|
|
||||||
}
|
|
||||||
|
|
||||||
int reconnect_ivl = 500;
|
|
||||||
zmq_setsockopt(sock, ZMQ_RECONNECT_IVL_MAX, &reconnect_ivl, sizeof(reconnect_ivl));
|
|
||||||
|
|
||||||
full_endpoint = "tcp://" + address + ":";
|
|
||||||
if (check_endpoint) {
|
|
||||||
full_endpoint += std::to_string(get_port(endpoint));
|
|
||||||
} else {
|
|
||||||
full_endpoint += endpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
return zmq_connect(sock, full_endpoint.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void BridgeZmqSubSocket::setTimeout(int timeout) {
|
|
||||||
zmq_setsockopt(sock, ZMQ_RCVTIMEO, &timeout, sizeof(int));
|
|
||||||
}
|
|
||||||
|
|
||||||
Message *BridgeZmqSubSocket::receive(bool non_blocking) {
|
|
||||||
zmq_msg_t msg;
|
|
||||||
assert(zmq_msg_init(&msg) == 0);
|
|
||||||
|
|
||||||
int flags = non_blocking ? ZMQ_DONTWAIT : 0;
|
|
||||||
int rc = zmq_msg_recv(&msg, sock, flags);
|
|
||||||
|
|
||||||
Message *ret = nullptr;
|
|
||||||
if (rc >= 0) {
|
|
||||||
ret = new BridgeZmqMessage;
|
|
||||||
ret->init((char *)zmq_msg_data(&msg), zmq_msg_size(&msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
zmq_msg_close(&msg);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
BridgeZmqSubSocket::~BridgeZmqSubSocket() {
|
|
||||||
if (sock != nullptr) {
|
|
||||||
zmq_close(sock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int BridgeZmqPubSocket::connect(BridgeZmqContext *context, std::string endpoint, bool check_endpoint) {
|
|
||||||
sock = zmq_socket(context->getRawContext(), ZMQ_PUB);
|
|
||||||
if (sock == nullptr) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
full_endpoint = "tcp://*:";
|
|
||||||
if (check_endpoint) {
|
|
||||||
full_endpoint += std::to_string(get_port(endpoint));
|
|
||||||
} else {
|
|
||||||
full_endpoint += endpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ZMQ pub sockets cannot be shared between processes, so we need to ensure pid stays the same.
|
|
||||||
pid = getpid();
|
|
||||||
|
|
||||||
return zmq_bind(sock, full_endpoint.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
int BridgeZmqPubSocket::sendMessage(Message *message) {
|
|
||||||
assert(pid == getpid());
|
|
||||||
return zmq_send(sock, message->getData(), message->getSize(), ZMQ_DONTWAIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
int BridgeZmqPubSocket::send(char *data, size_t size) {
|
|
||||||
assert(pid == getpid());
|
|
||||||
return zmq_send(sock, data, size, ZMQ_DONTWAIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
BridgeZmqPubSocket::~BridgeZmqPubSocket() {
|
|
||||||
if (sock != nullptr) {
|
|
||||||
zmq_close(sock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BridgeZmqPoller::registerSocket(BridgeZmqSubSocket *socket) {
|
|
||||||
assert(num_polls + 1 < (sizeof(polls) / sizeof(polls[0])));
|
|
||||||
polls[num_polls].socket = socket->getRawSocket();
|
|
||||||
polls[num_polls].events = ZMQ_POLLIN;
|
|
||||||
|
|
||||||
sockets.push_back(socket);
|
|
||||||
num_polls++;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<BridgeZmqSubSocket *> BridgeZmqPoller::poll(int timeout) {
|
|
||||||
std::vector<BridgeZmqSubSocket *> ret;
|
|
||||||
|
|
||||||
int rc = zmq_poll(polls, num_polls, timeout);
|
|
||||||
if (rc < 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < num_polls; i++) {
|
|
||||||
if (polls[i].revents) {
|
|
||||||
ret.push_back(sockets[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <zmq.h>
|
|
||||||
|
|
||||||
#include "msgq/ipc.h"
|
|
||||||
|
|
||||||
class BridgeZmqContext {
|
|
||||||
public:
|
|
||||||
BridgeZmqContext();
|
|
||||||
void *getRawContext() { return context; }
|
|
||||||
~BridgeZmqContext();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void *context = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
class BridgeZmqMessage : public Message {
|
|
||||||
public:
|
|
||||||
void init(size_t size);
|
|
||||||
void init(char *data, size_t size);
|
|
||||||
void close();
|
|
||||||
size_t getSize() { return size; }
|
|
||||||
char *getData() { return data; }
|
|
||||||
~BridgeZmqMessage();
|
|
||||||
|
|
||||||
private:
|
|
||||||
char *data = nullptr;
|
|
||||||
size_t size = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class BridgeZmqSubSocket {
|
|
||||||
public:
|
|
||||||
int connect(BridgeZmqContext *context, std::string endpoint, std::string address, bool conflate = false, bool check_endpoint = true);
|
|
||||||
void setTimeout(int timeout);
|
|
||||||
Message *receive(bool non_blocking = false);
|
|
||||||
void *getRawSocket() { return sock; }
|
|
||||||
~BridgeZmqSubSocket();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void *sock = nullptr;
|
|
||||||
std::string full_endpoint;
|
|
||||||
};
|
|
||||||
|
|
||||||
class BridgeZmqPubSocket {
|
|
||||||
public:
|
|
||||||
int connect(BridgeZmqContext *context, std::string endpoint, bool check_endpoint = true);
|
|
||||||
int sendMessage(Message *message);
|
|
||||||
int send(char *data, size_t size);
|
|
||||||
void *getRawSocket() { return sock; }
|
|
||||||
~BridgeZmqPubSocket();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void *sock = nullptr;
|
|
||||||
std::string full_endpoint;
|
|
||||||
int pid = -1;
|
|
||||||
};
|
|
||||||
|
|
||||||
class BridgeZmqPoller {
|
|
||||||
public:
|
|
||||||
void registerSocket(BridgeZmqSubSocket *socket);
|
|
||||||
std::vector<BridgeZmqSubSocket *> poll(int timeout);
|
|
||||||
|
|
||||||
private:
|
|
||||||
static constexpr size_t MAX_BRIDGE_ZMQ_POLLERS = 128;
|
|
||||||
std::vector<BridgeZmqSubSocket *> sockets;
|
|
||||||
zmq_pollitem_t polls[MAX_BRIDGE_ZMQ_POLLERS] = {};
|
|
||||||
size_t num_polls = 0;
|
|
||||||
};
|
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#include "cereal/services.h"
|
|
||||||
#include "common/util.h"
|
#include "common/util.h"
|
||||||
|
|
||||||
extern ExitHandler do_exit;
|
extern ExitHandler do_exit;
|
||||||
@@ -22,14 +21,14 @@ static std::string recv_zmq_msg(void *sock) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MsgqToZmq::run(const std::vector<std::string> &endpoints, const std::string &ip) {
|
void MsgqToZmq::run(const std::vector<std::string> &endpoints, const std::string &ip) {
|
||||||
zmq_context = std::make_unique<BridgeZmqContext>();
|
zmq_context = std::make_unique<ZMQContext>();
|
||||||
msgq_context = std::make_unique<Context>();
|
msgq_context = std::make_unique<MSGQContext>();
|
||||||
|
|
||||||
// Create ZMQPubSockets for each endpoint
|
// Create ZMQPubSockets for each endpoint
|
||||||
for (const auto &endpoint : endpoints) {
|
for (const auto &endpoint : endpoints) {
|
||||||
auto &socket_pair = socket_pairs.emplace_back();
|
auto &socket_pair = socket_pairs.emplace_back();
|
||||||
socket_pair.endpoint = endpoint;
|
socket_pair.endpoint = endpoint;
|
||||||
socket_pair.pub_sock = std::make_unique<BridgeZmqPubSocket>();
|
socket_pair.pub_sock = std::make_unique<ZMQPubSocket>();
|
||||||
int ret = socket_pair.pub_sock->connect(zmq_context.get(), endpoint);
|
int ret = socket_pair.pub_sock->connect(zmq_context.get(), endpoint);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
printf("Failed to create ZMQ publisher for [%s]: %s\n", endpoint.c_str(), zmq_strerror(zmq_errno()));
|
printf("Failed to create ZMQ publisher for [%s]: %s\n", endpoint.c_str(), zmq_strerror(zmq_errno()));
|
||||||
@@ -49,7 +48,7 @@ void MsgqToZmq::run(const std::vector<std::string> &endpoints, const std::string
|
|||||||
|
|
||||||
for (auto sub_sock : msgq_poller->poll(100)) {
|
for (auto sub_sock : msgq_poller->poll(100)) {
|
||||||
// Process messages for each socket
|
// Process messages for each socket
|
||||||
BridgeZmqPubSocket *pub_sock = sub2pub.at(sub_sock);
|
ZMQPubSocket *pub_sock = sub2pub.at(sub_sock);
|
||||||
for (int i = 0; i < MAX_MESSAGES_PER_SOCKET; ++i) {
|
for (int i = 0; i < MAX_MESSAGES_PER_SOCKET; ++i) {
|
||||||
auto msg = std::unique_ptr<Message>(sub_sock->receive(true));
|
auto msg = std::unique_ptr<Message>(sub_sock->receive(true));
|
||||||
if (!msg) break;
|
if (!msg) break;
|
||||||
@@ -72,7 +71,7 @@ void MsgqToZmq::zmqMonitorThread() {
|
|||||||
// Set up ZMQ monitor for each pub socket
|
// Set up ZMQ monitor for each pub socket
|
||||||
for (int i = 0; i < socket_pairs.size(); ++i) {
|
for (int i = 0; i < socket_pairs.size(); ++i) {
|
||||||
std::string addr = "inproc://op-bridge-monitor-" + std::to_string(i);
|
std::string addr = "inproc://op-bridge-monitor-" + std::to_string(i);
|
||||||
zmq_socket_monitor(socket_pairs[i].pub_sock->getRawSocket(), addr.c_str(), ZMQ_EVENT_ACCEPTED | ZMQ_EVENT_DISCONNECTED);
|
zmq_socket_monitor(socket_pairs[i].pub_sock->sock, addr.c_str(), ZMQ_EVENT_ACCEPTED | ZMQ_EVENT_DISCONNECTED);
|
||||||
|
|
||||||
void *monitor_socket = zmq_socket(zmq_context->getRawContext(), ZMQ_PAIR);
|
void *monitor_socket = zmq_socket(zmq_context->getRawContext(), ZMQ_PAIR);
|
||||||
zmq_connect(monitor_socket, addr.c_str());
|
zmq_connect(monitor_socket, addr.c_str());
|
||||||
@@ -109,8 +108,7 @@ void MsgqToZmq::zmqMonitorThread() {
|
|||||||
if (++pair.connected_clients == 1) {
|
if (++pair.connected_clients == 1) {
|
||||||
// Create new MSGQ subscriber socket and map to ZMQ publisher
|
// Create new MSGQ subscriber socket and map to ZMQ publisher
|
||||||
pair.sub_sock = std::make_unique<MSGQSubSocket>();
|
pair.sub_sock = std::make_unique<MSGQSubSocket>();
|
||||||
size_t queue_size = services.at(pair.endpoint).queue_size;
|
pair.sub_sock->connect(msgq_context.get(), pair.endpoint, "127.0.0.1");
|
||||||
pair.sub_sock->connect(msgq_context.get(), pair.endpoint, "127.0.0.1", false, true, queue_size);
|
|
||||||
sub2pub[pair.sub_sock.get()] = pair.pub_sock.get();
|
sub2pub[pair.sub_sock.get()] = pair.pub_sock.get();
|
||||||
registerSockets();
|
registerSockets();
|
||||||
}
|
}
|
||||||
@@ -130,7 +128,7 @@ void MsgqToZmq::zmqMonitorThread() {
|
|||||||
|
|
||||||
// Clean up monitor sockets
|
// Clean up monitor sockets
|
||||||
for (int i = 0; i < pollitems.size(); ++i) {
|
for (int i = 0; i < pollitems.size(); ++i) {
|
||||||
zmq_socket_monitor(socket_pairs[i].pub_sock->getRawSocket(), nullptr, 0);
|
zmq_socket_monitor(socket_pairs[i].pub_sock->sock, nullptr, 0);
|
||||||
zmq_close(pollitems[i].socket);
|
zmq_close(pollitems[i].socket);
|
||||||
}
|
}
|
||||||
cv.notify_one();
|
cv.notify_one();
|
||||||
|
|||||||
@@ -7,8 +7,9 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#define private public
|
||||||
#include "msgq/impl_msgq.h"
|
#include "msgq/impl_msgq.h"
|
||||||
#include "cereal/messaging/bridge_zmq.h"
|
#include "msgq/impl_zmq.h"
|
||||||
|
|
||||||
class MsgqToZmq {
|
class MsgqToZmq {
|
||||||
public:
|
public:
|
||||||
@@ -21,16 +22,16 @@ protected:
|
|||||||
|
|
||||||
struct SocketPair {
|
struct SocketPair {
|
||||||
std::string endpoint;
|
std::string endpoint;
|
||||||
std::unique_ptr<BridgeZmqPubSocket> pub_sock;
|
std::unique_ptr<ZMQPubSocket> pub_sock;
|
||||||
std::unique_ptr<MSGQSubSocket> sub_sock;
|
std::unique_ptr<MSGQSubSocket> sub_sock;
|
||||||
int connected_clients = 0;
|
int connected_clients = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<Context> msgq_context;
|
std::unique_ptr<MSGQContext> msgq_context;
|
||||||
std::unique_ptr<BridgeZmqContext> zmq_context;
|
std::unique_ptr<ZMQContext> zmq_context;
|
||||||
std::mutex mutex;
|
std::mutex mutex;
|
||||||
std::condition_variable cv;
|
std::condition_variable cv;
|
||||||
std::unique_ptr<MSGQPoller> msgq_poller;
|
std::unique_ptr<MSGQPoller> msgq_poller;
|
||||||
std::map<SubSocket *, BridgeZmqPubSocket *> sub2pub;
|
std::map<SubSocket *, ZMQPubSocket *> sub2pub;
|
||||||
std::vector<SocketPair> socket_pairs;
|
std::vector<SocketPair> socket_pairs;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ MessageContext message_context;
|
|||||||
struct SubMaster::SubMessage {
|
struct SubMaster::SubMessage {
|
||||||
std::string name;
|
std::string name;
|
||||||
SubSocket *socket = nullptr;
|
SubSocket *socket = nullptr;
|
||||||
float freq = 0.0f;
|
int freq = 0;
|
||||||
bool updated = false, alive = false, valid = false, ignore_alive;
|
bool updated = false, alive = false, valid = false, ignore_alive;
|
||||||
uint64_t rcv_time = 0, rcv_frame = 0;
|
uint64_t rcv_time = 0, rcv_frame = 0;
|
||||||
void *allocated_msg_reader = nullptr;
|
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);
|
assert(services.count(std::string(name)) > 0);
|
||||||
|
|
||||||
service serv = services.at(std::string(name));
|
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);
|
assert(socket != 0);
|
||||||
bool is_polled = inList(poll, name) || poll.empty();
|
bool is_polled = inList(poll, name) || poll.empty();
|
||||||
if (is_polled) poller_->registerSocket(socket);
|
if (is_polled) poller_->registerSocket(socket);
|
||||||
@@ -187,8 +187,7 @@ SubMaster::~SubMaster() {
|
|||||||
PubMaster::PubMaster(const std::vector<const char *> &service_list) {
|
PubMaster::PubMaster(const std::vector<const char *> &service_list) {
|
||||||
for (auto name : service_list) {
|
for (auto name : service_list) {
|
||||||
assert(services.count(name) > 0);
|
assert(services.count(name) > 0);
|
||||||
service serv = services.at(std::string(name));
|
PubSocket *socket = PubSocket::create(message_context.context(), name);
|
||||||
PubSocket *socket = PubSocket::create(message_context.context(), name, true, serv.queue_size);
|
|
||||||
assert(socket);
|
assert(socket);
|
||||||
sockets_[name] = socket;
|
sockets_[name] = socket;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import numbers
|
|||||||
import random
|
import random
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from openpilot.common.parameterized import parameterized
|
from parameterized import parameterized
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from cereal import log, car
|
from cereal import log, car
|
||||||
@@ -30,7 +30,7 @@ def zmq_sleep(t=1):
|
|||||||
|
|
||||||
# TODO: this should take any capnp struct and returrn a msg with random populated data
|
# TODO: this should take any capnp struct and returrn a msg with random populated data
|
||||||
def random_carstate():
|
def random_carstate():
|
||||||
fields = ["vEgo", "aEgo", "brake", "steeringAngleDeg"]
|
fields = ["vEgo", "aEgo", "gas", "steeringAngleDeg"]
|
||||||
msg = messaging.new_message("carState")
|
msg = messaging.new_message("carState")
|
||||||
cs = msg.carState
|
cs = msg.carState
|
||||||
for f in fields:
|
for f in fields:
|
||||||
@@ -177,8 +177,8 @@ class TestMessaging:
|
|||||||
|
|
||||||
# wait 5 socket timeouts before sending
|
# wait 5 socket timeouts before sending
|
||||||
msg = random_carstate()
|
msg = random_carstate()
|
||||||
start_time = time.monotonic()
|
|
||||||
delayed_send(sock_timeout*5, pub_sock, msg.to_bytes())
|
delayed_send(sock_timeout*5, pub_sock, msg.to_bytes())
|
||||||
|
start_time = time.monotonic()
|
||||||
recvd = messaging.recv_one_retry(sub_sock)
|
recvd = messaging.recv_one_retry(sub_sock)
|
||||||
assert (time.monotonic() - start_time) >= sock_timeout*5
|
assert (time.monotonic() - start_time) >= sock_timeout*5
|
||||||
assert isinstance(recvd, capnp._DynamicStructReader)
|
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, \
|
from cereal.messaging.tests.test_messaging import events, random_sock, random_socks, \
|
||||||
random_bytes, random_carstate, assert_carstate, \
|
random_bytes, random_carstate, assert_carstate, \
|
||||||
zmq_sleep
|
zmq_sleep
|
||||||
from cereal.services import SERVICE_LIST
|
|
||||||
|
|
||||||
|
|
||||||
class TestSubMaster:
|
class TestSubMaster:
|
||||||
@@ -27,9 +26,7 @@ class TestSubMaster:
|
|||||||
sm = messaging.SubMaster(socks)
|
sm = messaging.SubMaster(socks)
|
||||||
assert sm.frame == -1
|
assert sm.frame == -1
|
||||||
assert not any(sm.updated.values())
|
assert not any(sm.updated.values())
|
||||||
assert not any(sm.seen.values())
|
assert not any(sm.alive.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 all(t == 0. for t in sm.recv_time.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(f == 0 for f in sm.recv_frame.values())
|
||||||
assert all(t == 0 for t in sm.logMonoTime.values())
|
assert all(t == 0 for t in sm.logMonoTime.values())
|
||||||
@@ -86,7 +83,6 @@ class TestSubMaster:
|
|||||||
"cameraOdometry": (20, 10),
|
"cameraOdometry": (20, 10),
|
||||||
"liveCalibration": (4, 4),
|
"liveCalibration": (4, 4),
|
||||||
"carParams": (None, None),
|
"carParams": (None, None),
|
||||||
"userBookmark": (None, None),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for service, (max_freq, min_freq) in checks.items():
|
for service, (max_freq, min_freq) in checks.items():
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from openpilot.common.parameterized import parameterized
|
from parameterized import parameterized
|
||||||
|
|
||||||
import cereal.services as services
|
import cereal.services as services
|
||||||
from cereal.services import SERVICE_LIST
|
from cereal.services import SERVICE_LIST
|
||||||
|
|||||||
+23
-38
@@ -1,56 +1,48 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from enum import IntEnum
|
|
||||||
from typing import Optional
|
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:
|
class Service:
|
||||||
def __init__(self, should_log: bool, frequency: float, decimation: Optional[int] = None,
|
def __init__(self, should_log: bool, frequency: float, decimation: Optional[int] = None):
|
||||||
queue_size: QueueSize = QueueSize.SMALL):
|
|
||||||
self.should_log = should_log
|
self.should_log = should_log
|
||||||
self.frequency = frequency
|
self.frequency = frequency
|
||||||
self.decimation = decimation
|
self.decimation = decimation
|
||||||
self.queue_size = queue_size
|
|
||||||
|
|
||||||
|
|
||||||
_services: dict[str, tuple] = {
|
_services: dict[str, tuple] = {
|
||||||
# service: (should_log, frequency, qlog decimation (optional))
|
# service: (should_log, frequency, qlog decimation (optional))
|
||||||
# note: the "EncodeIdx" packets will still be in the log
|
# note: the "EncodeIdx" packets will still be in the log
|
||||||
"gyroscope": (True, 104., 104),
|
"gyroscope": (True, 104., 104),
|
||||||
|
"gyroscope2": (True, 100., 100),
|
||||||
"accelerometer": (True, 104., 104),
|
"accelerometer": (True, 104., 104),
|
||||||
|
"accelerometer2": (True, 100., 100),
|
||||||
"magnetometer": (True, 25.),
|
"magnetometer": (True, 25.),
|
||||||
"lightSensor": (True, 100., 100),
|
"lightSensor": (True, 100., 100),
|
||||||
"temperatureSensor": (True, 2., 200),
|
"temperatureSensor": (True, 2., 200),
|
||||||
|
"temperatureSensor2": (True, 2., 200),
|
||||||
"gpsNMEA": (True, 9.),
|
"gpsNMEA": (True, 9.),
|
||||||
"deviceState": (True, 2., 1),
|
"deviceState": (True, 2., 1),
|
||||||
"touch": (True, 20., 1),
|
"touch": (True, 20., 1),
|
||||||
"can": (True, 100., 2053, QueueSize.BIG), # decimation gives ~3 msgs in a full segment
|
"can": (True, 100., 2053), # decimation gives ~3 msgs in a full segment
|
||||||
"controlsState": (True, 100., 10, QueueSize.MEDIUM),
|
"controlsState": (True, 100., 10),
|
||||||
"selfdriveState": (True, 100., 10),
|
"selfdriveState": (True, 100., 10),
|
||||||
"pandaStates": (True, 10., 1),
|
"pandaStates": (True, 10., 1),
|
||||||
"peripheralState": (True, 2., 1),
|
"peripheralState": (True, 2., 1),
|
||||||
"radarState": (True, 20., 5),
|
"radarState": (True, 20., 5),
|
||||||
"roadEncodeIdx": (False, 20., 1),
|
"roadEncodeIdx": (False, 20., 1),
|
||||||
"liveTracks": (True, 20.),
|
"liveTracks": (True, 20.),
|
||||||
"sendcan": (True, 100., 139, QueueSize.MEDIUM),
|
"sendcan": (True, 100., 139),
|
||||||
"logMessage": (True, 0.),
|
"logMessage": (True, 0.),
|
||||||
"errorLogMessage": (True, 0., 1),
|
"errorLogMessage": (True, 0., 1),
|
||||||
"liveCalibration": (True, 4., 4),
|
"liveCalibration": (True, 4., 4),
|
||||||
"liveTorqueParameters": (True, 4., 1),
|
"liveTorqueParameters": (True, 4., 1),
|
||||||
"liveDelay": (True, 4., 1),
|
|
||||||
"androidLog": (True, 0.),
|
"androidLog": (True, 0.),
|
||||||
"carState": (True, 100., 10),
|
"carState": (True, 100., 10),
|
||||||
"carControl": (True, 100., 10),
|
"carControl": (True, 100., 10),
|
||||||
"carOutput": (True, 100., 10),
|
"carOutput": (True, 100., 10),
|
||||||
"longitudinalPlan": (True, 20., 10),
|
"longitudinalPlan": (True, 20., 10),
|
||||||
"driverAssistance": (True, 20., 20),
|
"driverAssistance": (True, 20., 20),
|
||||||
"procLog": (True, 0.5, 15, QueueSize.BIG),
|
"procLog": (True, 0.5, 15),
|
||||||
"gpsLocationExternal": (True, 10., 10),
|
"gpsLocationExternal": (True, 10., 10),
|
||||||
"gpsLocation": (True, 1., 1),
|
"gpsLocation": (True, 1., 1),
|
||||||
"ubloxGnss": (True, 10.),
|
"ubloxGnss": (True, 10.),
|
||||||
@@ -72,46 +64,39 @@ _services: dict[str, tuple] = {
|
|||||||
"wideRoadEncodeIdx": (False, 20., 1),
|
"wideRoadEncodeIdx": (False, 20., 1),
|
||||||
"wideRoadCameraState": (True, 20., 20),
|
"wideRoadCameraState": (True, 20., 20),
|
||||||
"drivingModelData": (True, 20., 10),
|
"drivingModelData": (True, 20., 10),
|
||||||
"modelV2": (True, 20., None, QueueSize.BIG),
|
"modelV2": (True, 20.),
|
||||||
"managerState": (True, 2., 1),
|
"managerState": (True, 2., 1),
|
||||||
"uploaderState": (True, 0., 1),
|
"uploaderState": (True, 0., 1),
|
||||||
"navInstruction": (True, 1., 10),
|
"navInstruction": (True, 1., 10),
|
||||||
"navRoute": (True, 0.),
|
"navRoute": (True, 0.),
|
||||||
"navThumbnail": (True, 0.),
|
"navThumbnail": (True, 0.),
|
||||||
"qRoadEncodeIdx": (False, 20.),
|
"qRoadEncodeIdx": (False, 20.),
|
||||||
"userBookmark": (True, 0., 1),
|
"userFlag": (True, 0., 1),
|
||||||
"soundPressure": (True, 10., 10),
|
"microphone": (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
|
# sunnypilot
|
||||||
"modelManagerSP": (False, 1., 1, QueueSize.BIG),
|
"modelManagerSP": (False, 1., 1),
|
||||||
"backupManagerSP": (False, 1., 1, QueueSize.BIG),
|
"backupManagerSP": (False, 1., 1),
|
||||||
"selfdriveStateSP": (True, 100., 10),
|
"selfdriveStateSP": (True, 100., 10),
|
||||||
"longitudinalPlanSP": (True, 20., 10),
|
"longitudinalPlanSP": (True, 20., 10),
|
||||||
"onroadEventsSP": (True, 1., 1),
|
"onroadEventsSP": (True, 1., 1),
|
||||||
"carParamsSP": (True, 0.02, 1),
|
"carParamsSP": (True, 0.02, 1),
|
||||||
"carControlSP": (True, 100., 10),
|
"carControlSP": (True, 100., 10),
|
||||||
"carStateSP": (True, 100., 10),
|
|
||||||
"liveMapDataSP": (True, 1., 1),
|
|
||||||
"modelDataV2SP": (True, 20., None, QueueSize.BIG),
|
|
||||||
"liveLocationKalman": (True, 20.),
|
|
||||||
|
|
||||||
# debug
|
# debug
|
||||||
"uiDebug": (True, 0., 1),
|
"uiDebug": (True, 0., 1),
|
||||||
"testJoystick": (True, 0.),
|
"testJoystick": (True, 0.),
|
||||||
"alertDebug": (True, 20., 5),
|
"alertDebug": (True, 20., 5),
|
||||||
|
"roadEncodeData": (False, 20.),
|
||||||
|
"driverEncodeData": (False, 20.),
|
||||||
|
"wideRoadEncodeData": (False, 20.),
|
||||||
|
"qRoadEncodeData": (False, 20.),
|
||||||
"livestreamWideRoadEncodeIdx": (False, 20.),
|
"livestreamWideRoadEncodeIdx": (False, 20.),
|
||||||
"livestreamRoadEncodeIdx": (False, 20.),
|
"livestreamRoadEncodeIdx": (False, 20.),
|
||||||
"livestreamDriverEncodeIdx": (False, 20.),
|
"livestreamDriverEncodeIdx": (False, 20.),
|
||||||
"livestreamWideRoadEncodeData": (False, 20., None, QueueSize.MEDIUM),
|
"livestreamWideRoadEncodeData": (False, 20.),
|
||||||
"livestreamRoadEncodeData": (False, 20., None, QueueSize.MEDIUM),
|
"livestreamRoadEncodeData": (False, 20.),
|
||||||
"livestreamDriverEncodeData": (False, 20., None, QueueSize.MEDIUM),
|
"livestreamDriverEncodeData": (False, 20.),
|
||||||
"customReservedRawData0": (True, 0.),
|
"customReservedRawData0": (True, 0.),
|
||||||
"customReservedRawData1": (True, 0.),
|
"customReservedRawData1": (True, 0.),
|
||||||
"customReservedRawData2": (True, 0.),
|
"customReservedRawData2": (True, 0.),
|
||||||
@@ -129,13 +114,13 @@ def build_header():
|
|||||||
h += "#include <map>\n"
|
h += "#include <map>\n"
|
||||||
h += "#include <string>\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"
|
h += "static std::map<std::string, service> services = {\n"
|
||||||
for k, v in SERVICE_LIST.items():
|
for k, v in SERVICE_LIST.items():
|
||||||
should_log = "true" if v.should_log else "false"
|
should_log = "true" if v.should_log else "false"
|
||||||
decimation = -1 if v.decimation is None else v.decimation
|
decimation = -1 if v.decimation is None else v.decimation
|
||||||
h += ' { "%s", {"%s", %s, %f, %d, %d}},\n' % \
|
h += ' { "%s", {"%s", %s, %d, %d}},\n' % \
|
||||||
(k, k, should_log, v.frequency, decimation, v.queue_size)
|
(k, k, should_log, v.frequency, decimation)
|
||||||
h += "};\n"
|
h += "};\n"
|
||||||
|
|
||||||
h += "#endif\n"
|
h += "#endif\n"
|
||||||
|
|||||||
+13
@@ -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"
|
||||||
+19
-3
@@ -4,11 +4,22 @@ common_libs = [
|
|||||||
'params.cc',
|
'params.cc',
|
||||||
'swaglog.cc',
|
'swaglog.cc',
|
||||||
'util.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")
|
_common = env.Library('common', common_libs, LIBS="json11")
|
||||||
Export('_common')
|
|
||||||
|
files = [
|
||||||
|
'clutil.cc',
|
||||||
|
]
|
||||||
|
|
||||||
|
_gpucommon = env.Library('gpucommon', files)
|
||||||
|
Export('_common', '_gpucommon')
|
||||||
|
|
||||||
if GetOption('extras'):
|
if GetOption('extras'):
|
||||||
env.Program('tests/test_common',
|
env.Program('tests/test_common',
|
||||||
@@ -18,6 +29,11 @@ if GetOption('extras'):
|
|||||||
# Cython bindings
|
# Cython bindings
|
||||||
params_python = envCython.Program('params_pyx.so', 'params_pyx.pyx', LIBS=envCython['LIBS'] + [_common, 'zmq', 'json11'])
|
params_python = envCython.Program('params_pyx.so', 'params_pyx.pyx', LIBS=envCython['LIBS'] + [_common, 'zmq', 'json11'])
|
||||||
|
|
||||||
common_python = [params_python]
|
SConscript([
|
||||||
|
'transformations/SConscript',
|
||||||
|
])
|
||||||
|
|
||||||
|
Import('transformations_python')
|
||||||
|
common_python = [params_python, transformations_python]
|
||||||
|
|
||||||
Export('common_python')
|
Export('common_python')
|
||||||
|
|||||||
@@ -14,13 +14,9 @@ class Api:
|
|||||||
def post(self, *args, **kwargs):
|
def post(self, *args, **kwargs):
|
||||||
return self.service.post(*args, **kwargs)
|
return self.service.post(*args, **kwargs)
|
||||||
|
|
||||||
def get_token(self, payload_extra=None, expiry_hours=1):
|
def get_token(self, expiry_hours=1):
|
||||||
return self.service.get_token(payload_extra, expiry_hours)
|
return self.service.get_token(expiry_hours)
|
||||||
|
|
||||||
|
|
||||||
def api_get(endpoint, method='GET', timeout=None, access_token=None, session=None, **params):
|
def api_get(endpoint, method='GET', timeout=None, access_token=None, **params):
|
||||||
return CommaConnectApi(None).api_get(endpoint, method, timeout, access_token, session, **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()
|
|
||||||
|
|||||||
+8
-24
@@ -1,22 +1,18 @@
|
|||||||
import jwt
|
import jwt
|
||||||
import os
|
|
||||||
import requests
|
import requests
|
||||||
import unicodedata
|
import unicodedata
|
||||||
from datetime import datetime, timedelta, UTC
|
from datetime import datetime, timedelta, UTC
|
||||||
from openpilot.system.hardware.hw import Paths
|
from openpilot.system.hardware.hw import Paths
|
||||||
from openpilot.system.version import get_version
|
from openpilot.system.version import get_version
|
||||||
|
|
||||||
# name: jwt signature algorithm
|
|
||||||
KEYS = {"id_rsa": "RS256",
|
|
||||||
"id_ecdsa": "ES256"}
|
|
||||||
|
|
||||||
|
|
||||||
class BaseApi:
|
class BaseApi:
|
||||||
def __init__(self, dongle_id, api_host, user_agent="openpilot-"):
|
def __init__(self, dongle_id, api_host, user_agent="openpilot-"):
|
||||||
self.dongle_id = dongle_id
|
self.dongle_id = dongle_id
|
||||||
self.api_host = api_host
|
self.api_host = api_host
|
||||||
self.user_agent = user_agent
|
self.user_agent = user_agent
|
||||||
self.jwt_algorithm, self.private_key, _ = self.get_key_pair()
|
with open(f'{Paths.persist_root()}/comma/id_rsa') as f:
|
||||||
|
self.private_key = f.read()
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
def get(self, *args, **kwargs):
|
||||||
return self.request('GET', *args, **kwargs)
|
return self.request('GET', *args, **kwargs)
|
||||||
@@ -27,7 +23,7 @@ class BaseApi:
|
|||||||
def request(self, method, endpoint, timeout=None, access_token=None, **params):
|
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)
|
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):
|
def _get_token(self, expiry_hours=1, **extra_payload):
|
||||||
now = datetime.now(UTC).replace(tzinfo=None)
|
now = datetime.now(UTC).replace(tzinfo=None)
|
||||||
payload = {
|
payload = {
|
||||||
'identity': self.dongle_id,
|
'identity': self.dongle_id,
|
||||||
@@ -36,22 +32,20 @@ class BaseApi:
|
|||||||
'exp': now + timedelta(hours=expiry_hours),
|
'exp': now + timedelta(hours=expiry_hours),
|
||||||
**extra_payload
|
**extra_payload
|
||||||
}
|
}
|
||||||
if payload_extra is not None:
|
token = jwt.encode(payload, self.private_key, algorithm='RS256')
|
||||||
payload.update(payload_extra)
|
|
||||||
token = jwt.encode(payload, self.private_key, algorithm=self.jwt_algorithm)
|
|
||||||
if isinstance(token, bytes):
|
if isinstance(token, bytes):
|
||||||
token = token.decode('utf8')
|
token = token.decode('utf8')
|
||||||
return token
|
return token
|
||||||
|
|
||||||
def get_token(self, payload_extra=None, expiry_hours=1):
|
def get_token(self, expiry_hours=1):
|
||||||
return self._get_token(payload_extra, expiry_hours)
|
return self._get_token(expiry_hours)
|
||||||
|
|
||||||
def remove_non_ascii_chars(self, text):
|
def remove_non_ascii_chars(self, text):
|
||||||
normalized_text = unicodedata.normalize('NFD', text)
|
normalized_text = unicodedata.normalize('NFD', text)
|
||||||
ascii_encoded_text = normalized_text.encode('ascii', 'ignore')
|
ascii_encoded_text = normalized_text.encode('ascii', 'ignore')
|
||||||
return ascii_encoded_text.decode()
|
return ascii_encoded_text.decode()
|
||||||
|
|
||||||
def api_get(self, endpoint, method='GET', timeout=None, access_token=None, session=None, json=None, **params):
|
def api_get(self, endpoint, method='GET', timeout=None, access_token=None, json=None, **params):
|
||||||
headers = {}
|
headers = {}
|
||||||
if access_token is not None:
|
if access_token is not None:
|
||||||
headers['Authorization'] = "JWT " + access_token
|
headers['Authorization'] = "JWT " + access_token
|
||||||
@@ -59,14 +53,4 @@ class BaseApi:
|
|||||||
version = self.remove_non_ascii_chars(get_version())
|
version = self.remove_non_ascii_chars(get_version())
|
||||||
headers['User-Agent'] = self.user_agent + version
|
headers['User-Agent'] = self.user_agent + version
|
||||||
|
|
||||||
# TODO: add session to Api
|
return requests.request(method, f"{self.api_host}/{endpoint}", timeout=timeout, headers=headers, json=json, params=params)
|
||||||
req = requests if session is None else session
|
|
||||||
return req.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
|
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
#include "common/clutil.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "common/util.h"
|
||||||
|
#include "common/swaglog.h"
|
||||||
|
|
||||||
|
namespace { // helper functions
|
||||||
|
|
||||||
|
template <typename Func, typename Id, typename Name>
|
||||||
|
std::string get_info(Func get_info_func, Id id, Name param_name) {
|
||||||
|
size_t size = 0;
|
||||||
|
CL_CHECK(get_info_func(id, param_name, 0, NULL, &size));
|
||||||
|
std::string info(size, '\0');
|
||||||
|
CL_CHECK(get_info_func(id, param_name, size, info.data(), NULL));
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
inline std::string get_platform_info(cl_platform_id id, cl_platform_info name) { return get_info(&clGetPlatformInfo, id, name); }
|
||||||
|
inline std::string get_device_info(cl_device_id id, cl_device_info name) { return get_info(&clGetDeviceInfo, id, name); }
|
||||||
|
|
||||||
|
void cl_print_info(cl_platform_id platform, cl_device_id device) {
|
||||||
|
size_t work_group_size = 0;
|
||||||
|
cl_device_type device_type = 0;
|
||||||
|
clGetDeviceInfo(device, CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(work_group_size), &work_group_size, NULL);
|
||||||
|
clGetDeviceInfo(device, CL_DEVICE_TYPE, sizeof(device_type), &device_type, NULL);
|
||||||
|
const char *type_str = "Other...";
|
||||||
|
switch (device_type) {
|
||||||
|
case CL_DEVICE_TYPE_CPU: type_str ="CL_DEVICE_TYPE_CPU"; break;
|
||||||
|
case CL_DEVICE_TYPE_GPU: type_str = "CL_DEVICE_TYPE_GPU"; break;
|
||||||
|
case CL_DEVICE_TYPE_ACCELERATOR: type_str = "CL_DEVICE_TYPE_ACCELERATOR"; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGD("vendor: %s", get_platform_info(platform, CL_PLATFORM_VENDOR).c_str());
|
||||||
|
LOGD("platform version: %s", get_platform_info(platform, CL_PLATFORM_VERSION).c_str());
|
||||||
|
LOGD("profile: %s", get_platform_info(platform, CL_PLATFORM_PROFILE).c_str());
|
||||||
|
LOGD("extensions: %s", get_platform_info(platform, CL_PLATFORM_EXTENSIONS).c_str());
|
||||||
|
LOGD("name: %s", get_device_info(device, CL_DEVICE_NAME).c_str());
|
||||||
|
LOGD("device version: %s", get_device_info(device, CL_DEVICE_VERSION).c_str());
|
||||||
|
LOGD("max work group size: %zu", work_group_size);
|
||||||
|
LOGD("type = %d, %s", (int)device_type, type_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cl_print_build_errors(cl_program program, cl_device_id device) {
|
||||||
|
cl_build_status status;
|
||||||
|
clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_STATUS, sizeof(status), &status, NULL);
|
||||||
|
size_t log_size;
|
||||||
|
clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, 0, NULL, &log_size);
|
||||||
|
std::string log(log_size, '\0');
|
||||||
|
clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, log_size, &log[0], NULL);
|
||||||
|
|
||||||
|
LOGE("build failed; status=%d, log: %s", status, log.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
cl_device_id cl_get_device_id(cl_device_type device_type) {
|
||||||
|
cl_uint num_platforms = 0;
|
||||||
|
CL_CHECK(clGetPlatformIDs(0, NULL, &num_platforms));
|
||||||
|
std::unique_ptr<cl_platform_id[]> platform_ids = std::make_unique<cl_platform_id[]>(num_platforms);
|
||||||
|
CL_CHECK(clGetPlatformIDs(num_platforms, &platform_ids[0], NULL));
|
||||||
|
|
||||||
|
for (size_t i = 0; i < num_platforms; ++i) {
|
||||||
|
LOGD("platform[%zu] CL_PLATFORM_NAME: %s", i, get_platform_info(platform_ids[i], CL_PLATFORM_NAME).c_str());
|
||||||
|
|
||||||
|
// Get first device
|
||||||
|
if (cl_device_id device_id = NULL; clGetDeviceIDs(platform_ids[i], device_type, 1, &device_id, NULL) == 0 && device_id) {
|
||||||
|
cl_print_info(platform_ids[i], device_id);
|
||||||
|
return device_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOGE("No valid openCL platform found");
|
||||||
|
assert(0);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
cl_context cl_create_context(cl_device_id device_id) {
|
||||||
|
return CL_CHECK_ERR(clCreateContext(NULL, 1, &device_id, NULL, NULL, &err));
|
||||||
|
}
|
||||||
|
|
||||||
|
void cl_release_context(cl_context context) {
|
||||||
|
clReleaseContext(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
cl_program cl_program_from_file(cl_context ctx, cl_device_id device_id, const char* path, const char* args) {
|
||||||
|
return cl_program_from_source(ctx, device_id, util::read_file(path), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
cl_program cl_program_from_source(cl_context ctx, cl_device_id device_id, const std::string& src, const char* args) {
|
||||||
|
const char *csrc = src.c_str();
|
||||||
|
cl_program prg = CL_CHECK_ERR(clCreateProgramWithSource(ctx, 1, &csrc, NULL, &err));
|
||||||
|
if (int err = clBuildProgram(prg, 1, &device_id, args, NULL, NULL); err != 0) {
|
||||||
|
cl_print_build_errors(prg, device_id);
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
return prg;
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <OpenCL/cl.h>
|
||||||
|
#else
|
||||||
|
#include <CL/cl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#define CL_CHECK(_expr) \
|
||||||
|
do { \
|
||||||
|
assert(CL_SUCCESS == (_expr)); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define CL_CHECK_ERR(_expr) \
|
||||||
|
({ \
|
||||||
|
cl_int err = CL_INVALID_VALUE; \
|
||||||
|
__typeof__(_expr) _ret = _expr; \
|
||||||
|
assert(_ret&& err == CL_SUCCESS); \
|
||||||
|
_ret; \
|
||||||
|
})
|
||||||
|
|
||||||
|
cl_device_id cl_get_device_id(cl_device_type device_type);
|
||||||
|
cl_context cl_create_context(cl_device_id device_id);
|
||||||
|
void cl_release_context(cl_context context);
|
||||||
|
cl_program cl_program_from_source(cl_context ctx, cl_device_id device_id, const std::string& src, const char* args = nullptr);
|
||||||
|
cl_program cl_program_from_file(cl_context ctx, cl_device_id device_id, const char* path, const char* args);
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
# conversions
|
class Conversions:
|
||||||
class CV:
|
|
||||||
# Speed
|
# Speed
|
||||||
MPH_TO_KPH = 1.609344
|
MPH_TO_KPH = 1.609344
|
||||||
KPH_TO_MPH = 1. / MPH_TO_KPH
|
KPH_TO_MPH = 1. / MPH_TO_KPH
|
||||||
@@ -18,6 +17,3 @@ class CV:
|
|||||||
|
|
||||||
# Mass
|
# Mass
|
||||||
LB_TO_KG = 0.453592
|
LB_TO_KG = 0.453592
|
||||||
|
|
||||||
|
|
||||||
ACCELERATION_DUE_TO_GRAVITY = 9.81 # m/s^2
|
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import platform
|
||||||
|
|
||||||
|
|
||||||
|
def suffix():
|
||||||
|
if platform.system() == "Darwin":
|
||||||
|
return ".dylib"
|
||||||
|
else:
|
||||||
|
return ".so"
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import math
|
|
||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
CHUNK_SIZE = 45 * 1024 * 1024 # 45MB, under GitHub's 50MB limit
|
|
||||||
|
|
||||||
def get_chunk_name(name, idx, num_chunks):
|
|
||||||
return f"{name}.chunk{idx+1:02d}of{num_chunks:02d}"
|
|
||||||
|
|
||||||
def get_manifest_path(name):
|
|
||||||
return f"{name}.chunkmanifest"
|
|
||||||
|
|
||||||
def get_chunk_paths(path, file_size):
|
|
||||||
num_chunks = math.ceil(file_size / CHUNK_SIZE)
|
|
||||||
return [get_manifest_path(path)] + [get_chunk_name(path, i, num_chunks) for i in range(num_chunks)]
|
|
||||||
|
|
||||||
def chunk_file(path, targets):
|
|
||||||
manifest_path, *chunk_paths = targets
|
|
||||||
with open(path, 'rb') as f:
|
|
||||||
data = f.read()
|
|
||||||
actual_num_chunks = max(1, math.ceil(len(data) / CHUNK_SIZE))
|
|
||||||
assert len(chunk_paths) >= actual_num_chunks, f"Allowed {len(chunk_paths)} chunks but needs at least {actual_num_chunks}, for path {path}"
|
|
||||||
for i, chunk_path in enumerate(chunk_paths):
|
|
||||||
with open(chunk_path, 'wb') as f:
|
|
||||||
f.write(data[i * CHUNK_SIZE:(i + 1) * CHUNK_SIZE])
|
|
||||||
Path(manifest_path).write_text(str(len(chunk_paths)))
|
|
||||||
os.remove(path)
|
|
||||||
|
|
||||||
|
|
||||||
def read_file_chunked(path):
|
|
||||||
manifest_path = get_manifest_path(path)
|
|
||||||
if os.path.isfile(manifest_path):
|
|
||||||
num_chunks = int(Path(manifest_path).read_text().strip())
|
|
||||||
return b''.join(Path(get_chunk_name(path, i, num_chunks)).read_bytes() for i in range(num_chunks))
|
|
||||||
if os.path.isfile(path):
|
|
||||||
return Path(path).read_bytes()
|
|
||||||
raise FileNotFoundError(path)
|
|
||||||
@@ -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
-17
@@ -1,4 +1,5 @@
|
|||||||
class FirstOrderFilter:
|
class FirstOrderFilter:
|
||||||
|
# first order filter
|
||||||
def __init__(self, x0, rc, dt, initialized=True):
|
def __init__(self, x0, rc, dt, initialized=True):
|
||||||
self.x = x0
|
self.x = x0
|
||||||
self.dt = dt
|
self.dt = dt
|
||||||
@@ -15,20 +16,3 @@ class FirstOrderFilter:
|
|||||||
self.initialized = True
|
self.initialized = True
|
||||||
self.x = x
|
self.x = x
|
||||||
return self.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
|
|
||||||
|
|||||||
+7
-7
@@ -1,30 +1,30 @@
|
|||||||
from functools import cache
|
from functools import cache
|
||||||
import subprocess
|
import subprocess
|
||||||
from openpilot.common.utils import run_cmd, run_cmd_default
|
from openpilot.common.run import run_cmd, run_cmd_default
|
||||||
|
|
||||||
|
|
||||||
@cache
|
@cache
|
||||||
def get_commit(cwd: str | None = None, branch: str = "HEAD") -> str:
|
def get_commit(cwd: str = None, branch: str = "HEAD") -> str:
|
||||||
return run_cmd_default(["git", "rev-parse", branch], cwd=cwd)
|
return run_cmd_default(["git", "rev-parse", branch], cwd=cwd)
|
||||||
|
|
||||||
|
|
||||||
@cache
|
@cache
|
||||||
def get_commit_date(cwd: str | None = None, commit: str = "HEAD") -> str:
|
def get_commit_date(cwd: str = None, commit: str = "HEAD") -> str:
|
||||||
return run_cmd_default(["git", "show", "--no-patch", "--format='%ct %ci'", commit], cwd=cwd)
|
return run_cmd_default(["git", "show", "--no-patch", "--format='%ct %ci'", commit], cwd=cwd)
|
||||||
|
|
||||||
|
|
||||||
@cache
|
@cache
|
||||||
def get_short_branch(cwd: str | None = None) -> str:
|
def get_short_branch(cwd: str = None) -> str:
|
||||||
return run_cmd_default(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=cwd)
|
return run_cmd_default(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=cwd)
|
||||||
|
|
||||||
|
|
||||||
@cache
|
@cache
|
||||||
def get_branch(cwd: str | None = None) -> str:
|
def get_branch(cwd: str = None) -> str:
|
||||||
return run_cmd_default(["git", "rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"], cwd=cwd)
|
return run_cmd_default(["git", "rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"], cwd=cwd)
|
||||||
|
|
||||||
|
|
||||||
@cache
|
@cache
|
||||||
def get_origin(cwd: str | None = None) -> str:
|
def get_origin(cwd: str = None) -> str:
|
||||||
try:
|
try:
|
||||||
local_branch = run_cmd(["git", "name-rev", "--name-only", "HEAD"], cwd=cwd)
|
local_branch = run_cmd(["git", "name-rev", "--name-only", "HEAD"], cwd=cwd)
|
||||||
tracking_remote = run_cmd(["git", "config", "branch." + local_branch + ".remote"], cwd=cwd)
|
tracking_remote = run_cmd(["git", "config", "branch." + local_branch + ".remote"], cwd=cwd)
|
||||||
@@ -34,7 +34,7 @@ def get_origin(cwd: str | None = None) -> str:
|
|||||||
|
|
||||||
|
|
||||||
@cache
|
@cache
|
||||||
def get_normalized_origin(cwd: str | None = None) -> str:
|
def get_normalized_origin(cwd: str = None) -> str:
|
||||||
return get_origin(cwd) \
|
return get_origin(cwd) \
|
||||||
.replace("git@", "", 1) \
|
.replace("git@", "", 1) \
|
||||||
.replace(".git", "", 1) \
|
.replace(".git", "", 1) \
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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 os
|
||||||
import fcntl
|
|
||||||
import ctypes
|
|
||||||
from functools import cache
|
from functools import cache
|
||||||
|
|
||||||
def gpio_init(pin: int, output: bool) -> None:
|
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):
|
if irq.isdigit() and action in get_irq_action(irq):
|
||||||
ret.append(irq)
|
ret.append(irq)
|
||||||
return ret
|
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)
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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);
|
||||||
|
};
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
import os
|
|
||||||
import fcntl
|
|
||||||
import ctypes
|
|
||||||
|
|
||||||
# I2C constants from /usr/include/linux/i2c-dev.h
|
|
||||||
I2C_SLAVE = 0x0703
|
|
||||||
I2C_SLAVE_FORCE = 0x0706
|
|
||||||
I2C_SMBUS = 0x0720
|
|
||||||
|
|
||||||
# SMBus transfer types
|
|
||||||
I2C_SMBUS_READ = 1
|
|
||||||
I2C_SMBUS_WRITE = 0
|
|
||||||
I2C_SMBUS_BYTE_DATA = 2
|
|
||||||
I2C_SMBUS_I2C_BLOCK_DATA = 8
|
|
||||||
|
|
||||||
I2C_SMBUS_BLOCK_MAX = 32
|
|
||||||
|
|
||||||
|
|
||||||
class _I2cSmbusData(ctypes.Union):
|
|
||||||
_fields_ = [
|
|
||||||
("byte", ctypes.c_uint8),
|
|
||||||
("word", ctypes.c_uint16),
|
|
||||||
("block", ctypes.c_uint8 * (I2C_SMBUS_BLOCK_MAX + 2)),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class _I2cSmbusIoctlData(ctypes.Structure):
|
|
||||||
_fields_ = [
|
|
||||||
("read_write", ctypes.c_uint8),
|
|
||||||
("command", ctypes.c_uint8),
|
|
||||||
("size", ctypes.c_uint32),
|
|
||||||
("data", ctypes.POINTER(_I2cSmbusData)),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class SMBus:
|
|
||||||
def __init__(self, bus: int):
|
|
||||||
self._fd = os.open(f'/dev/i2c-{bus}', os.O_RDWR)
|
|
||||||
|
|
||||||
def __enter__(self) -> 'SMBus':
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __exit__(self, *args) -> None:
|
|
||||||
self.close()
|
|
||||||
|
|
||||||
def close(self) -> None:
|
|
||||||
if hasattr(self, '_fd') and self._fd >= 0:
|
|
||||||
os.close(self._fd)
|
|
||||||
self._fd = -1
|
|
||||||
|
|
||||||
def _set_address(self, addr: int, force: bool = False) -> None:
|
|
||||||
ioctl_arg = I2C_SLAVE_FORCE if force else I2C_SLAVE
|
|
||||||
fcntl.ioctl(self._fd, ioctl_arg, addr)
|
|
||||||
|
|
||||||
def _smbus_access(self, read_write: int, command: int, size: int, data: _I2cSmbusData) -> None:
|
|
||||||
ioctl_data = _I2cSmbusIoctlData(read_write, command, size, ctypes.pointer(data))
|
|
||||||
fcntl.ioctl(self._fd, I2C_SMBUS, ioctl_data)
|
|
||||||
|
|
||||||
def read_byte_data(self, addr: int, register: int, force: bool = False) -> int:
|
|
||||||
self._set_address(addr, force)
|
|
||||||
data = _I2cSmbusData()
|
|
||||||
self._smbus_access(I2C_SMBUS_READ, register, I2C_SMBUS_BYTE_DATA, data)
|
|
||||||
return int(data.byte)
|
|
||||||
|
|
||||||
def write_byte_data(self, addr: int, register: int, value: int, force: bool = False) -> None:
|
|
||||||
self._set_address(addr, force)
|
|
||||||
data = _I2cSmbusData()
|
|
||||||
data.byte = value & 0xFF
|
|
||||||
self._smbus_access(I2C_SMBUS_WRITE, register, I2C_SMBUS_BYTE_DATA, data)
|
|
||||||
|
|
||||||
def read_i2c_block_data(self, addr: int, register: int, length: int, force: bool = False) -> list[int]:
|
|
||||||
self._set_address(addr, force)
|
|
||||||
if not (0 <= length <= I2C_SMBUS_BLOCK_MAX):
|
|
||||||
raise ValueError(f"length must be 0..{I2C_SMBUS_BLOCK_MAX}")
|
|
||||||
|
|
||||||
data = _I2cSmbusData()
|
|
||||||
data.block[0] = length
|
|
||||||
self._smbus_access(I2C_SMBUS_READ, register, I2C_SMBUS_I2C_BLOCK_DATA, data)
|
|
||||||
read_len = int(data.block[0]) or length
|
|
||||||
read_len = min(read_len, length)
|
|
||||||
return [int(b) for b in data.block[1 : read_len + 1]]
|
|
||||||
@@ -199,6 +199,7 @@ class SwagLogger(logging.Logger):
|
|||||||
co = f.f_code
|
co = f.f_code
|
||||||
filename = os.path.normcase(co.co_filename)
|
filename = os.path.normcase(co.co_filename)
|
||||||
|
|
||||||
|
# TODO: is this pylint exception correct?
|
||||||
if filename == _srcfile:
|
if filename == _srcfile:
|
||||||
f = f.f_back
|
f = f.f_back
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef struct vec3 {
|
||||||
|
float v[3];
|
||||||
|
} vec3;
|
||||||
|
|
||||||
|
typedef struct vec4 {
|
||||||
|
float v[4];
|
||||||
|
} vec4;
|
||||||
|
|
||||||
|
typedef struct mat3 {
|
||||||
|
float v[3*3];
|
||||||
|
} mat3;
|
||||||
|
|
||||||
|
typedef struct mat4 {
|
||||||
|
float v[4*4];
|
||||||
|
} mat4;
|
||||||
|
|
||||||
|
static inline mat3 matmul3(const mat3 &a, const mat3 &b) {
|
||||||
|
mat3 ret = {{0.0}};
|
||||||
|
for (int r=0; r<3; r++) {
|
||||||
|
for (int c=0; c<3; c++) {
|
||||||
|
float v = 0.0;
|
||||||
|
for (int k=0; k<3; k++) {
|
||||||
|
v += a.v[r*3+k] * b.v[k*3+c];
|
||||||
|
}
|
||||||
|
ret.v[r*3+c] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline vec3 matvecmul3(const mat3 &a, const vec3 &b) {
|
||||||
|
vec3 ret = {{0.0}};
|
||||||
|
for (int r=0; r<3; r++) {
|
||||||
|
for (int c=0; c<3; c++) {
|
||||||
|
ret.v[r] += a.v[r*3+c] * b.v[c];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline mat4 matmul(const mat4 &a, const mat4 &b) {
|
||||||
|
mat4 ret = {{0.0}};
|
||||||
|
for (int r=0; r<4; r++) {
|
||||||
|
for (int c=0; c<4; c++) {
|
||||||
|
float v = 0.0;
|
||||||
|
for (int k=0; k<4; k++) {
|
||||||
|
v += a.v[r*4+k] * b.v[k*4+c];
|
||||||
|
}
|
||||||
|
ret.v[r*4+c] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline vec4 matvecmul(const mat4 &a, const vec4 &b) {
|
||||||
|
vec4 ret = {{0.0}};
|
||||||
|
for (int r=0; r<4; r++) {
|
||||||
|
for (int c=0; c<4; c++) {
|
||||||
|
ret.v[r] += a.v[r*4+c] * b.v[c];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// scales the input and output space of a transformation matrix
|
||||||
|
// that assumes pixel-center origin.
|
||||||
|
static inline mat3 transform_scale_buffer(const mat3 &in, float s) {
|
||||||
|
// in_pt = ( transform(out_pt/s + 0.5) - 0.5) * s
|
||||||
|
|
||||||
|
mat3 transform_out = {{
|
||||||
|
1.0f/s, 0.0f, 0.5f,
|
||||||
|
0.0f, 1.0f/s, 0.5f,
|
||||||
|
0.0f, 0.0f, 1.0f,
|
||||||
|
}};
|
||||||
|
|
||||||
|
mat3 transform_in = {{
|
||||||
|
s, 0.0f, -0.5f*s,
|
||||||
|
0.0f, s, -0.5f*s,
|
||||||
|
0.0f, 0.0f, 1.0f,
|
||||||
|
}};
|
||||||
|
|
||||||
|
return matmul3(transform_in, matmul3(in, transform_out));
|
||||||
|
}
|
||||||
+1
-1
@@ -1 +1 @@
|
|||||||
#define DEFAULT_MODEL "CD210 (Default)"
|
#define DEFAULT_MODEL "Filet o Fish (Default)"
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
import sys
|
|
||||||
import pytest
|
|
||||||
import inspect
|
|
||||||
|
|
||||||
|
|
||||||
class parameterized:
|
|
||||||
@staticmethod
|
|
||||||
def expand(cases):
|
|
||||||
cases = list(cases)
|
|
||||||
|
|
||||||
if not cases:
|
|
||||||
return lambda func: pytest.mark.skip("no parameterized cases")(func)
|
|
||||||
|
|
||||||
def decorator(func):
|
|
||||||
params = [p for p in inspect.signature(func).parameters if p != 'self']
|
|
||||||
normalized = [c if isinstance(c, tuple) else (c,) for c in cases]
|
|
||||||
# Infer arg count from first case so extra params (e.g. from @given) are left untouched
|
|
||||||
expand_params = params[: len(normalized[0])]
|
|
||||||
if len(expand_params) == 1:
|
|
||||||
return pytest.mark.parametrize(expand_params[0], [c[0] for c in normalized])(func)
|
|
||||||
return pytest.mark.parametrize(', '.join(expand_params), normalized)(func)
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
|
|
||||||
def parameterized_class(attrs, input_list=None):
|
|
||||||
if isinstance(attrs, list) and (not attrs or isinstance(attrs[0], dict)):
|
|
||||||
params_list = attrs
|
|
||||||
else:
|
|
||||||
assert input_list is not None
|
|
||||||
attr_names = (attrs,) if isinstance(attrs, str) else tuple(attrs)
|
|
||||||
params_list = [dict(zip(attr_names, v if isinstance(v, (tuple, list)) else (v,), strict=False)) for v in input_list]
|
|
||||||
|
|
||||||
def decorator(cls):
|
|
||||||
globs = sys._getframe(1).f_globals
|
|
||||||
for i, params in enumerate(params_list):
|
|
||||||
name = f"{cls.__name__}_{i}"
|
|
||||||
new_cls = type(name, (cls,), dict(params))
|
|
||||||
new_cls.__module__ = cls.__module__
|
|
||||||
new_cls.__test__ = True # override inherited False so pytest collects this subclass
|
|
||||||
globs[name] = new_cls
|
|
||||||
# Don't collect the un-parametrised base, but return it so outer decorators
|
|
||||||
# (e.g. @pytest.mark.skip) land on it and propagate to subclasses via MRO.
|
|
||||||
cls.__test__ = False
|
|
||||||
return cls
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
+7
-15
@@ -103,10 +103,10 @@ Params::~Params() {
|
|||||||
assert(queue.empty());
|
assert(queue.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> Params::allKeys(ParamKeyFlag flag) const {
|
std::vector<std::string> Params::allKeys(ParamKeyType type) const {
|
||||||
std::vector<std::string> ret;
|
std::vector<std::string> ret;
|
||||||
for (auto &p : keys) {
|
for (auto &p : keys) {
|
||||||
if (flag == ALL || (p.second.flags & flag)) {
|
if (type == ALL || (p.second & type)) {
|
||||||
ret.push_back(p.first);
|
ret.push_back(p.first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,16 +117,8 @@ bool Params::checkKey(const std::string &key) {
|
|||||||
return keys.find(key) != keys.end();
|
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) {
|
ParamKeyType Params::getKeyType(const std::string &key) {
|
||||||
return keys[key].type;
|
return static_cast<ParamKeyType>(keys[key]);
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> Params::getKeyDefaultValue(const std::string &key) {
|
|
||||||
return keys[key].default_value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Params::put(const char* key, const char* value, size_t value_size) {
|
int Params::put(const char* key, const char* value, size_t value_size) {
|
||||||
@@ -150,7 +142,7 @@ int Params::put(const char* key, const char* value, size_t value_size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fsync to force persist the changes.
|
// 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");
|
FileLock file_lock(params_path + "/.lock");
|
||||||
|
|
||||||
@@ -205,17 +197,17 @@ std::map<std::string, std::string> Params::readAll() {
|
|||||||
return util::read_files_in_dir(getParamPath());
|
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");
|
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.
|
// 2) delete files that are not defined in the keys.
|
||||||
if (DIR *d = opendir(getParamPath().c_str())) {
|
if (DIR *d = opendir(getParamPath().c_str())) {
|
||||||
struct dirent *de = NULL;
|
struct dirent *de = NULL;
|
||||||
while ((de = readdir(d))) {
|
while ((de = readdir(d))) {
|
||||||
if (de->d_type != DT_DIR) {
|
if (de->d_type != DT_DIR) {
|
||||||
auto it = keys.find(de->d_name);
|
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());
|
unlink(getParamPath(de->d_name).c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-24
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include <future>
|
#include <future>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@@ -10,34 +9,17 @@
|
|||||||
|
|
||||||
#include "common/queue.h"
|
#include "common/queue.h"
|
||||||
|
|
||||||
enum ParamKeyFlag {
|
enum ParamKeyType {
|
||||||
PERSISTENT = 0x02,
|
PERSISTENT = 0x02,
|
||||||
CLEAR_ON_MANAGER_START = 0x04,
|
CLEAR_ON_MANAGER_START = 0x04,
|
||||||
CLEAR_ON_ONROAD_TRANSITION = 0x08,
|
CLEAR_ON_ONROAD_TRANSITION = 0x08,
|
||||||
CLEAR_ON_OFFROAD_TRANSITION = 0x10,
|
CLEAR_ON_OFFROAD_TRANSITION = 0x10,
|
||||||
DONT_LOG = 0x20,
|
DONT_LOG = 0x20,
|
||||||
DEVELOPMENT_ONLY = 0x40,
|
DEVELOPMENT_ONLY = 0x40,
|
||||||
CLEAR_ON_IGNITION_ON = 0x80,
|
BACKUP = 0x80,
|
||||||
BACKUP = 0x100,
|
|
||||||
ALL = 0xFFFFFFFF
|
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 {
|
class Params {
|
||||||
public:
|
public:
|
||||||
explicit Params(const std::string &path = {});
|
explicit Params(const std::string &path = {});
|
||||||
@@ -46,18 +28,16 @@ public:
|
|||||||
Params(const Params&) = delete;
|
Params(const Params&) = delete;
|
||||||
Params& operator=(const Params&) = delete;
|
Params& operator=(const Params&) = delete;
|
||||||
|
|
||||||
std::vector<std::string> allKeys(ParamKeyFlag flag = ALL) const;
|
std::vector<std::string> allKeys(ParamKeyType type = ALL) const;
|
||||||
bool checkKey(const std::string &key);
|
bool checkKey(const std::string &key);
|
||||||
ParamKeyFlag getKeyFlag(const std::string &key);
|
|
||||||
ParamKeyType getKeyType(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 = {}) {
|
inline std::string getParamPath(const std::string &key = {}) {
|
||||||
return params_path + params_prefix + (key.empty() ? "" : "/" + key);
|
return params_path + params_prefix + (key.empty() ? "" : "/" + key);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete a value
|
// Delete a value
|
||||||
int remove(const std::string &key);
|
int remove(const std::string &key);
|
||||||
void clearAll(ParamKeyFlag flag);
|
void clearAll(ParamKeyType type);
|
||||||
|
|
||||||
// helpers for reading values
|
// helpers for reading values
|
||||||
std::string get(const std::string &key, bool block = false);
|
std::string get(const std::string &key, bool block = false);
|
||||||
|
|||||||
+1
-2
@@ -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 Params
|
||||||
assert ParamKeyFlag
|
|
||||||
assert ParamKeyType
|
assert ParamKeyType
|
||||||
assert UnknownKeyName
|
assert UnknownKeyName
|
||||||
|
|
||||||
|
|||||||
+149
-257
@@ -3,276 +3,168 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "cereal/gen/cpp/log.capnp.h"
|
inline static std::unordered_map<std::string, uint32_t> keys = {
|
||||||
|
{"AccessToken", CLEAR_ON_MANAGER_START | DONT_LOG},
|
||||||
inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
{"AdbEnabled", PERSISTENT},
|
||||||
{"AccessToken", {CLEAR_ON_MANAGER_START | DONT_LOG, STRING}},
|
{"AlwaysOnDM", PERSISTENT},
|
||||||
{"AdbEnabled", {PERSISTENT | BACKUP, BOOL}},
|
{"ApiCache_Device", PERSISTENT},
|
||||||
{"AlwaysOnDM", {PERSISTENT | BACKUP, BOOL}},
|
{"ApiCache_FirehoseStats", PERSISTENT},
|
||||||
{"ApiCache_Device", {PERSISTENT, STRING}},
|
{"AssistNowToken", PERSISTENT},
|
||||||
{"ApiCache_FirehoseStats", {PERSISTENT, JSON}},
|
{"AthenadPid", PERSISTENT},
|
||||||
{"AssistNowToken", {PERSISTENT, STRING}},
|
{"AthenadUploadQueue", PERSISTENT},
|
||||||
{"AthenadPid", {PERSISTENT, INT}},
|
{"AthenadRecentlyViewedRoutes", PERSISTENT},
|
||||||
{"AthenadUploadQueue", {PERSISTENT, JSON}},
|
{"BootCount", PERSISTENT},
|
||||||
{"AthenadRecentlyViewedRoutes", {PERSISTENT, STRING}},
|
{"CalibrationParams", PERSISTENT},
|
||||||
{"BootCount", {PERSISTENT, INT}},
|
{"CameraDebugExpGain", CLEAR_ON_MANAGER_START},
|
||||||
{"CalibrationParams", {PERSISTENT, BYTES}},
|
{"CameraDebugExpTime", CLEAR_ON_MANAGER_START},
|
||||||
{"CameraDebugExpGain", {CLEAR_ON_MANAGER_START, STRING}},
|
{"CarBatteryCapacity", PERSISTENT},
|
||||||
{"CameraDebugExpTime", {CLEAR_ON_MANAGER_START, STRING}},
|
{"CarParams", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||||
{"CarBatteryCapacity", {PERSISTENT, INT}},
|
{"CarParamsCache", CLEAR_ON_MANAGER_START},
|
||||||
{"CarParams", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BYTES}},
|
{"CarParamsPersistent", PERSISTENT},
|
||||||
{"CarParamsCache", {CLEAR_ON_MANAGER_START, BYTES}},
|
{"CarParamsPrevRoute", PERSISTENT},
|
||||||
{"CarParamsPersistent", {PERSISTENT, BYTES}},
|
{"CompletedTrainingVersion", PERSISTENT},
|
||||||
{"CarParamsPrevRoute", {PERSISTENT, BYTES}},
|
{"ControlsReady", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||||
{"CompletedTrainingVersion", {PERSISTENT, STRING, "0"}},
|
{"CurrentBootlog", PERSISTENT},
|
||||||
{"ControlsReady", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
|
{"CurrentRoute", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||||
{"CurrentBootlog", {PERSISTENT, STRING}},
|
{"DisableLogging", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||||
{"CurrentRoute", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, STRING}},
|
{"DisablePowerDown", PERSISTENT | BACKUP},
|
||||||
{"DisableLogging", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
|
{"DisableUpdates", PERSISTENT | BACKUP},
|
||||||
{"DisablePowerDown", {PERSISTENT | BACKUP, BOOL}},
|
{"DisengageOnAccelerator", PERSISTENT | BACKUP},
|
||||||
{"DisableUpdates", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"DongleId", PERSISTENT},
|
||||||
{"DisengageOnAccelerator", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"DoReboot", CLEAR_ON_MANAGER_START},
|
||||||
{"DongleId", {PERSISTENT, STRING}},
|
{"DoShutdown", CLEAR_ON_MANAGER_START},
|
||||||
{"DoReboot", {CLEAR_ON_MANAGER_START, BOOL}},
|
{"DoUninstall", CLEAR_ON_MANAGER_START},
|
||||||
{"DoShutdown", {CLEAR_ON_MANAGER_START, BOOL}},
|
{"ExperimentalLongitudinalEnabled", PERSISTENT | DEVELOPMENT_ONLY | BACKUP},
|
||||||
{"DoUninstall", {CLEAR_ON_MANAGER_START, BOOL}},
|
{"ExperimentalMode", PERSISTENT | BACKUP},
|
||||||
{"DriverTooDistracted", {CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON, BOOL}},
|
{"ExperimentalModeConfirmed", PERSISTENT | BACKUP},
|
||||||
{"AlphaLongitudinalEnabled", {PERSISTENT | DEVELOPMENT_ONLY | BACKUP, BOOL}},
|
{"FirmwareQueryDone", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||||
{"ExperimentalMode", {PERSISTENT | BACKUP, BOOL}},
|
{"ForcePowerDown", PERSISTENT},
|
||||||
{"ExperimentalModeConfirmed", {PERSISTENT | BACKUP, BOOL}},
|
{"GitBranch", PERSISTENT},
|
||||||
{"FirmwareQueryDone", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
|
{"GitCommit", PERSISTENT},
|
||||||
{"ForcePowerDown", {PERSISTENT, BOOL}},
|
{"GitCommitDate", PERSISTENT},
|
||||||
{"GitBranch", {PERSISTENT, STRING}},
|
{"GitDiff", PERSISTENT},
|
||||||
{"GitCommit", {PERSISTENT, STRING}},
|
{"GithubSshKeys", PERSISTENT | BACKUP},
|
||||||
{"GitCommitDate", {PERSISTENT, STRING}},
|
{"GithubUsername", PERSISTENT | BACKUP},
|
||||||
{"GitDiff", {PERSISTENT, STRING}},
|
{"GitRemote", PERSISTENT},
|
||||||
{"GithubSshKeys", {PERSISTENT | BACKUP, STRING}},
|
{"GsmApn", PERSISTENT | BACKUP},
|
||||||
{"GithubUsername", {PERSISTENT | BACKUP, STRING}},
|
{"GsmMetered", PERSISTENT | BACKUP},
|
||||||
{"GitRemote", {PERSISTENT, STRING}},
|
{"GsmRoaming", PERSISTENT | BACKUP},
|
||||||
{"GsmApn", {PERSISTENT | BACKUP, STRING}},
|
{"HardwareSerial", PERSISTENT},
|
||||||
{"GsmMetered", {PERSISTENT | BACKUP, BOOL, "1"}},
|
{"HasAcceptedTerms", PERSISTENT},
|
||||||
{"GsmRoaming", {PERSISTENT | BACKUP, BOOL}},
|
{"InstallDate", PERSISTENT},
|
||||||
{"HardwareSerial", {PERSISTENT, STRING}},
|
{"IsDriverViewEnabled", CLEAR_ON_MANAGER_START},
|
||||||
{"HasAcceptedTerms", {PERSISTENT, STRING, "0"}},
|
{"IsEngaged", PERSISTENT},
|
||||||
{"InstallDate", {PERSISTENT, TIME}},
|
{"IsLdwEnabled", PERSISTENT | BACKUP},
|
||||||
{"IsDriverViewEnabled", {CLEAR_ON_MANAGER_START, BOOL}},
|
{"IsMetric", PERSISTENT | BACKUP},
|
||||||
{"IsEngaged", {PERSISTENT, BOOL}},
|
{"IsOffroad", CLEAR_ON_MANAGER_START},
|
||||||
{"IsLdwEnabled", {PERSISTENT | BACKUP, BOOL}},
|
{"IsOnroad", PERSISTENT},
|
||||||
{"IsMetric", {PERSISTENT | BACKUP, BOOL}},
|
{"IsRhdDetected", PERSISTENT},
|
||||||
{"IsOffroad", {CLEAR_ON_MANAGER_START, BOOL}},
|
{"IsReleaseBranch", CLEAR_ON_MANAGER_START},
|
||||||
{"IsOnroad", {PERSISTENT, BOOL}},
|
{"IsTakingSnapshot", CLEAR_ON_MANAGER_START},
|
||||||
{"IsRhdDetected", {PERSISTENT, BOOL}},
|
{"IsTestedBranch", CLEAR_ON_MANAGER_START},
|
||||||
{"IsReleaseBranch", {CLEAR_ON_MANAGER_START, BOOL}},
|
{"JoystickDebugMode", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
||||||
{"IsTakingSnapshot", {CLEAR_ON_MANAGER_START, BOOL}},
|
{"LanguageSetting", PERSISTENT | BACKUP},
|
||||||
{"IsTestedBranch", {CLEAR_ON_MANAGER_START, BOOL}},
|
{"LastAthenaPingTime", CLEAR_ON_MANAGER_START},
|
||||||
{"JoystickDebugMode", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
{"LastGPSPosition", PERSISTENT},
|
||||||
{"LanguageSetting", {PERSISTENT | BACKUP, STRING, "en"}},
|
{"LastManagerExitReason", CLEAR_ON_MANAGER_START},
|
||||||
{"LastAthenaPingTime", {CLEAR_ON_MANAGER_START, INT}},
|
{"LastOffroadStatusPacket", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
||||||
{"LastGPSPosition", {PERSISTENT, STRING}},
|
{"LastPowerDropDetected", CLEAR_ON_MANAGER_START},
|
||||||
{"LastManagerExitReason", {CLEAR_ON_MANAGER_START, STRING}},
|
{"LastUpdateException", CLEAR_ON_MANAGER_START},
|
||||||
{"LastOffroadStatusPacket", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, JSON}},
|
{"LastUpdateTime", PERSISTENT},
|
||||||
{"LastAgnosPowerMonitorShutdown", {CLEAR_ON_MANAGER_START, STRING}},
|
{"LiveParameters", PERSISTENT},
|
||||||
{"LastPowerDropDetected", {CLEAR_ON_MANAGER_START, STRING}},
|
{"LiveTorqueParameters", PERSISTENT | DONT_LOG},
|
||||||
{"LastUpdateException", {CLEAR_ON_MANAGER_START, STRING}},
|
{"LocationFilterInitialState", PERSISTENT},
|
||||||
{"LastUpdateRouteCount", {PERSISTENT, INT, "0"}},
|
{"LongitudinalManeuverMode", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
||||||
{"LastUpdateTime", {PERSISTENT, TIME}},
|
{"LongitudinalPersonality", PERSISTENT | BACKUP},
|
||||||
{"LastUpdateUptimeOnroad", {PERSISTENT, FLOAT, "0.0"}},
|
{"NetworkMetered", PERSISTENT},
|
||||||
{"LiveDelay", {PERSISTENT | BACKUP, BYTES}},
|
{"ObdMultiplexingChanged", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||||
{"LiveParameters", {PERSISTENT, JSON}},
|
{"ObdMultiplexingEnabled", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||||
{"LiveParametersV2", {PERSISTENT, BYTES}},
|
{"Offroad_BadNvme", CLEAR_ON_MANAGER_START},
|
||||||
{"LiveTorqueParameters", {PERSISTENT | DONT_LOG, BYTES}},
|
{"Offroad_CarUnrecognized", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||||
{"LocationFilterInitialState", {PERSISTENT, BYTES}},
|
{"Offroad_ConnectivityNeeded", CLEAR_ON_MANAGER_START},
|
||||||
{"LongitudinalManeuverMode", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
{"Offroad_ConnectivityNeededPrompt", CLEAR_ON_MANAGER_START},
|
||||||
{"LongitudinalPersonality", {PERSISTENT | BACKUP, INT, std::to_string(static_cast<int>(cereal::LongitudinalPersonality::STANDARD))}},
|
{"Offroad_IsTakingSnapshot", CLEAR_ON_MANAGER_START},
|
||||||
{"NetworkMetered", {PERSISTENT | BACKUP, BOOL}},
|
{"Offroad_NeosUpdate", CLEAR_ON_MANAGER_START},
|
||||||
{"ObdMultiplexingChanged", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
|
{"Offroad_NoFirmware", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||||
{"ObdMultiplexingEnabled", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
|
{"Offroad_Recalibration", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||||
{"Offroad_CarUnrecognized", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}},
|
{"Offroad_StorageMissing", CLEAR_ON_MANAGER_START},
|
||||||
{"Offroad_ConnectivityNeeded", {CLEAR_ON_MANAGER_START, JSON}},
|
{"Offroad_TemperatureTooHigh", CLEAR_ON_MANAGER_START},
|
||||||
{"Offroad_ConnectivityNeededPrompt", {CLEAR_ON_MANAGER_START, JSON}},
|
{"Offroad_UnofficialHardware", CLEAR_ON_MANAGER_START},
|
||||||
{"Offroad_ExcessiveActuation", {PERSISTENT, JSON}},
|
{"Offroad_UpdateFailed", CLEAR_ON_MANAGER_START},
|
||||||
{"Offroad_IsTakingSnapshot", {CLEAR_ON_MANAGER_START, JSON}},
|
{"OpenpilotEnabledToggle", PERSISTENT | BACKUP},
|
||||||
{"Offroad_NeosUpdate", {CLEAR_ON_MANAGER_START, JSON}},
|
{"PandaHeartbeatLost", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
||||||
{"Offroad_NoFirmware", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}},
|
{"PandaSomResetTriggered", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
||||||
{"Offroad_Recalibration", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}},
|
{"PandaSignatures", CLEAR_ON_MANAGER_START},
|
||||||
{"Offroad_TemperatureTooHigh", {CLEAR_ON_MANAGER_START, JSON}},
|
{"PrimeType", PERSISTENT},
|
||||||
{"Offroad_UnregisteredHardware", {CLEAR_ON_MANAGER_START, JSON}},
|
{"RecordFront", PERSISTENT | BACKUP},
|
||||||
{"Offroad_UpdateFailed", {CLEAR_ON_MANAGER_START, JSON}},
|
{"RecordFrontLock", PERSISTENT}, // for the internal fleet
|
||||||
{"Offroad_DriverMonitoringUncertain", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}},
|
{"SecOCKey", PERSISTENT | DONT_LOG}, // Candidate for | BACKUP
|
||||||
{"OnroadCycleRequested", {CLEAR_ON_MANAGER_START, BOOL}},
|
{"RouteCount", PERSISTENT},
|
||||||
{"OpenpilotEnabledToggle", {PERSISTENT | BACKUP, BOOL, "1"}},
|
{"SnoozeUpdate", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
||||||
{"PandaHeartbeatLost", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
{"SshEnabled", PERSISTENT | BACKUP},
|
||||||
{"PandaSomResetTriggered", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
{"TermsVersion", PERSISTENT},
|
||||||
{"PandaSignatures", {CLEAR_ON_MANAGER_START, BYTES}},
|
{"TrainingVersion", PERSISTENT},
|
||||||
{"PrimeType", {PERSISTENT, INT}},
|
{"UbloxAvailable", PERSISTENT},
|
||||||
{"RecordAudio", {PERSISTENT | BACKUP, BOOL}},
|
{"UpdateAvailable", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||||
{"RecordAudioFeedback", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"UpdateFailedCount", CLEAR_ON_MANAGER_START},
|
||||||
{"RecordFront", {PERSISTENT | BACKUP, BOOL}},
|
{"UpdaterAvailableBranches", PERSISTENT},
|
||||||
{"RecordFrontLock", {PERSISTENT, BOOL}}, // for the internal fleet
|
{"UpdaterCurrentDescription", CLEAR_ON_MANAGER_START},
|
||||||
{"SecOCKey", {PERSISTENT | DONT_LOG | BACKUP, STRING}},
|
{"UpdaterCurrentReleaseNotes", CLEAR_ON_MANAGER_START},
|
||||||
{"ShowDebugInfo", {PERSISTENT, BOOL}},
|
{"UpdaterFetchAvailable", CLEAR_ON_MANAGER_START},
|
||||||
{"RouteCount", {PERSISTENT, INT, "0"}},
|
{"UpdaterNewDescription", CLEAR_ON_MANAGER_START},
|
||||||
{"SnoozeUpdate", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
{"UpdaterNewReleaseNotes", CLEAR_ON_MANAGER_START},
|
||||||
{"SshEnabled", {PERSISTENT | BACKUP, BOOL}},
|
{"UpdaterState", CLEAR_ON_MANAGER_START},
|
||||||
{"TermsVersion", {PERSISTENT, STRING}},
|
{"UpdaterTargetBranch", CLEAR_ON_MANAGER_START},
|
||||||
{"TorqueBar", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"UpdaterLastFetchTime", PERSISTENT},
|
||||||
{"TrainingVersion", {PERSISTENT, STRING}},
|
{"Version", PERSISTENT},
|
||||||
{"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 --- //
|
// --- sunnypilot params --- //
|
||||||
{"ApiCache_DriveStats", {PERSISTENT, JSON}},
|
{"ApiCache_DriveStats", PERSISTENT},
|
||||||
{"AutoLaneChangeBsmDelay", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"AutoLaneChangeBsmDelay", PERSISTENT},
|
||||||
{"AutoLaneChangeTimer", {PERSISTENT | BACKUP, INT, "0"}},
|
{"AutoLaneChangeTimer", PERSISTENT},
|
||||||
{"BlinkerLateralReengageDelay", {PERSISTENT | BACKUP, INT, "0"}}, // seconds
|
{"CarParamsSP", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||||
{"BlinkerMinLateralControlSpeed", {PERSISTENT | BACKUP, INT, "20"}}, // MPH or km/h
|
{"CarParamsSPCache", CLEAR_ON_MANAGER_START},
|
||||||
{"BlinkerPauseLateralControl", {PERSISTENT | BACKUP, INT, "0"}},
|
{"CarParamsSPPersistent", PERSISTENT},
|
||||||
{"Brightness", {PERSISTENT | BACKUP, INT, "0"}},
|
{"CarPlatformBundle", PERSISTENT},
|
||||||
{"CarList", {PERSISTENT, JSON}},
|
{"EnableGithubRunner", PERSISTENT | BACKUP},
|
||||||
{"CarParamsSP", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BYTES}},
|
{"ModelRunnerTypeCache", CLEAR_ON_ONROAD_TRANSITION},
|
||||||
{"CarParamsSPCache", {CLEAR_ON_MANAGER_START, BYTES}},
|
{"OffroadMode", CLEAR_ON_MANAGER_START},
|
||||||
{"CarParamsSPPersistent", {PERSISTENT, BYTES}},
|
{"OffroadMode_Status", CLEAR_ON_MANAGER_START},
|
||||||
{"CarPlatformBundle", {PERSISTENT | BACKUP, JSON}},
|
{"QuietMode", PERSISTENT | BACKUP},
|
||||||
{"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"}},
|
|
||||||
{"OnroadScreenOffBrightnessMigrated", {PERSISTENT | BACKUP, STRING, "0.0"}},
|
|
||||||
{"OnroadScreenOffTimer", {PERSISTENT | BACKUP, INT, "15"}},
|
|
||||||
{"OnroadScreenOffTimerMigrated", {PERSISTENT | BACKUP, STRING, "0.0"}},
|
|
||||||
{"OnroadUploads", {PERSISTENT | BACKUP, BOOL, "1"}},
|
|
||||||
{"QuickBootToggle", {PERSISTENT | BACKUP, BOOL, "0"}},
|
|
||||||
{"QuietMode", {PERSISTENT | BACKUP, BOOL, "0"}},
|
|
||||||
{"RainbowMode", {PERSISTENT | BACKUP, BOOL, "0"}},
|
|
||||||
{"RocketFuel", {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 params
|
||||||
{"Mads", {PERSISTENT | BACKUP, BOOL, "1"}},
|
{"Mads", PERSISTENT | BACKUP},
|
||||||
{"MadsMainCruiseAllowed", {PERSISTENT | BACKUP, BOOL, "1"}},
|
{"MadsMainCruiseAllowed", PERSISTENT | BACKUP},
|
||||||
{"MadsSteeringMode", {PERSISTENT | BACKUP, INT, "0"}},
|
{"MadsPauseLateralOnBrake", PERSISTENT | BACKUP},
|
||||||
{"MadsUnifiedEngagementMode", {PERSISTENT | BACKUP, BOOL, "1"}},
|
{"MadsUnifiedEngagementMode", PERSISTENT | BACKUP},
|
||||||
|
|
||||||
// Model Manager params
|
// Model Manager params
|
||||||
{"ModelManager_ActiveBundle", {PERSISTENT, JSON}},
|
{"ModelManager_ActiveBundle", PERSISTENT},
|
||||||
{"ModelManager_ClearCache", {CLEAR_ON_MANAGER_START, BOOL}},
|
{"ModelManager_DownloadIndex", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||||
{"ModelManager_DownloadIndex", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, INT}},
|
{"ModelManager_LastSyncTime", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
||||||
{"ModelManager_Favs", {PERSISTENT | BACKUP, STRING}},
|
{"ModelManager_ModelsCache", PERSISTENT | BACKUP},
|
||||||
{"ModelManager_LastSyncTime", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, INT, "0"}},
|
|
||||||
{"ModelManager_ModelsCache", {PERSISTENT | BACKUP, JSON}},
|
|
||||||
|
|
||||||
// Neural Network Lateral Control
|
// Neural Network Lateral Control
|
||||||
{"NeuralNetworkLateralControl", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"NeuralNetworkLateralControl", PERSISTENT | BACKUP},
|
||||||
|
|
||||||
// sunnylink params
|
// sunnylink params
|
||||||
{"EnableSunnylinkUploader", {PERSISTENT | BACKUP, BOOL}},
|
{"EnableSunnylinkUploader", PERSISTENT | BACKUP},
|
||||||
{"LastSunnylinkPingTime", {CLEAR_ON_MANAGER_START, INT}},
|
{"LastSunnylinkPingTime", CLEAR_ON_MANAGER_START},
|
||||||
{"SunnylinkCache_Roles", {PERSISTENT, STRING}},
|
{"SunnylinkCache_Roles", PERSISTENT},
|
||||||
{"SunnylinkCache_Users", {PERSISTENT, STRING}},
|
{"SunnylinkCache_Users", PERSISTENT},
|
||||||
{"SunnylinkDongleId", {PERSISTENT, STRING}},
|
{"SunnylinkDongleId", PERSISTENT},
|
||||||
{"SunnylinkdPid", {PERSISTENT, INT}},
|
{"SunnylinkdPid", PERSISTENT},
|
||||||
{"SunnylinkEnabled", {PERSISTENT, BOOL, "1"}},
|
{"SunnylinkEnabled", PERSISTENT},
|
||||||
{"SunnylinkTempFault", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL, "0"}},
|
|
||||||
|
|
||||||
// Backup Manager params
|
// Backup Manager params
|
||||||
{"BackupManager_CreateBackup", {PERSISTENT, BOOL}},
|
{"BackupManager_CreateBackup", PERSISTENT},
|
||||||
{"BackupManager_RestoreVersion", {PERSISTENT, STRING}},
|
{"BackupManager_RestoreVersion", PERSISTENT},
|
||||||
|
|
||||||
// sunnypilot car specific params
|
// sunnypilot car specific params
|
||||||
{"HyundaiLongitudinalTuning", {PERSISTENT | BACKUP, INT, "0"}},
|
{"HyundaiRadarTracks", PERSISTENT},
|
||||||
{"SubaruStopAndGo", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"HyundaiRadarTracksConfirmed", PERSISTENT},
|
||||||
{"SubaruStopAndGoManualParkingBrake", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"HyundaiRadarTracksPersistent", PERSISTENT},
|
||||||
{"TeslaCoopSteering", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"HyundaiRadarTracksToggle", PERSISTENT},
|
||||||
{"ToyotaEnforceStockLongitudinal", {PERSISTENT | BACKUP, BOOL, "0"}},
|
|
||||||
{"ToyotaStopAndGoHack", {PERSISTENT | BACKUP, BOOL, "0"}},
|
|
||||||
|
|
||||||
{"DynamicExperimentalControl", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"DynamicExperimentalControl", PERSISTENT},
|
||||||
{"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 | BACKUP, BOOL, "0"}},
|
|
||||||
|
|
||||||
// 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}},
|
|
||||||
{"TorqueControlTune", {PERSISTENT | BACKUP, FLOAT}},
|
|
||||||
{"TorqueParamsOverrideEnabled", {PERSISTENT | BACKUP, BOOL, "0"}},
|
|
||||||
{"TorqueParamsOverrideFriction", {PERSISTENT | BACKUP, FLOAT, "0.1"}},
|
|
||||||
{"TorqueParamsOverrideLatAccelFactor", {PERSISTENT | BACKUP, FLOAT, "2.5"}},
|
|
||||||
};
|
};
|
||||||
|
|||||||
+13
-86
@@ -1,35 +1,19 @@
|
|||||||
# distutils: language = c++
|
# distutils: language = c++
|
||||||
# cython: language_level = 3
|
# cython: language_level = 3
|
||||||
import builtins
|
|
||||||
import datetime
|
|
||||||
import json
|
|
||||||
from libcpp cimport bool
|
from libcpp cimport bool
|
||||||
from libcpp.string cimport string
|
from libcpp.string cimport string
|
||||||
from libcpp.vector cimport vector
|
from libcpp.vector cimport vector
|
||||||
from libcpp.optional cimport optional
|
|
||||||
|
|
||||||
from openpilot.common.swaglog import cloudlog
|
|
||||||
|
|
||||||
cdef extern from "common/params.h":
|
cdef extern from "common/params.h":
|
||||||
cpdef enum ParamKeyFlag:
|
cpdef enum ParamKeyType:
|
||||||
PERSISTENT
|
PERSISTENT
|
||||||
CLEAR_ON_MANAGER_START
|
CLEAR_ON_MANAGER_START
|
||||||
CLEAR_ON_ONROAD_TRANSITION
|
CLEAR_ON_ONROAD_TRANSITION
|
||||||
CLEAR_ON_OFFROAD_TRANSITION
|
CLEAR_ON_OFFROAD_TRANSITION
|
||||||
DEVELOPMENT_ONLY
|
DEVELOPMENT_ONLY
|
||||||
CLEAR_ON_IGNITION_ON
|
|
||||||
BACKUP
|
BACKUP
|
||||||
ALL
|
ALL
|
||||||
|
|
||||||
cpdef enum ParamKeyType:
|
|
||||||
STRING
|
|
||||||
BOOL
|
|
||||||
INT
|
|
||||||
FLOAT
|
|
||||||
TIME
|
|
||||||
JSON
|
|
||||||
BYTES
|
|
||||||
|
|
||||||
cdef cppclass c_Params "Params":
|
cdef cppclass c_Params "Params":
|
||||||
c_Params(string) except + nogil
|
c_Params(string) except + nogil
|
||||||
string get(string, bool) nogil
|
string get(string, bool) nogil
|
||||||
@@ -40,31 +24,10 @@ cdef extern from "common/params.h":
|
|||||||
void putBoolNonBlocking(string, bool) nogil
|
void putBoolNonBlocking(string, bool) nogil
|
||||||
int putBool(string, bool) nogil
|
int putBool(string, bool) nogil
|
||||||
bool checkKey(string) nogil
|
bool checkKey(string) nogil
|
||||||
ParamKeyType getKeyType(string) nogil
|
|
||||||
optional[string] getKeyDefaultValue(string) nogil
|
|
||||||
string getParamPath(string) nogil
|
string getParamPath(string) nogil
|
||||||
void clearAll(ParamKeyFlag)
|
void clearAll(ParamKeyType)
|
||||||
vector[string] allKeys(ParamKeyFlag)
|
vector[string] allKeys(ParamKeyType)
|
||||||
|
|
||||||
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):
|
def ensure_bytes(v):
|
||||||
return v.encode() if isinstance(v, str) else v
|
return v.encode() if isinstance(v, str) else v
|
||||||
@@ -88,8 +51,8 @@ cdef class Params:
|
|||||||
def __dealloc__(self):
|
def __dealloc__(self):
|
||||||
del self.p
|
del self.p
|
||||||
|
|
||||||
def clear_all(self, tx_flag=ParamKeyFlag.ALL):
|
def clear_all(self, tx_type=ParamKeyType.ALL):
|
||||||
self.p.clearAll(tx_flag)
|
self.p.clearAll(tx_type)
|
||||||
|
|
||||||
def check_key(self, key):
|
def check_key(self, key):
|
||||||
key = ensure_bytes(key)
|
key = ensure_bytes(key)
|
||||||
@@ -97,38 +60,21 @@ cdef class Params:
|
|||||||
raise UnknownKeyName(key)
|
raise UnknownKeyName(key)
|
||||||
return key
|
return key
|
||||||
|
|
||||||
def python2cpp(self, proposed_type, expected_type, value, key):
|
def get(self, key, bool block=False, encoding=None):
|
||||||
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):
|
|
||||||
cdef string k = self.check_key(key)
|
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
|
cdef string val
|
||||||
with nogil:
|
with nogil:
|
||||||
val = self.p.get(k, block)
|
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 val == b"":
|
||||||
if block:
|
if block:
|
||||||
# If we got no value while running in blocked mode
|
# If we got no value while running in blocked mode
|
||||||
# it means we got an interrupt while waiting
|
# it means we got an interrupt while waiting
|
||||||
raise KeyboardInterrupt
|
raise KeyboardInterrupt
|
||||||
else:
|
else:
|
||||||
return self._cpp2python(t, default_val, None, key)
|
return None
|
||||||
return self._cpp2python(t, val, default_val, key)
|
|
||||||
|
return val if encoding is None else val.decode(encoding)
|
||||||
|
|
||||||
def get_bool(self, key, bool block=False):
|
def get_bool(self, key, bool block=False):
|
||||||
cdef string k = self.check_key(key)
|
cdef string k = self.check_key(key)
|
||||||
@@ -137,11 +83,6 @@ cdef class Params:
|
|||||||
r = self.p.getBool(k, block)
|
r = self.p.getBool(k, block)
|
||||||
return r
|
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):
|
def put(self, key, dat):
|
||||||
"""
|
"""
|
||||||
Warning: This function blocks until the param is written to disk!
|
Warning: This function blocks until the param is written to disk!
|
||||||
@@ -150,7 +91,7 @@ cdef class Params:
|
|||||||
in general try to avoid writing params as much as possible.
|
in general try to avoid writing params as much as possible.
|
||||||
"""
|
"""
|
||||||
cdef string k = self.check_key(key)
|
cdef string k = self.check_key(key)
|
||||||
cdef string dat_bytes = self._put_cast(key, dat)
|
cdef string dat_bytes = ensure_bytes(dat)
|
||||||
with nogil:
|
with nogil:
|
||||||
self.p.put(k, dat_bytes)
|
self.p.put(k, dat_bytes)
|
||||||
|
|
||||||
@@ -161,7 +102,7 @@ cdef class Params:
|
|||||||
|
|
||||||
def put_nonblocking(self, key, dat):
|
def put_nonblocking(self, key, dat):
|
||||||
cdef string k = self.check_key(key)
|
cdef string k = self.check_key(key)
|
||||||
cdef string dat_bytes = self._put_cast(key, dat)
|
cdef string dat_bytes = ensure_bytes(dat)
|
||||||
with nogil:
|
with nogil:
|
||||||
self.p.putNonBlocking(k, dat_bytes)
|
self.p.putNonBlocking(k, dat_bytes)
|
||||||
|
|
||||||
@@ -179,19 +120,5 @@ cdef class Params:
|
|||||||
cdef string key_bytes = ensure_bytes(key)
|
cdef string key_bytes = ensure_bytes(key)
|
||||||
return self.p.getParamPath(key_bytes).decode("utf-8")
|
return self.p.getParamPath(key_bytes).decode("utf-8")
|
||||||
|
|
||||||
def get_type(self, key):
|
def all_keys(self, type=ParamKeyType.ALL):
|
||||||
return self.p.getKeyType(self.check_key(key))
|
return self.p.allKeys(type)
|
||||||
|
|
||||||
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)
|
|
||||||
|
|||||||
+34
-21
@@ -2,14 +2,23 @@ import numpy as np
|
|||||||
from numbers import Number
|
from numbers import Number
|
||||||
|
|
||||||
class PIDController:
|
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: list[list[float]] = [[0], [k_p]] if isinstance(k_p, Number) else k_p
|
self._k_p = k_p
|
||||||
self._k_i: list[list[float]] = [[0], [k_i]] if isinstance(k_i, Number) else k_i
|
self._k_i = k_i
|
||||||
self._k_d: list[list[float]] = [[0], [k_d]] if isinstance(k_d, Number) else k_d
|
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):
|
||||||
|
self._k_i = [[0], [self._k_i]]
|
||||||
|
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.speed = 0.0
|
||||||
|
|
||||||
self.reset()
|
self.reset()
|
||||||
@@ -26,6 +35,10 @@ class PIDController:
|
|||||||
def k_d(self):
|
def k_d(self):
|
||||||
return np.interp(self.speed, self._k_d[0], self._k_d[1])
|
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):
|
def reset(self):
|
||||||
self.p = 0.0
|
self.p = 0.0
|
||||||
self.i = 0.0
|
self.i = 0.0
|
||||||
@@ -33,25 +46,25 @@ class PIDController:
|
|||||||
self.f = 0.0
|
self.f = 0.0
|
||||||
self.control = 0
|
self.control = 0
|
||||||
|
|
||||||
def set_limits(self, pos_limit, neg_limit):
|
def update(self, error, error_rate=0.0, speed=0.0, override=False, feedforward=0., freeze_integrator=False):
|
||||||
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):
|
|
||||||
self.speed = speed
|
self.speed = speed
|
||||||
self.p = self.k_p * float(error)
|
|
||||||
self.d = self.k_d * error_rate
|
|
||||||
self.f = feedforward
|
|
||||||
|
|
||||||
if not freeze_integrator:
|
self.p = float(error) * self.k_p
|
||||||
i = self.i + self.k_i * self.i_dt * error
|
self.f = feedforward * self.k_f
|
||||||
|
self.d = error_rate * self.k_d
|
||||||
|
|
||||||
# Don't allow windup if already clipping
|
if override:
|
||||||
test_control = self.p + i + self.d + self.f
|
self.i -= self.i_unwind_rate * float(np.sign(self.i))
|
||||||
i_upperbound = self.i if test_control > self.pos_limit else self.pos_limit
|
else:
|
||||||
i_lowerbound = self.i if test_control < self.neg_limit else self.neg_limit
|
if not freeze_integrator:
|
||||||
self.i = np.clip(i, i_lowerbound, i_upperbound)
|
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
|
control = self.p + self.i + self.d + self.f
|
||||||
|
|
||||||
self.control = np.clip(control, self.neg_limit, self.pos_limit)
|
self.control = np.clip(control, self.neg_limit, self.pos_limit)
|
||||||
return self.control
|
return self.control
|
||||||
|
|||||||
+5
-9
@@ -13,11 +13,7 @@ public:
|
|||||||
if (prefix.empty()) {
|
if (prefix.empty()) {
|
||||||
prefix = util::random_string(15);
|
prefix = util::random_string(15);
|
||||||
}
|
}
|
||||||
#ifdef __APPLE__
|
msgq_path = Path::shm_path() + "/" + prefix;
|
||||||
msgq_path = "/tmp/msgq_" + prefix;
|
|
||||||
#else
|
|
||||||
msgq_path = "/dev/shm/msgq_" + prefix;
|
|
||||||
#endif
|
|
||||||
bool ret = util::create_directories(msgq_path, 0777);
|
bool ret = util::create_directories(msgq_path, 0777);
|
||||||
assert(ret);
|
assert(ret);
|
||||||
setenv("OPENPILOT_PREFIX", prefix.c_str(), 1);
|
setenv("OPENPILOT_PREFIX", prefix.c_str(), 1);
|
||||||
@@ -27,14 +23,14 @@ public:
|
|||||||
auto param_path = Params().getParamPath();
|
auto param_path = Params().getParamPath();
|
||||||
if (util::file_exists(param_path)) {
|
if (util::file_exists(param_path)) {
|
||||||
std::string real_path = util::readlink(param_path);
|
std::string real_path = util::readlink(param_path);
|
||||||
util::check_system(util::string_format("rm %s -rf", real_path.c_str()));
|
system(util::string_format("rm %s -rf", real_path.c_str()).c_str());
|
||||||
unlink(param_path.c_str());
|
unlink(param_path.c_str());
|
||||||
}
|
}
|
||||||
if (getenv("COMMA_CACHE") == nullptr) {
|
if (getenv("COMMA_CACHE") == nullptr) {
|
||||||
util::check_system(util::string_format("rm %s -rf", Path::download_cache_root().c_str()));
|
system(util::string_format("rm %s -rf", Path::download_cache_root().c_str()).c_str());
|
||||||
}
|
}
|
||||||
util::check_system(util::string_format("rm %s -rf", Path::comma_home().c_str()));
|
system(util::string_format("rm %s -rf", Path::comma_home().c_str()).c_str());
|
||||||
util::check_system(util::string_format("rm %s -rf", msgq_path.c_str()));
|
system(util::string_format("rm %s -rf", msgq_path.c_str()).c_str());
|
||||||
unsetenv("OPENPILOT_PREFIX");
|
unsetenv("OPENPILOT_PREFIX");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-15
@@ -1,5 +1,4 @@
|
|||||||
import os
|
import os
|
||||||
import platform
|
|
||||||
import shutil
|
import shutil
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
@@ -10,20 +9,20 @@ from openpilot.system.hardware.hw import Paths
|
|||||||
from openpilot.system.hardware.hw import DEFAULT_DOWNLOAD_CACHE_ROOT
|
from openpilot.system.hardware.hw import DEFAULT_DOWNLOAD_CACHE_ROOT
|
||||||
|
|
||||||
class OpenpilotPrefix:
|
class OpenpilotPrefix:
|
||||||
def __init__(self, prefix: str | None = None, create_dirs_on_enter: bool = True, clean_dirs_on_exit: bool = True, shared_download_cache: bool = False):
|
def __init__(self, prefix: str = None, clean_dirs_on_exit: bool = True, shared_download_cache: bool = False):
|
||||||
self.prefix = prefix if prefix else str(uuid.uuid4().hex[0:15])
|
self.prefix = prefix if prefix else str(uuid.uuid4().hex[0:15])
|
||||||
shm_path = "/tmp" if platform.system() == "Darwin" else "/dev/shm"
|
self.msgq_path = os.path.join(Paths.shm_path(), self.prefix)
|
||||||
self.msgq_path = os.path.join(shm_path, "msgq_" + self.prefix)
|
|
||||||
self.create_dirs_on_enter = create_dirs_on_enter
|
|
||||||
self.clean_dirs_on_exit = clean_dirs_on_exit
|
self.clean_dirs_on_exit = clean_dirs_on_exit
|
||||||
self.shared_download_cache = shared_download_cache
|
self.shared_download_cache = shared_download_cache
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self.original_prefix = os.environ.get('OPENPILOT_PREFIX', None)
|
self.original_prefix = os.environ.get('OPENPILOT_PREFIX', None)
|
||||||
os.environ['OPENPILOT_PREFIX'] = self.prefix
|
os.environ['OPENPILOT_PREFIX'] = self.prefix
|
||||||
|
try:
|
||||||
if self.create_dirs_on_enter:
|
os.mkdir(self.msgq_path)
|
||||||
self.create_dirs()
|
except FileExistsError:
|
||||||
|
pass
|
||||||
|
os.makedirs(Paths.log_root(), exist_ok=True)
|
||||||
|
|
||||||
if self.shared_download_cache:
|
if self.shared_download_cache:
|
||||||
os.environ["COMMA_CACHE"] = DEFAULT_DOWNLOAD_CACHE_ROOT
|
os.environ["COMMA_CACHE"] = DEFAULT_DOWNLOAD_CACHE_ROOT
|
||||||
@@ -41,13 +40,6 @@ class OpenpilotPrefix:
|
|||||||
pass
|
pass
|
||||||
return False
|
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):
|
def clean_dirs(self):
|
||||||
symlink_path = Params().get_param_path()
|
symlink_path = Params().get_param_path()
|
||||||
if os.path.exists(symlink_path):
|
if os.path.exists(symlink_path):
|
||||||
|
|||||||
@@ -6,9 +6,9 @@
|
|||||||
#include "common/timing.h"
|
#include "common/timing.h"
|
||||||
#include "common/util.h"
|
#include "common/util.h"
|
||||||
|
|
||||||
RateKeeper::RateKeeper(const std::string &name_, float rate, float print_delay_threshold_)
|
RateKeeper::RateKeeper(const std::string &name, float rate, float print_delay_threshold)
|
||||||
: name(name_),
|
: name(name),
|
||||||
print_delay_threshold(std::max(0.f, print_delay_threshold_)) {
|
print_delay_threshold(std::max(0.f, print_delay_threshold)) {
|
||||||
interval = 1 / rate;
|
interval = 1 / rate;
|
||||||
last_monitor_time = seconds_since_boot();
|
last_monitor_time = seconds_since_boot();
|
||||||
next_frame_time = last_monitor_time + interval;
|
next_frame_time = last_monitor_time + interval;
|
||||||
|
|||||||
+3
-4
@@ -1,12 +1,11 @@
|
|||||||
"""Utilities for reading real time clocks and keeping soft real time constraints."""
|
"""Utilities for reading real time clocks and keeping soft real time constraints."""
|
||||||
import gc
|
import gc
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from setproctitle import getproctitle
|
from setproctitle import getproctitle
|
||||||
|
|
||||||
from openpilot.common.utils import MovingAverage
|
from openpilot.common.util import MovingAverage
|
||||||
from openpilot.system.hardware import PC
|
from openpilot.system.hardware import PC
|
||||||
|
|
||||||
|
|
||||||
@@ -29,13 +28,13 @@ class Priority:
|
|||||||
|
|
||||||
|
|
||||||
def set_core_affinity(cores: list[int]) -> None:
|
def set_core_affinity(cores: list[int]) -> None:
|
||||||
if sys.platform == 'linux' and not PC:
|
if not PC:
|
||||||
os.sched_setaffinity(0, cores)
|
os.sched_setaffinity(0, cores)
|
||||||
|
|
||||||
|
|
||||||
def config_realtime_process(cores: int | list[int], priority: int) -> None:
|
def config_realtime_process(cores: int | list[int], priority: int) -> None:
|
||||||
gc.disable()
|
gc.disable()
|
||||||
if sys.platform == 'linux' and not PC:
|
if not PC:
|
||||||
os.sched_setscheduler(0, os.SCHED_FIFO, os.sched_param(priority))
|
os.sched_setscheduler(0, os.SCHED_FIFO, os.sched_param(priority))
|
||||||
c = cores if isinstance(cores, list) else [cores, ]
|
c = cores if isinstance(cores, list) else [cores, ]
|
||||||
set_core_affinity(c)
|
set_core_affinity(c)
|
||||||
|
|||||||
@@ -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()
|
||||||
@@ -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
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user