Compare commits

...

9 Commits

Author SHA1 Message Date
Jason Wen 737a6c4236 [TICI] ci: point to master-tici for certain submodules (#1350)
* ci: point to master-tici for certain submodules

* new

* new

* minimal

* check

* revert

* try this out

* more
2025-10-09 20:33:28 -04:00
Jason Wen 86cd1b238d [TICI] relock after inplace metadrive update (#1321) (#1348)
relock after inplace metadrive update (#1321)

relock after inplace metadrive update (#36256)

* relock after inplace metadrive update

* Revert "relock after inplace metadrive update"

This reverts commit 18193ffe34b66085e18605e6c9289ddcd658844d.

* just the hash

(cherry picked from commit 4d53a26a06)


(cherry picked from commit cca3be3a96)

Co-authored-by: DevTekVE <devtekve@gmail.com>
Co-authored-by: Armand du Parc Locmaria <adpl33@gmail.com>
2025-10-09 19:11:59 -04:00
DevTekVE 772e62127e (TICI) bugfix: param store to support the latest param changes (#1246)
* bugfix: update parameter handling to use custom CarControlSP.Param and improve param retrieval

* bump opendbc

* bump opendbc
2025-09-14 20:29:26 +02:00
DevTekVE e7a3ea1b32 (TICI) sync master and master-tici (#1245)
* SL: bugfix parameter handling in sunnylink restore and remote setting (#1234)

* refactor: improve parameter handling in sunnylink for robustness

- Updated `get_param_as_byte` to return `None` for nonexistent parameters.
- Enhanced param compression and encoding in `sunnylinkd`.

* refactor: centralize parameter restoration with new helper function

- Added `save_param_from_base64_encoded_string` to handle param decoding and saving.
- Updated backup manager and sunnylinkd to use the new method.
- Improved code readability and reduced duplication in parameter handling logic.

* don't bother

* clean

(cherry picked from commit b7f8dd11a5)

* UI: Developer UI (#1233)

(cherry picked from commit 1bb4ca2547)

* Revert & Reapply "UI: Developer UI" temporarily due to QT version mismatch (#1237)

* Revert "UI: Developer UI (#1233)"

This reverts commit 1bb4ca2547.

* Reapply "UI: Developer UI (#1233)"

This reverts commit b0a77049da.

* QColorConstants is not on device's QT version. Thanks @kumar for the fix

(cherry picked from commit 810a2d9448)

* Revert "UI: Developer UI" (#1238)

* Revert "Revert & Reapply "UI: Developer UI" temporarily due to QT version mismatch (#1237)"

This reverts commit 810a2d9448.

* Revert "UI: Developer UI (#1233)"

This reverts commit 1bb4ca2547.

(cherry picked from commit 1be13fdc55)

* Reapply "UI: Developer UI" (#1238) (#1239)

This reverts commit 1be13fdc55.

Co-authored-by: Jason Wen <haibin.wen3@gmail.com>
(cherry picked from commit 4f44d6e643)

---------

Co-authored-by: Nayan <nayan8teen@gmail.com>
Co-authored-by: Jason Wen <haibin.wen3@gmail.com>
2025-09-14 12:18:59 +02:00
Jason Wen 87af129090 tici: fix staging root updates (#1224) 2025-09-06 18:46:19 -04:00
Jason Wen f6cf1336ef update: actually detect device type as TICI (#1222) 2025-09-06 18:08:26 -04:00
Jason Wen 51046dd7fd ci: restore cache based on master-tici 2025-09-06 17:43:36 -04:00
Jason Wen 2c85f29a0b staging-c3-new > staging-tici 2025-09-06 17:38:11 -04:00
Jason Wen 68af0c4a17 ci: update for master-tici (#1220)
* ci: master > master-ci

* disable reset and squash for master-tici

* disable release drafter

* docker img

* temp builds

* actual temp

* new ref

* Revert "actual temp"

This reverts commit 2e73befd17.

* wrong name

* Revert "wrong name"

This reverts commit 5efa687aaf.

* Reapply "actual temp"

This reverts commit 75a9d986fd.

* Revert "Reapply "actual temp""

This reverts commit 1f77d902d4.

* Reapply "wrong name"

This reverts commit ed24def13c.

* revert
2025-09-06 17:28:43 -04:00
49 changed files with 764 additions and 139 deletions
+2 -2
View File
@@ -29,11 +29,11 @@ jobs:
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
target: /^(?!master$).*/ target: /^(?!master-tici$).*/
exclude: /sunnypilot:.*/ exclude: /sunnypilot:.*/
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-tici` branch"
update-pr-labels: update-pr-labels:
name: Update fork's PR Labels name: Update fork's PR Labels
+1 -1
View File
@@ -5,7 +5,7 @@ on:
workflow_dispatch: workflow_dispatch:
env: env:
BASE_IMAGE: sunnypilot-base BASE_IMAGE: sunnypilot-tici-base
DOCKER_REGISTRY: ghcr.io/sunnypilot DOCKER_REGISTRY: ghcr.io/sunnypilot
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 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
+2 -2
View File
@@ -3,7 +3,7 @@ name: cereal validation
on: on:
push: push:
branches: branches:
- master - master-tici
pull_request: pull_request:
paths: paths:
- 'cereal/**' - 'cereal/**'
@@ -16,7 +16,7 @@ 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-tici' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
cancel-in-progress: true cancel-in-progress: true
env: env:
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: ${{fromJSON(needs.setup.outputs.ci_runs)}} matrix: ${{fromJSON(needs.setup.outputs.ci_runs)}}
uses: sunnypilot/sunnypilot/.github/workflows/ci_weekly_run.yaml@master uses: sunnypilot/sunnypilot/.github/workflows/ci_weekly_run.yaml@master-tici
with: with:
run_number: ${{ matrix.run_number }} run_number: ${{ matrix.run_number }}
+1 -1
View File
@@ -12,6 +12,6 @@ concurrency:
jobs: jobs:
selfdrive_tests: selfdrive_tests:
uses: sunnypilot/sunnypilot/.github/workflows/selfdrive_tests.yaml@master uses: sunnypilot/sunnypilot/.github/workflows/selfdrive_tests.yaml@master-tici
with: with:
run_number: ${{ inputs.run_number }} run_number: ${{ inputs.run_number }}
@@ -15,7 +15,7 @@ runs:
scons -j$(nproc) --cache-populate" scons -j$(nproc) --cache-populate"
- name: Save scons cache - name: Save scons cache
uses: actions/cache/save@v4 uses: actions/cache/save@v4
if: github.ref == 'refs/heads/master' if: github.ref == 'refs/heads/master-tici'
with: with:
path: .ci_cache/scons_cache path: .ci_cache/scons_cache
key: scons-${{ runner.arch }}-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }} key: scons-${{ runner.arch }}-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
+4 -4
View File
@@ -3,7 +3,7 @@ name: docs
on: on:
push: push:
branches: branches:
- master - master-tici
pull_request: pull_request:
workflow_call: workflow_call:
inputs: inputs:
@@ -12,7 +12,7 @@ on:
required: true required: true
type: string type: string
concurrency: concurrency:
group: docs-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 }} group: docs-tests-ci-run-${{ inputs.run_number }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master-tici' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
cancel-in-progress: true cancel-in-progress: true
jobs: jobs:
@@ -35,13 +35,13 @@ jobs:
# Push to docs.comma.ai # Push to docs.comma.ai
- uses: actions/checkout@v4 - uses: actions/checkout@v4
if: github.ref == 'refs/heads/master' && github.repository == 'sunnypilot/sunnypilot' if: github.ref == 'refs/heads/master-tici' && github.repository == 'sunnypilot/sunnypilot'
with: with:
path: openpilot-docs path: openpilot-docs
ssh-key: ${{ secrets.OPENPILOT_DOCS_KEY }} ssh-key: ${{ secrets.OPENPILOT_DOCS_KEY }}
repository: sunnypilot/sunnypilot-docs repository: sunnypilot/sunnypilot-docs
- name: Push - name: Push
if: github.ref == 'refs/heads/master' && github.repository == 'sunnypilot/sunnypilot' if: github.ref == 'refs/heads/master-tici' && github.repository == 'sunnypilot/sunnypilot'
run: | run: |
set -x set -x
+2 -2
View File
@@ -24,11 +24,11 @@ jobs:
if: ${{ github.event_name != 'workflow_dispatch' }} 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-tici
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).*).)*$ check-regexp: ^((?!.*(build __nightly).*).)*$
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true
+1
View File
@@ -19,6 +19,7 @@ jobs:
contents: write contents: write
pull-requests: write pull-requests: write
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: False
steps: steps:
- uses: release-drafter/release-drafter@v6 - uses: release-drafter/release-drafter@v6
with: with:
+2 -2
View File
@@ -10,7 +10,7 @@ jobs:
env: env:
ImageOS: ubuntu24 ImageOS: ubuntu24
container: container:
image: ghcr.io/sunnypilot/sunnypilot-base:latest image: ghcr.io/sunnypilot/sunnypilot-tici-base:latest
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.repository == 'sunnypilot/sunnypilot' if: github.repository == 'sunnypilot/sunnypilot'
permissions: permissions:
@@ -25,7 +25,7 @@ jobs:
if: ${{ github.event_name == 'schedule' }} if: ${{ github.event_name == 'schedule' }}
uses: lewagon/wait-on-check-action@ccfb013c15c8afb7bf2b7c028fb74dc5a068cccc uses: lewagon/wait-on-check-action@ccfb013c15c8afb7bf2b7c028fb74dc5a068cccc
with: with:
ref: master ref: master-tici
wait-interval: 30 wait-interval: 30
running-workflow-name: 'build __nightly' running-workflow-name: 'build __nightly'
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
+4 -4
View File
@@ -6,7 +6,7 @@ on:
workflow_dispatch: workflow_dispatch:
env: env:
BASE_IMAGE: sunnypilot-base BASE_IMAGE: sunnypilot-tici-base
BUILD: release/ci/docker_build_sp.sh base BUILD: release/ci/docker_build_sp.sh base
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c 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
@@ -28,7 +28,7 @@ jobs:
title: "[bot] Update translations" title: "[bot] Update translations"
body: "Automatic PR from repo-maintenance -> update_translations" body: "Automatic PR from repo-maintenance -> update_translations"
branch: "update-translations" branch: "update-translations"
base: "master" base: "master-tici"
delete-branch: true delete-branch: true
labels: bot labels: bot
@@ -36,7 +36,7 @@ jobs:
name: package_updates name: package_updates
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
image: ghcr.io/sunnypilot/sunnypilot-base:latest image: ghcr.io/sunnypilot/sunnypilot-tici-base:latest
if: github.repository == 'sunnypilot/sunnypilot' if: github.repository == 'sunnypilot/sunnypilot'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -68,7 +68,7 @@ jobs:
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-tici
delete-branch: true delete-branch: true
body: 'Automatic PR from repo-maintenance -> package_updates' body: 'Automatic PR from repo-maintenance -> package_updates'
labels: bot labels: bot
+10 -10
View File
@@ -3,7 +3,7 @@ name: selfdrive
on: on:
push: push:
branches: branches:
- master - master-tici
pull_request: pull_request:
workflow_dispatch: workflow_dispatch:
workflow_call: workflow_call:
@@ -14,12 +14,12 @@ on:
type: string type: string
concurrency: concurrency:
group: selfdrive-tests-ci-run-${{ inputs.run_number }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }} group: selfdrive-tests-ci-run-${{ inputs.run_number }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master-tici' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
cancel-in-progress: true cancel-in-progress: true
env: env:
PYTHONWARNINGS: error PYTHONWARNINGS: error
BASE_IMAGE: sunnypilot-base BASE_IMAGE: sunnypilot-tici-base
AZURE_TOKEN: ${{ secrets.AZURE_COMMADATACI_OPENPILOTCI_TOKEN }} AZURE_TOKEN: ${{ secrets.AZURE_COMMADATACI_OPENPILOTCI_TOKEN }}
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 }}
@@ -68,10 +68,10 @@ jobs:
if: github.repository == 'sunnypilot/sunnypilot' if: github.repository == 'sunnypilot/sunnypilot'
timeout-minutes: 3 timeout-minutes: 3
run: | run: |
if [ "${{ github.ref }}" != "refs/heads/master" ]; then if [ "${{ github.ref }}" != "refs/heads/master-tici" ]; then
git fetch origin master:refs/remotes/origin/master git fetch origin master-tici:refs/remotes/origin/master-tici
SUBMODULE_PATHS=$(git diff origin/master HEAD --name-only | grep -E '^[^/]+$' | while read path; do SUBMODULE_PATHS=$(git diff origin/master-tici HEAD --name-only | grep -E '^[^/]+$' | while read path; do
if git ls-files --stage "$path" | grep -q "^160000"; then if git ls-files --stage "$path" | grep -q "^160000"; then
echo "$path" echo "$path"
fi fi
@@ -97,7 +97,7 @@ jobs:
with: with:
submodules: true submodules: true
- name: Setup docker push - name: Setup docker push
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'sunnypilot/sunnypilot' if: github.ref == 'refs/heads/master-tici' && github.event_name != 'pull_request' && github.repository == 'sunnypilot/sunnypilot'
run: | run: |
echo "PUSH_IMAGE=true" >> "$GITHUB_ENV" echo "PUSH_IMAGE=true" >> "$GITHUB_ENV"
$DOCKER_LOGIN $DOCKER_LOGIN
@@ -129,7 +129,7 @@ jobs:
PYTHONWARNINGS: default PYTHONWARNINGS: default
- name: Save Homebrew cache - name: Save Homebrew cache
uses: actions/cache/save@v4 uses: actions/cache/save@v4
if: github.ref == 'refs/heads/master' if: github.ref == 'refs/heads/master-tici'
with: with:
path: ~/Library/Caches/Homebrew path: ~/Library/Caches/Homebrew
key: brew-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }} key: brew-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
@@ -146,7 +146,7 @@ jobs:
run: . .venv/bin/activate && scons -j$(nproc) run: . .venv/bin/activate && scons -j$(nproc)
- name: Save scons cache - name: Save scons cache
uses: actions/cache/save@v4 uses: actions/cache/save@v4
if: github.ref == 'refs/heads/master' if: github.ref == 'refs/heads/master-tici'
with: with:
path: /tmp/scons_cache path: /tmp/scons_cache
key: scons-${{ runner.arch }}-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }} key: scons-${{ runner.arch }}-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
@@ -305,5 +305,5 @@ jobs:
- name: Upload Test Report - name: Upload Test Report
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }} name: report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master-tici' && 'master-tici' || github.event.number }}
path: selfdrive/ui/tests/test_ui/report_1/screenshots path: selfdrive/ui/tests/test_ui/report_1/screenshots
@@ -14,7 +14,7 @@ on:
upstream_branch: upstream_branch:
description: 'Upstream branch to build from' description: 'Upstream branch to build from'
required: true required: true
default: 'master' default: 'master-tici'
type: string type: string
custom_name: custom_name:
description: 'Custom name for the model (no date, only name)' description: 'Custom name for the model (no date, only name)'
@@ -35,7 +35,7 @@ on:
upstream_branch: upstream_branch:
description: 'Upstream branch to build from' description: 'Upstream branch to build from'
required: true required: true
default: 'master' default: 'master-tici'
type: string type: string
custom_name: custom_name:
description: 'Custom name for the model (no date, only name)' description: 'Custom name for the model (no date, only name)'
@@ -8,14 +8,14 @@ env:
PUBLIC_REPO_URL: "https://github.com/sunnypilot/sunnypilot" PUBLIC_REPO_URL: "https://github.com/sunnypilot/sunnypilot"
# Branch configurations # Branch configurations
STAGING_C3_SOURCE_BRANCH: ${{ vars.STAGING_C3_SOURCE_BRANCH || 'master' }} # vars are set on repo settings. STAGING_C3_SOURCE_BRANCH: 'master-tici' # vars.STAGING_C3_SOURCE_BRANCH could be used, set on repo settings.
# Runtime configuration # Runtime configuration
SOURCE_BRANCH: "${{ github.head_ref || github.ref_name }}" SOURCE_BRANCH: "${{ github.head_ref || github.ref_name }}"
on: on:
push: push:
branches: [ master, master-dev-c3-new ] branches: [ master-tici ]
tags: [ 'release/*' ] tags: [ 'release/*' ]
pull_request_target: pull_request_target:
types: [ labeled ] types: [ labeled ]
@@ -1,7 +1,7 @@
name: Build dev-c3-new name: Build dev-c3-new
env: env:
DEFAULT_SOURCE_BRANCH: "master" DEFAULT_SOURCE_BRANCH: "master-tici"
DEFAULT_TARGET_BRANCH: "master-dev-c3-new" DEFAULT_TARGET_BRANCH: "master-dev-c3-new"
PR_LABEL: "dev-c3" 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'
@@ -10,17 +10,17 @@ env:
on: on:
push: push:
branches: branches:
- master - master-tici
pull_request_target: pull_request_target:
types: [ labeled ] types: [ labeled ]
branches: branches:
- 'master' - 'master-tici'
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-tici'
type: string type: string
target_branch: target_branch:
description: 'Target branch to reset and squash into' description: 'Target branch to reset and squash into'
@@ -40,11 +40,7 @@ concurrency:
jobs: jobs:
reset-and-squash: reset-and-squash:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ( if: False
(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 == 'dev-c3' || github.event.label.name == 'trust-fork-pr') && contains(github.event.pull_request.labels.*.name, 'dev-c3'))))
)
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
+10 -10
View File
@@ -2,19 +2,19 @@ name: "ui preview"
on: on:
push: push:
branches: branches:
- master - master-tici
pull_request_target: pull_request_target:
types: [assigned, opened, synchronize, reopened, edited] types: [assigned, opened, synchronize, reopened, edited]
branches: branches:
- 'master' - 'master-tici'
paths: paths:
- 'selfdrive/ui/**' - 'selfdrive/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-tici' && 'master-tici' || 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-tici' && github.sha || github.event.pull_request.head.sha }}
BRANCH_NAME: "openpilot/pr-${{ github.event.number }}" BRANCH_NAME: "openpilot/pr-${{ github.event.number }}"
jobs: jobs:
@@ -55,7 +55,7 @@ jobs:
name: report-1-${{ env.REPORT_NAME }} name: report-1-${{ env.REPORT_NAME }}
path: ${{ github.workspace }}/pr_ui path: ${{ github.workspace }}/pr_ui
- name: Getting master ui - name: Getting master-tici ui
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
repository: sunnypilot/ci-artifacts repository: sunnypilot/ci-artifacts
@@ -63,8 +63,8 @@ jobs:
path: ${{ github.workspace }}/master_ui path: ${{ github.workspace }}/master_ui
ref: openpilot_master_ui ref: openpilot_master_ui
- name: Saving new master ui - name: Saving new master-tici ui
if: github.ref == 'refs/heads/master' && github.event_name == 'push' if: github.ref == 'refs/heads/master-tici' && github.event_name == 'push'
working-directory: ${{ github.workspace }}/master_ui working-directory: ${{ github.workspace }}/master_ui
run: | run: |
git checkout --orphan=new_master_ui git checkout --orphan=new_master_ui
@@ -93,9 +93,9 @@ jobs:
for ((i=0; i<${#A[*]}; i=i+1)); for ((i=0; i<${#A[*]}; i=i+1));
do do
# Check if the master file exists # Check if the master-tici file exists
if [ ! -f "${{ github.workspace }}/master_ui/${A[$i]}.png" ]; then if [ ! -f "${{ github.workspace }}/master_ui/${A[$i]}.png" ]; then
# This is a new file in PR UI that doesn't exist in master # This is a new file in PR UI that doesn't exist in master-tici
DIFF="${DIFF}<details open>" DIFF="${DIFF}<details open>"
DIFF="${DIFF}<summary>${A[$i]} : \$\${\\color{cyan}\\text{NEW}}\$\$</summary>" DIFF="${DIFF}<summary>${A[$i]} : \$\${\\color{cyan}\\text{NEW}}\$\$</summary>"
DIFF="${DIFF}<table>" DIFF="${DIFF}<table>"
@@ -118,7 +118,7 @@ jobs:
DIFF="${DIFF}<table>" DIFF="${DIFF}<table>"
DIFF="${DIFF}<tr>" 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> master-tici <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}_master_ref.png\"> </td>"
DIFF="${DIFF} <td> proposed <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}.png\"> </td>" DIFF="${DIFF} <td> proposed <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}.png\"> </td>"
DIFF="${DIFF}</tr>" DIFF="${DIFF}</tr>"
+1 -1
View File
@@ -1,4 +1,4 @@
FROM ghcr.io/sunnypilot/sunnypilot-base:latest FROM ghcr.io/sunnypilot/sunnypilot-tici-base:latest
ENV PYTHONUNBUFFERED=1 ENV PYTHONUNBUFFERED=1
+9 -10
View File
@@ -22,7 +22,7 @@ https://docs.sunnypilot.ai/ is your one stop shop for everything from features t
Detailed instructions for [how to mount the device in a car](https://comma.ai/setup). Detailed instructions for [how to mount the device in a car](https://comma.ai/setup).
## Installation ## Installation
Please refer to [Recommended Branches](#-recommended-branches) to find your preferred/supported branch. This guide will assume you want to install the latest `staging-c3-new` branch. Please refer to [Recommended Branches](#-recommended-branches) to find your preferred/supported branch. This guide will assume you want to install the latest `staging-tici` branch.
### If you want to use our newest branches (our rewrite) ### If you want to use our newest branches (our rewrite)
> [!TIP] > [!TIP]
@@ -31,25 +31,24 @@ Please refer to [Recommended Branches](#-recommended-branches) to find your pref
* sunnypilot not installed or you installed a version before 0.8.17? * 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. 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. 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: ```https://staging-c3-new.sunnypilot.ai```. 3. Input the installation URL per [Recommended Branches](#-recommended-branches). Example: ```https://staging-tici.sunnypilot.ai```.
4. Complete the rest of the installation following the onscreen instructions. 4. Complete the rest of the installation following the onscreen instructions.
* sunnypilot already installed and you installed a version after 0.8.17? * sunnypilot already installed and you installed a version after 0.8.17?
1. On the comma three, go to `Settings` ▶️ `Software`. 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. 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. 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: `staging-c3-new` 4. Scroll to select the desired branch per Recommended Branches (see below). Example: `staging-tici`
| Branch | Installation URL | | Branch | Installation URL |
|:----------------:|:---------------------------------------------:| |:---------------:|:---------------------------------------------:|
| `staging-c3-new` | `https://staging-c3-new.sunnypilot.ai` | | `staging-tici` | `https://staging-tici.sunnypilot.ai` |
| `dev-c3-new` | `https://dev-c3-new.sunnypilot.ai` | | `custom-branch` | `https://install.sunnypilot.ai/{branch_name}` |
| `custom-branch` | `https://install.sunnypilot.ai/{branch_name}` | | `release-tici` | **Not yet available**. |
| `release-c3-new` | **Not yet available**. |
> [!TIP] > [!TIP]
> You can use sunnypilot/targetbranch as an install URL. Example: 'sunnypilot/staging-c3-new'. > You can use sunnypilot/targetbranch as an install URL. Example: 'sunnypilot/staging-tici'.
> [!NOTE] > [!NOTE]
> Do you require further assistance with software installation? Join the [sunnypilot Discord server](https://discord.sunnypilot.com) and message us in the `#installation-help` channel. > Do you require further assistance with software installation? Join the [sunnypilot Discord server](https://discord.sunnypilot.com) and message us in the `#installation-help` channel.
+14 -1
View File
@@ -202,7 +202,20 @@ struct CarControlSP @0xa5cd762cd951a455 {
struct Param { struct Param {
key @0 :Text; key @0 :Text;
value @1 :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;
} }
} }
+1
View File
@@ -146,6 +146,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"CustomAccLongPressIncrement", {PERSISTENT | BACKUP, INT, "5"}}, {"CustomAccLongPressIncrement", {PERSISTENT | BACKUP, INT, "5"}},
{"CustomAccShortPressIncrement", {PERSISTENT | BACKUP, INT, "1"}}, {"CustomAccShortPressIncrement", {PERSISTENT | BACKUP, INT, "1"}},
{"DeviceBootMode", {PERSISTENT | BACKUP, INT, "0"}}, {"DeviceBootMode", {PERSISTENT | BACKUP, INT, "0"}},
{"DevUIInfo", {PERSISTENT | BACKUP, INT, "0"}},
{"EnableCopyparty", {PERSISTENT | BACKUP, BOOL}}, {"EnableCopyparty", {PERSISTENT | BACKUP, BOOL}},
{"EnableGithubRunner", {PERSISTENT | BACKUP, BOOL}}, {"EnableGithubRunner", {PERSISTENT | BACKUP, BOOL}},
{"GithubRunnerSufficientVoltage", {CLEAR_ON_MANAGER_START , BOOL}}, {"GithubRunnerSufficientVoltage", {CLEAR_ON_MANAGER_START , BOOL}},
+18 -6
View File
@@ -31,13 +31,25 @@ while read hash submodule ref; do
exit 1 exit 1
fi fi
else else
git -C $submodule fetch --depth 100 origin master # Check against master-tici for specific submodules, master for others
git -C $submodule branch -r --contains $hash | grep "origin/master" if [ "$submodule" = "opendbc_repo" ] || [ "$submodule" = "panda" ]; then
if [ "$?" -eq 0 ]; then git -C $submodule fetch --depth 100 origin master-tici
echo "$submodule ok" remote_hash=$(git -C $submodule rev-parse FETCH_HEAD)
if git -C $submodule merge-base --is-ancestor $hash $remote_hash; then
echo "$submodule ok"
else
echo "$submodule: $hash is not on master-tici"
exit 1
fi
else else
echo "$submodule: $hash is not on master" git -C $submodule fetch --depth 100 origin master
exit 1 git -C $submodule branch -r --contains $hash | grep "origin/master"
if [ "$?" -eq 0 ]; then
echo "$submodule ok"
else
echo "$submodule: $hash is not on master"
exit 1
fi
fi fi
fi fi
done <<< $(git submodule status --recursive) done <<< $(git submodule status --recursive)
+2 -2
View File
@@ -1,8 +1,8 @@
if [ "$1" = "base" ]; then if [ "$1" = "base" ]; then
export DOCKER_IMAGE=sunnypilot-base export DOCKER_IMAGE=sunnypilot-tici-base
export DOCKER_FILE=Dockerfile.sunnypilot_base export DOCKER_FILE=Dockerfile.sunnypilot_base
elif [ "$1" = "prebuilt" ]; then elif [ "$1" = "prebuilt" ]; then
export DOCKER_IMAGE=sunnypilot-prebuilt export DOCKER_IMAGE=sunnypilot-tici-prebuilt
export DOCKER_FILE=Dockerfile.sunnypilot export DOCKER_FILE=Dockerfile.sunnypilot
else else
echo "Invalid docker build image: '$1'" echo "Invalid docker build image: '$1'"
+9
View File
@@ -4,6 +4,9 @@
#include <map> #include <map>
#include "selfdrive/ui/qt/util.h" #include "selfdrive/ui/qt/util.h"
#ifdef SUNNYPILOT
#include "selfdrive/ui/sunnypilot/ui.h"
#endif
void OnroadAlerts::updateState(const UIState &s) { void OnroadAlerts::updateState(const UIState &s) {
Alert a = getAlert(*(s.sm), s.scene.started_frame); Alert a = getAlert(*(s.sm), s.scene.started_frame);
@@ -73,6 +76,12 @@ void OnroadAlerts::paintEvent(QPaintEvent *event) {
} }
QRect r = QRect(0 + margin, height() - h + margin, width() - margin*2, h - margin*2); QRect r = QRect(0 + margin, height() - h + margin, width() - margin*2, h - margin*2);
#ifdef SUNNYPILOT
const int dev_ui_info = uiStateSP()->scene.dev_ui_info;
const int adjustment = dev_ui_info > 1 && alert.size != cereal::SelfdriveState::AlertSize::FULL ? 30 : 0;
r = QRect(0 + margin, height() - h + margin - adjustment, width() - margin*2, h - margin*2);
#endif
QPainter p(this); QPainter p(this);
// draw background + gradient // draw background + gradient
@@ -12,6 +12,7 @@
#include "selfdrive/ui/sunnypilot/qt/onroad/model.h" #include "selfdrive/ui/sunnypilot/qt/onroad/model.h"
#define ExperimentalButton ExperimentalButtonSP #define ExperimentalButton ExperimentalButtonSP
#define ModelRenderer ModelRendererSP #define ModelRenderer ModelRendererSP
#define HudRenderer HudRendererSP
#else #else
#include "selfdrive/ui/qt/onroad/buttons.h" #include "selfdrive/ui/qt/onroad/buttons.h"
#include "selfdrive/ui/qt/onroad/hud.h" #include "selfdrive/ui/qt/onroad/hud.h"
@@ -73,6 +73,11 @@ void DriverMonitorRenderer::draw(QPainter &painter, const QRect &surface_rect) {
float y = surface_rect.height() - offset; float y = surface_rect.height() - offset;
float opacity = is_active ? 0.65f : 0.2f; float opacity = is_active ? 0.65f : 0.2f;
#ifdef SUNNYPILOT
const int dev_ui_info = uiStateSP()->scene.dev_ui_info;
y -= dev_ui_info > 1 ? 50 : 0;
#endif
drawIcon(painter, QPoint(x, y), dm_img, QColor(0, 0, 0, 70), opacity); drawIcon(painter, QPoint(x, y), dm_img, QColor(0, 0, 0, 70), opacity);
QPointF keypoints[std::size(DEFAULT_FACE_KPTS_3D)]; QPointF keypoints[std::size(DEFAULT_FACE_KPTS_3D)];
+1
View File
@@ -39,6 +39,7 @@ qt_src = [
"sunnypilot/qt/offroad/settings/visuals_panel.cc", "sunnypilot/qt/offroad/settings/visuals_panel.cc",
"sunnypilot/qt/onroad/annotated_camera.cc", "sunnypilot/qt/onroad/annotated_camera.cc",
"sunnypilot/qt/onroad/buttons.cc", "sunnypilot/qt/onroad/buttons.cc",
"sunnypilot/qt/onroad/developer_ui/developer_ui.cc",
"sunnypilot/qt/onroad/hud.cc", "sunnypilot/qt/onroad/hud.cc",
"sunnypilot/qt/onroad/model.cc", "sunnypilot/qt/onroad/model.cc",
"sunnypilot/qt/onroad/onroad_home.cc", "sunnypilot/qt/onroad/onroad_home.cc",
@@ -72,6 +72,15 @@ VisualsPanel::VisualsPanel(QWidget *parent) : QWidget(parent) {
list->addItem(chevron_info_settings); list->addItem(chevron_info_settings);
param_watcher->addParam("ChevronInfo"); param_watcher->addParam("ChevronInfo");
// Visuals: Developer UI Info (Dev UI)
std::vector<QString> dev_ui_settings_texts{tr("Off"), tr("Right"), tr("Right &&\nBottom")};
dev_ui_settings = new ButtonParamControlSP(
"DevUIInfo", tr("Developer UI"), tr("Display real-time parameters and metrics from various sources."),
"",
dev_ui_settings_texts,
380);
list->addItem(dev_ui_settings);
sunnypilotScroller = new ScrollViewSP(list, this); sunnypilotScroller = new ScrollViewSP(list, this);
vlayout->addWidget(sunnypilotScroller); vlayout->addWidget(sunnypilotScroller);
@@ -90,4 +99,7 @@ void VisualsPanel::paramsRefresh() {
if (chevron_info_settings) { if (chevron_info_settings) {
chevron_info_settings->refresh(); chevron_info_settings->refresh();
} }
if (dev_ui_settings) {
dev_ui_settings->refresh();
}
} }
@@ -28,4 +28,5 @@ protected:
std::map<std::string, ParamControlSP*> toggles; std::map<std::string, ParamControlSP*> toggles;
ParamWatcher * param_watcher; ParamWatcher * param_watcher;
ButtonParamControlSP *chevron_info_settings; ButtonParamControlSP *chevron_info_settings;
ButtonParamControlSP *dev_ui_settings;
}; };
@@ -14,3 +14,8 @@ AnnotatedCameraWidgetSP::AnnotatedCameraWidgetSP(VisionStreamType type, QWidget
void AnnotatedCameraWidgetSP::updateState(const UIState &s) { void AnnotatedCameraWidgetSP::updateState(const UIState &s) {
AnnotatedCameraWidget::updateState(s); AnnotatedCameraWidget::updateState(s);
} }
void AnnotatedCameraWidgetSP::showEvent(QShowEvent *event) {
AnnotatedCameraWidget::showEvent(event);
ui_update_params_sp(uiState());
}
@@ -15,4 +15,7 @@ class AnnotatedCameraWidgetSP : public AnnotatedCameraWidget {
public: public:
explicit AnnotatedCameraWidgetSP(VisionStreamType type, QWidget *parent = nullptr); explicit AnnotatedCameraWidgetSP(VisionStreamType type, QWidget *parent = nullptr);
void updateState(const UIState &s) override; void updateState(const UIState &s) override;
protected:
void showEvent(QShowEvent *event) override;
}; };
@@ -0,0 +1,227 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include <cmath>
#include "common/util.h"
#include "selfdrive/ui/sunnypilot/qt/onroad/developer_ui/developer_ui.h"
// Add Relative Distance to Primary Lead Car
// Unit: Meters
UiElement DeveloperUi::getDRel(bool lead_status, float lead_d_rel) {
QString value = lead_status ? QString::number(lead_d_rel, 'f', 0) : "-";
QColor color = QColor(255, 255, 255, 255);
if (lead_status) {
// Orange if close, Red if very close
if (lead_d_rel < 5) {
color = QColor(255, 0, 0, 255);
} else if (lead_d_rel < 15) {
color = QColor(255, 188, 0, 255);
}
}
return UiElement(value, "REL DIST", "m", color);
}
// Add Relative Velocity vs Primary Lead Car
// Unit: kph if metric, else mph
UiElement DeveloperUi::getVRel(bool lead_status, float lead_v_rel, bool is_metric, const QString &speed_unit) {
QString value = lead_status ? QString::number(lead_v_rel * (is_metric ? MS_TO_KPH : MS_TO_MPH), 'f', 0) : "-";
QColor color = QColor(255, 255, 255, 255);
if (lead_status) {
// Red if approaching faster than 10mph
// Orange if approaching (negative)
if (lead_v_rel < -4.4704) {
color = QColor(255, 0, 0, 255);
} else if (lead_v_rel < 0) {
color = QColor(255, 188, 0, 255);
}
}
return UiElement(value, "REL SPEED", speed_unit, color);
}
// Add Real Steering Angle
// Unit: Degrees
UiElement DeveloperUi::getSteeringAngleDeg(float angle_steers, bool lat_active, bool steer_override) {
QString value = QString("%1%2%3").arg(QString::number(angle_steers, 'f', 1)).arg("°").arg("");
QColor color = lat_active ? (steer_override ? QColor(0x91, 0x9b, 0x95, 0xff) : QColor(0, 255, 0, 255)) : QColor(255, 255, 255, 255);
// Red if large steering angle
// Orange if moderate steering angle
if (std::fabs(angle_steers) > 180) {
color = QColor(255, 0, 0, 255);
} else if (std::fabs(angle_steers) > 90) {
color = QColor(255, 188, 0, 255);
}
return UiElement(value, "REAL STEER", "", color);
}
// Add Actual Lateral Acceleration (roll compensated) when using Torque
// Unit: m/s²
UiElement DeveloperUi::getActualLateralAccel(float curvature, float v_ego, float roll, bool lat_active, bool steer_override) {
double actualLateralAccel = (curvature * pow(v_ego, 2)) - (roll * 9.81);
QString value = QString::number(actualLateralAccel, 'f', 2);
QColor color = lat_active ? (steer_override ? QColor(0x91, 0x9b, 0x95, 0xff) : QColor(0, 255, 0, 255)) : QColor(255, 255, 255, 255);
return UiElement(value, "ACTUAL L.A.", "m/s²", color);
}
// Add Desired Steering Angle when using PID
// Unit: Degrees
UiElement DeveloperUi::getSteeringAngleDesiredDeg(bool lat_active, float steer_angle_desired, float angle_steers) {
QString value = lat_active ? QString("%1%2%3").arg(QString::number(steer_angle_desired, 'f', 1)).arg("°").arg("") : "-";
QColor color = QColor(255, 255, 255, 255);
if (lat_active) {
// Red if large steering angle
// Orange if moderate steering angle
if (std::fabs(angle_steers) > 180) {
color = QColor(255, 0, 0, 255);
} else if (std::fabs(angle_steers) > 90) {
color = QColor(255, 188, 0, 255);
} else {
color = QColor(0, 255, 0, 255);
}
}
return UiElement(value, "DESIRED STEER", "", color);
}
// Add Device Memory (RAM) Usage
// Unit: Percent
UiElement DeveloperUi::getMemoryUsagePercent(int memory_usage_percent) {
QString value = QString("%1%2").arg(QString::number(memory_usage_percent, 'd', 0)).arg("%");
QColor color = (memory_usage_percent > 85) ? QColor(255, 188, 0, 255) : QColor(255, 255, 255, 255);
return UiElement(value, "RAM", "", color);
}
// Add Vehicle Current Acceleration
// Unit: m/s²
UiElement DeveloperUi::getAEgo(float a_ego) {
QString value = QString::number(a_ego, 'f', 1);
QColor color = QColor(255, 255, 255, 255);
return UiElement(value, "ACC.", "m/s²", color);
}
// Add Relative Velocity to Primary Lead Car
// Unit: kph if metric, else mph
UiElement DeveloperUi::getVEgoLead(bool lead_status, float lead_v_rel, float v_ego, bool is_metric, const QString &speed_unit) {
QString value = lead_status ? QString::number((lead_v_rel + v_ego) * (is_metric ? MS_TO_KPH : MS_TO_MPH), 'f', 0) : "-";
QColor color = QColor(255, 255, 255, 255);
if (lead_status) {
// Red if approaching faster than 10mph
// Orange if approaching (negative)
if (lead_v_rel < -4.4704) {
color = QColor(255, 0, 0, 255);
} else if (lead_v_rel < 0) {
color = QColor(255, 188, 0, 255);
}
}
return UiElement(value, "L.S.", speed_unit, color);
}
// Add Friction Coefficient Raw from torqued
// Unit: None
UiElement DeveloperUi::getFrictionCoefficientFiltered(float friction_coefficient_filtered, bool live_valid) {
QString value = QString::number(friction_coefficient_filtered, 'f', 3);
QColor color = live_valid ? QColor(0, 255, 0, 255) : QColor(255, 255, 255, 255);
return UiElement(value, "FRIC.", "", color);
}
// Add Lateral Acceleration Factor Raw from torqued
// Unit: m/s²
UiElement DeveloperUi::getLatAccelFactorFiltered(float lat_accel_factor_filtered, bool live_valid) {
QString value = QString::number(lat_accel_factor_filtered, 'f', 3);
QColor color = live_valid ? QColor(0, 255, 0, 255) : QColor(255, 255, 255, 255);
return UiElement(value, "L.A.", "m/s²", color);
}
// Add Steering Torque from Car EPS
// Unit: Newton Meters
UiElement DeveloperUi::getSteeringTorqueEps(float steering_torque_eps) {
QString value = QString::number(std::fabs(steering_torque_eps), 'f', 1);
QColor color = QColor(255, 255, 255, 255);
return UiElement(value, "E.T.", "N·dm", color);
}
// Add Bearing Degree and Direction from Car (Compass)
// Unit: Meters
UiElement DeveloperUi::getBearingDeg(float bearing_accuracy_deg, float bearing_deg) {
QString value = (bearing_accuracy_deg != 180.00) ? QString("%1%2%3").arg(QString::number(bearing_deg, 'd', 0)).arg("°").arg("") : "-";
QColor color = QColor(255, 255, 255, 255);
QString dir_value;
if (bearing_accuracy_deg != 180.00) {
if (((bearing_deg >= 337.5) && (bearing_deg <= 360)) || ((bearing_deg >= 0) && (bearing_deg <= 22.5))) {
dir_value = "N";
} else if ((bearing_deg > 22.5) && (bearing_deg < 67.5)) {
dir_value = "NE";
} else if ((bearing_deg >= 67.5) && (bearing_deg <= 112.5)) {
dir_value = "E";
} else if ((bearing_deg > 112.5) && (bearing_deg < 157.5)) {
dir_value = "SE";
} else if ((bearing_deg >= 157.5) && (bearing_deg <= 202.5)) {
dir_value = "S";
} else if ((bearing_deg > 202.5) && (bearing_deg < 247.5)) {
dir_value = "SW";
} else if ((bearing_deg >= 247.5) && (bearing_deg <= 292.5)) {
dir_value = "W";
} else if ((bearing_deg > 292.5) && (bearing_deg < 337.5)) {
dir_value = "NW";
}
} else {
dir_value = "OFF";
}
return UiElement(QString("%1 | %2").arg(dir_value).arg(value), "B.D.", "", color);
}
// Add Altitude of Current Location
// Unit: Meters
UiElement DeveloperUi::getAltitude(float gps_accuracy, float altitude) {
QString value = (gps_accuracy != 0.00) ? QString::number(altitude, 'f', 1) : "-";
QColor color = QColor(255, 255, 255, 255);
return UiElement(value, "ALT.", "m", color);
}
// Add Actuators Output
// Unit: Degree (angle) or m/s² (torque)
UiElement DeveloperUi::getActuatorsOutputLateral(cereal::CarParams::SteerControlType steerControlType,
cereal::CarControl::Actuators::Reader &actuators,
float desiredCurvature, float v_ego, float roll, bool lat_active, bool steer_override) {
QString label;
QString value;
QString unit;
if (steerControlType == cereal::CarParams::SteerControlType::ANGLE) {
label = "DESIRED STEER";
value = QString("%1%2%3").arg(QString::number(actuators.getSteeringAngleDeg(), 'f', 1)).arg("°").arg("");
} else {
label = "DESIRED L.A.";
double desiredLateralAccel = (desiredCurvature * pow(v_ego, 2)) - (roll * 9.81);
value = QString::number(desiredLateralAccel, 'f', 2);
unit = "m/s²";
}
value = lat_active ? value : "-";
QColor color = lat_active ? (steer_override ? QColor(0x91, 0x9b, 0x95, 0xff) : QColor(0, 255, 0, 255)) : QColor(255, 255, 255, 255);
return UiElement(value, label, unit, color);
}
@@ -0,0 +1,31 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/sunnypilot/qt/onroad/developer_ui/ui_elements.h"
class DeveloperUi {
public:
static UiElement getDRel(bool lead_status, float lead_d_rel);
static UiElement getVRel(bool lead_status, float lead_v_rel, bool is_metric, const QString &speed_unit);
static UiElement getSteeringAngleDeg(float angle_steers, bool lat_active, bool steer_override);
static UiElement getActualLateralAccel(float curvature, float v_ego, float roll, bool lat_active, bool steer_override);
static UiElement getSteeringAngleDesiredDeg(bool lat_active, float steer_angle_desired, float angle_steers);
static UiElement getMemoryUsagePercent(int memory_usage_percent);
static UiElement getAEgo(float a_ego);
static UiElement getVEgoLead(bool lead_status, float lead_v_rel, float v_ego, bool is_metric, const QString &speed_unit);
static UiElement getFrictionCoefficientFiltered(float friction_coefficient_filtered, bool live_valid);
static UiElement getLatAccelFactorFiltered(float lat_accel_factor_filtered, bool live_valid);
static UiElement getSteeringTorqueEps(float steering_torque_eps);
static UiElement getBearingDeg(float bearing_accuracy_deg, float bearing_deg);
static UiElement getAltitude(float gps_accuracy, float altitude);
static UiElement getActuatorsOutputLateral(cereal::CarParams::SteerControlType steerControlType,
cereal::CarControl::Actuators::Reader &actuators,
float desiredCurvature, float v_ego, float roll, bool lat_active, bool steer_override);
};
@@ -0,0 +1,19 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include <QColor>
struct UiElement {
QString value{};
QString label{};
QString units{};
QColor color{};
explicit UiElement(const QString &value = "", const QString &label = "", const QString &units = "", const QColor &color = QColor(255, 255, 255, 255))
: value(value), label(label), units(units), color(color) {}
};
+190
View File
@@ -7,12 +7,202 @@
#include "selfdrive/ui/sunnypilot/qt/onroad/hud.h" #include "selfdrive/ui/sunnypilot/qt/onroad/hud.h"
#include "selfdrive/ui/qt/util.h"
HudRendererSP::HudRendererSP() {} HudRendererSP::HudRendererSP() {}
void HudRendererSP::updateState(const UIState &s) { void HudRendererSP::updateState(const UIState &s) {
HudRenderer::updateState(s); HudRenderer::updateState(s);
const SubMaster &sm = *(s.sm);
const bool cs_alive = sm.alive("controlsState");
const auto cs = sm["controlsState"].getControlsState();
const auto car_state = sm["carState"].getCarState();
const auto car_control = sm["carControl"].getCarControl();
const auto radar_state = sm["radarState"].getRadarState();
const auto is_gps_location_external = sm.rcv_frame("gpsLocationExternal") > 1;
const auto gpsLocation = is_gps_location_external ? sm["gpsLocationExternal"].getGpsLocationExternal() : sm["gpsLocation"].getGpsLocation();
const auto ltp = sm["liveTorqueParameters"].getLiveTorqueParameters();
const auto car_params = sm["carParams"].getCarParams();
static int reverse_delay = 0;
bool reverse_allowed = false;
if (int(car_state.getGearShifter()) != 4) {
reverse_delay = 0;
reverse_allowed = false;
} else {
reverse_delay += 50;
if (reverse_delay >= 1000) {
reverse_allowed = true;
}
}
reversing = reverse_allowed;
is_metric = s.scene.is_metric;
// Handle older routes where vEgoCluster is not set
v_ego_cluster_seen = v_ego_cluster_seen || car_state.getVEgoCluster() != 0.0;
float v_ego = v_ego_cluster_seen ? car_state.getVEgoCluster() : car_state.getVEgo();
speed = cs_alive ? std::max<float>(0.0, v_ego) : 0.0;
speed *= is_metric ? MS_TO_KPH : MS_TO_MPH;
latActive = car_control.getLatActive();
steerOverride = car_state.getSteeringPressed();
devUiInfo = s.scene.dev_ui_info;
speedUnit = is_metric ? tr("km/h") : tr("mph");
lead_d_rel = radar_state.getLeadOne().getDRel();
lead_v_rel = radar_state.getLeadOne().getVRel();
lead_status = radar_state.getLeadOne().getStatus();
steerControlType = car_params.getSteerControlType();
actuators = car_control.getActuators();
torqueLateral = steerControlType == cereal::CarParams::SteerControlType::TORQUE;
angleSteers = car_state.getSteeringAngleDeg();
desiredCurvature = cs.getDesiredCurvature();
curvature = cs.getCurvature();
roll = sm["liveParameters"].getLiveParameters().getRoll();
memoryUsagePercent = sm["deviceState"].getDeviceState().getMemoryUsagePercent();
gpsAccuracy = is_gps_location_external ? gpsLocation.getHorizontalAccuracy() : 1.0; // External reports accuracy, internal does not.
altitude = gpsLocation.getAltitude();
vEgo = car_state.getVEgo();
aEgo = car_state.getAEgo();
steeringTorqueEps = car_state.getSteeringTorqueEps();
bearingAccuracyDeg = gpsLocation.getBearingAccuracyDeg();
bearingDeg = gpsLocation.getBearingDeg();
torquedUseParams = ltp.getUseParams();
latAccelFactorFiltered = ltp.getLatAccelFactorFiltered();
frictionCoefficientFiltered = ltp.getFrictionCoefficientFiltered();
liveValid = ltp.getLiveValid();
} }
void HudRendererSP::draw(QPainter &p, const QRect &surface_rect) { void HudRendererSP::draw(QPainter &p, const QRect &surface_rect) {
HudRenderer::draw(p, surface_rect); HudRenderer::draw(p, surface_rect);
if (!reversing) {
// Bottom Dev UI
if (devUiInfo == 2) {
QRect rect_bottom(surface_rect.left(), surface_rect.bottom() - 60, surface_rect.width(), 61);
p.setPen(Qt::NoPen);
p.setBrush(QColor(0, 0, 0, 100));
p.drawRect(rect_bottom);
drawBottomDevUI(p, rect_bottom.left(), rect_bottom.center().y());
}
// Right Dev UI
if (devUiInfo != 0) {
QRect rect_right(surface_rect.right() - (UI_BORDER_SIZE * 2), UI_BORDER_SIZE * 1.5, 184, 170);
drawRightDevUI(p, surface_rect.right() - 184 - UI_BORDER_SIZE * 2, UI_BORDER_SIZE * 2 + rect_right.height());
}
}
}
void HudRendererSP::drawText(QPainter &p, int x, int y, const QString &text, QColor color) {
QRect real_rect = p.fontMetrics().boundingRect(text);
real_rect.moveCenter({x, y - real_rect.height() / 2});
p.setPen(color);
p.drawText(real_rect.x(), real_rect.bottom(), text);
}
int HudRendererSP::drawRightDevUIElement(QPainter &p, int x, int y, const QString &value, const QString &label, const QString &units, QColor &color) {
p.setFont(InterFont(28, QFont::Bold));
x += 92;
y += 80;
drawText(p, x, y, label);
p.setFont(InterFont(30 * 2, QFont::Bold));
y += 65;
drawText(p, x, y, value, color);
p.setFont(InterFont(28, QFont::Bold));
if (units.length() > 0) {
p.save();
x += 120;
y -= 25;
p.translate(x, y);
p.rotate(-90);
drawText(p, 0, 0, units);
p.restore();
}
return 130;
}
void HudRendererSP::drawRightDevUI(QPainter &p, int x, int y) {
int rh = 5;
int ry = y;
UiElement dRelElement = DeveloperUi::getDRel(lead_status, lead_d_rel);
rh += drawRightDevUIElement(p, x, ry, dRelElement.value, dRelElement.label, dRelElement.units, dRelElement.color);
ry = y + rh;
UiElement vRelElement = DeveloperUi::getVRel(lead_status, lead_v_rel, is_metric, speedUnit);
rh += drawRightDevUIElement(p, x, ry, vRelElement.value, vRelElement.label, vRelElement.units, vRelElement.color);
ry = y + rh;
UiElement steeringAngleDegElement = DeveloperUi::getSteeringAngleDeg(angleSteers, latActive, steerOverride);
rh += drawRightDevUIElement(p, x, ry, steeringAngleDegElement.value, steeringAngleDegElement.label, steeringAngleDegElement.units, steeringAngleDegElement.color);
ry = y + rh;
UiElement actuatorsOutputLateralElement = DeveloperUi::getActuatorsOutputLateral(steerControlType, actuators, desiredCurvature, vEgo, roll, latActive, steerOverride);
rh += drawRightDevUIElement(p, x, ry, actuatorsOutputLateralElement.value, actuatorsOutputLateralElement.label, actuatorsOutputLateralElement.units, actuatorsOutputLateralElement.color);
ry = y + rh;
UiElement actualLateralAccelElement = DeveloperUi::getActualLateralAccel(curvature, vEgo, roll, latActive, steerOverride);
rh += drawRightDevUIElement(p, x, ry, actualLateralAccelElement.value, actualLateralAccelElement.label, actualLateralAccelElement.units, actualLateralAccelElement.color);
}
int HudRendererSP::drawBottomDevUIElement(QPainter &p, int x, int y, const QString &value, const QString &label, const QString &units, QColor &color) {
p.setFont(InterFont(38, QFont::Bold));
QFontMetrics fm(p.font());
QRect init_rect = fm.boundingRect(label + " ");
QRect real_rect = fm.boundingRect(init_rect, 0, label + " ");
real_rect.moveCenter({x, y});
QRect init_rect2 = fm.boundingRect(value);
QRect real_rect2 = fm.boundingRect(init_rect2, 0, value);
real_rect2.moveTop(real_rect.top());
real_rect2.moveLeft(real_rect.right() + 10);
QRect init_rect3 = fm.boundingRect(units);
QRect real_rect3 = fm.boundingRect(init_rect3, 0, units);
real_rect3.moveTop(real_rect.top());
real_rect3.moveLeft(real_rect2.right() + 10);
p.setPen(Qt::white);
p.drawText(real_rect, Qt::AlignLeft | Qt::AlignVCenter, label);
p.setPen(color);
p.drawText(real_rect2, Qt::AlignRight | Qt::AlignVCenter, value);
p.drawText(real_rect3, Qt::AlignLeft | Qt::AlignVCenter, units);
return 430;
}
void HudRendererSP::drawBottomDevUI(QPainter &p, int x, int y) {
int rw = 90;
UiElement aEgoElement = DeveloperUi::getAEgo(aEgo);
rw += drawBottomDevUIElement(p, rw, y, aEgoElement.value, aEgoElement.label, aEgoElement.units, aEgoElement.color);
UiElement vEgoLeadElement = DeveloperUi::getVEgoLead(lead_status, lead_v_rel, vEgo, is_metric, speedUnit);
rw += drawBottomDevUIElement(p, rw, y, vEgoLeadElement.value, vEgoLeadElement.label, vEgoLeadElement.units, vEgoLeadElement.color);
if (torqueLateral && torquedUseParams) {
UiElement frictionCoefficientFilteredElement = DeveloperUi::getFrictionCoefficientFiltered(frictionCoefficientFiltered, liveValid);
rw += drawBottomDevUIElement(p, rw, y, frictionCoefficientFilteredElement.value, frictionCoefficientFilteredElement.label, frictionCoefficientFilteredElement.units, frictionCoefficientFilteredElement.color);
UiElement latAccelFactorFilteredElement = DeveloperUi::getLatAccelFactorFiltered(latAccelFactorFiltered, liveValid);
rw += drawBottomDevUIElement(p, rw, y, latAccelFactorFilteredElement.value, latAccelFactorFilteredElement.label, latAccelFactorFilteredElement.units, latAccelFactorFilteredElement.color);
} else {
UiElement steeringTorqueEpsElement = DeveloperUi::getSteeringTorqueEps(steeringTorqueEps);
rw += drawBottomDevUIElement(p, rw, y, steeringTorqueEpsElement.value, steeringTorqueEpsElement.label, steeringTorqueEpsElement.units, steeringTorqueEpsElement.color);
UiElement bearingDegElement = DeveloperUi::getBearingDeg(bearingAccuracyDeg, bearingDeg);
rw += drawBottomDevUIElement(p, rw, y, bearingDegElement.value, bearingDegElement.label, bearingDegElement.units, bearingDegElement.color);
}
UiElement altitudeElement = DeveloperUi::getAltitude(gpsAccuracy, altitude);
rw += drawBottomDevUIElement(p, rw, y, altitudeElement.value, altitudeElement.label, altitudeElement.units, altitudeElement.color);
} }
+37 -2
View File
@@ -7,9 +7,8 @@
#pragma once #pragma once
#include <QPainter>
#include "selfdrive/ui/qt/onroad/hud.h" #include "selfdrive/ui/qt/onroad/hud.h"
#include "selfdrive/ui/sunnypilot/qt/onroad/developer_ui/developer_ui.h"
class HudRendererSP : public HudRenderer { class HudRendererSP : public HudRenderer {
Q_OBJECT Q_OBJECT
@@ -18,4 +17,40 @@ public:
HudRendererSP(); HudRendererSP();
void updateState(const UIState &s) override; void updateState(const UIState &s) override;
void draw(QPainter &p, const QRect &surface_rect) override; void draw(QPainter &p, const QRect &surface_rect) override;
private:
Params params;
void drawText(QPainter &p, int x, int y, const QString &text, QColor color = Qt::white);
void drawRightDevUI(QPainter &p, int x, int y);
int drawRightDevUIElement(QPainter &p, int x, int y, const QString &value, const QString &label, const QString &units, QColor &color);
int drawBottomDevUIElement(QPainter &p, int x, int y, const QString &value, const QString &label, const QString &units, QColor &color);
void drawBottomDevUI(QPainter &p, int x, int y);
bool lead_status;
float lead_d_rel;
float lead_v_rel;
bool torqueLateral;
float angleSteers;
float desiredCurvature;
float curvature;
float roll;
int memoryUsagePercent;
int devUiInfo;
float gpsAccuracy;
float altitude;
float vEgo;
float aEgo;
float steeringTorqueEps;
float bearingAccuracyDeg;
float bearingDeg;
bool torquedUseParams;
float latAccelFactorFiltered;
float frictionCoefficientFiltered;
bool liveValid;
QString speedUnit;
bool latActive;
bool steerOverride;
bool reversing;
cereal::CarParams::SteerControlType steerControlType;
cereal::CarControl::Actuators::Reader actuators;
}; };
+15 -1
View File
@@ -18,13 +18,22 @@ UIStateSP::UIStateSP(QObject *parent) : UIState(parent) {
"modelV2", "controlsState", "liveCalibration", "radarState", "deviceState", "modelV2", "controlsState", "liveCalibration", "radarState", "deviceState",
"pandaStates", "carParams", "driverMonitoringState", "carState", "driverStateV2", "pandaStates", "carParams", "driverMonitoringState", "carState", "driverStateV2",
"wideRoadCameraState", "managerState", "selfdriveState", "longitudinalPlan", "wideRoadCameraState", "managerState", "selfdriveState", "longitudinalPlan",
"modelManagerSP", "selfdriveStateSP", "longitudinalPlanSP", "backupManagerSP" "modelManagerSP", "selfdriveStateSP", "longitudinalPlanSP", "backupManagerSP",
"carControl", "gpsLocationExternal", "gpsLocation", "liveTorqueParameters",
"carStateSP", "liveParameters"
}); });
// update timer // update timer
timer = new QTimer(this); timer = new QTimer(this);
QObject::connect(timer, &QTimer::timeout, this, &UIStateSP::update); QObject::connect(timer, &QTimer::timeout, this, &UIStateSP::update);
timer->start(1000 / UI_FREQ); timer->start(1000 / UI_FREQ);
// Param watcher for UIScene param updates
param_watcher = new ParamWatcher(this);
connect(param_watcher, &ParamWatcher::paramChanged, [=](const QString &param_name, const QString &param_value) {
ui_update_params_sp(this);
});
param_watcher->addParam("DevUIInfo");
} }
// This method overrides completely the update method from the parent class intentionally. // This method overrides completely the update method from the parent class intentionally.
@@ -39,6 +48,11 @@ void UIStateSP::update() {
emit uiUpdate(*this); emit uiUpdate(*this);
} }
void ui_update_params_sp(UIStateSP *s) {
auto params = Params();
s->scene.dev_ui_info = std::atoi(params.get("DevUIInfo").c_str());
}
DeviceSP::DeviceSP(QObject *parent) : Device(parent) { DeviceSP::DeviceSP(QObject *parent) : Device(parent) {
QObject::connect(uiStateSP(), &UIStateSP::uiUpdate, this, &DeviceSP::update); QObject::connect(uiStateSP(), &UIStateSP::uiUpdate, this, &DeviceSP::update);
QObject::connect(this, &Device::displayPowerChanged, this, &DeviceSP::handleDisplayPowerChanged); QObject::connect(this, &Device::displayPowerChanged, this, &DeviceSP::handleDisplayPowerChanged);
+4
View File
@@ -13,6 +13,7 @@
#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/models/role_model.h" #include "selfdrive/ui/sunnypilot/qt/network/sunnylink/models/role_model.h"
#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/models/sponsor_role_model.h" #include "selfdrive/ui/sunnypilot/qt/network/sunnylink/models/sponsor_role_model.h"
#include "selfdrive/ui/ui.h" #include "selfdrive/ui/ui.h"
#include "selfdrive/ui/qt/util.h"
class UIStateSP : public UIState { class UIStateSP : public UIState {
Q_OBJECT Q_OBJECT
@@ -73,6 +74,7 @@ private slots:
private: private:
std::vector<RoleModel> sunnylinkRoles = {}; std::vector<RoleModel> sunnylinkRoles = {};
std::vector<UserModel> sunnylinkUsers = {}; std::vector<UserModel> sunnylinkUsers = {};
ParamWatcher *param_watcher;
}; };
UIStateSP *uiStateSP(); UIStateSP *uiStateSP();
@@ -92,3 +94,5 @@ private:
DeviceSP *deviceSP(); DeviceSP *deviceSP();
inline DeviceSP *device() { return deviceSP(); } inline DeviceSP *device() { return deviceSP(); }
void ui_update_params_sp(UIStateSP *s);
+12
View File
@@ -0,0 +1,12 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
typedef struct UISceneSP : UIScene {
int dev_ui_info = 0;
} UISceneSP;
+5
View File
@@ -66,6 +66,11 @@ typedef struct UIScene {
uint64_t started_frame; uint64_t started_frame;
} UIScene; } UIScene;
#ifdef SUNNYPILOT
#include "sunnypilot/ui_scene.h"
#define UIScene UISceneSP
#endif
class UIState : public QObject { class UIState : public QObject {
Q_OBJECT Q_OBJECT
@@ -73,7 +73,7 @@ class ControlsExt:
# MADS state # MADS state
CC_SP.mads = sm['selfdriveStateSP'].mads CC_SP.mads = sm['selfdriveStateSP'].mads
CC_SP.params = self.param_store.publish() CC_SP.params = self.param_store.param_list
return CC_SP return CC_SP
@@ -4,39 +4,41 @@ Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License. This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details. See the LICENSE.md file in the root directory for more details.
""" """
import capnp
from cereal import custom from cereal import custom
from opendbc.car import structs from opendbc.car import structs
from openpilot.common.params import Params from openpilot.common.params import Params
from sunnypilot.sunnylink.utils import get_param_as_byte
class ParamStore: class ParamStore:
keys: list[str] keys: list[str]
values: dict[str, str] _params: dict[str, custom.CarControlSP.Param]
def __init__(self, CP: structs.CarParams): def __init__(self, CP: structs.CarParams):
universal_params: list[str] = [] universal_params: list[str] = []
brand_params: list[str] = [] brand_params: list[str] = []
self.keys = universal_params + brand_params self.keys = universal_params + brand_params
self.values = {} self._params = {}
self.cached_params_list: list[capnp.lib.capnp._DynamicStructBuilder] | None = None
self.frame = 0 self.frame = 0
def update(self, params: Params) -> None: def update(self, params: Params) -> None:
if self.frame % 300 == 0:
old_values = dict(self.values)
self.values = {k: params.get(k) or "0" for k in self.keys}
if old_values != self.values:
self.cached_params_list = None
self.frame += 1 self.frame += 1
if self.frame % 300 != 0:
return
def publish(self) -> list[capnp.lib.capnp._DynamicStructBuilder]: for key in self.keys:
if self.cached_params_list is None: param_type = params.get_type(key).name.lower() # Using string instead of number because its "loose" dependency, and could change by OP at anytime.
# TODO-SP: Why are we doing a list instead of a dictionary here?
self.cached_params_list = [custom.CarControlSP.Param(key=k, value=self.values[k]) for k in self.keys] # Over engineering opportunity: It's possible this conversion is slow, we may check the value as params returns it for cache purposes. Not today.
return self.cached_params_list param_value = get_param_as_byte(key, params)
if (existing_param := self._params.get(key)) is not None and existing_param.value == param_value:
continue
self._params[key] = custom.CarControlSP.Param(key=key, value=param_value, type=param_type)
@property
def param_list(self) -> list[custom.CarControlSP.Param]:
return [v for k,v in self._params.items()]
+12 -14
View File
@@ -23,7 +23,7 @@ from websocket import (ABNF, WebSocket, WebSocketException, WebSocketTimeoutExce
import cereal.messaging as messaging import cereal.messaging as messaging
from sunnypilot.sunnylink.api import SunnylinkApi from sunnypilot.sunnylink.api import SunnylinkApi
from sunnypilot.sunnylink.utils import sunnylink_need_register, sunnylink_ready, get_param_as_byte from sunnypilot.sunnylink.utils import sunnylink_need_register, sunnylink_ready, get_param_as_byte, save_param_from_base64_encoded_string
SUNNYLINK_ATHENA_HOST = os.getenv('SUNNYLINK_ATHENA_HOST', 'wss://ws.stg.api.sunnypilot.ai') SUNNYLINK_ATHENA_HOST = os.getenv('SUNNYLINK_ATHENA_HOST', 'wss://ws.stg.api.sunnypilot.ai')
HANDLER_THREADS = int(os.getenv('HANDLER_THREADS', "4")) HANDLER_THREADS = int(os.getenv('HANDLER_THREADS', "4"))
@@ -184,14 +184,18 @@ def getParams(params_keys: list[str], compression: bool = False) -> str | dict[s
try: try:
param_keys_validated = [key for key in params_keys if key in getParamsAllKeys()] param_keys_validated = [key for key in params_keys if key in getParamsAllKeys()]
params_dict: dict[str, list[dict[str, str | bool | int ]]] = {"params": [ params_dict: dict[str, list[dict[str, str | bool | int]]] = {"params": []}
{ for key in param_keys_validated:
value = get_param_as_byte(key)
if value is None:
continue
params_dict["params"].append({
"key": key, "key": key,
"value": base64.b64encode(gzip.compress(get_param_as_byte(key)) if compression else get_param_as_byte(key)).decode('utf-8'), "value": base64.b64encode(gzip.compress(value) if compression else value).decode('utf-8'),
"type": int(params.get_type(key).value), "type": int(params.get_type(key).value),
"is_compressed": compression "is_compressed": compression
} for key in param_keys_validated })
]}
response = {str(param.get('key')): str(param.get('value')) for param in params_dict.get("params", [])} response = {str(param.get('key')): str(param.get('value')) for param in params_dict.get("params", [])}
response |= {"params": json.dumps(params_dict.get("params", []))} # Upcoming for settings v1 response |= {"params": json.dumps(params_dict.get("params", []))} # Upcoming for settings v1
@@ -204,15 +208,9 @@ def getParams(params_keys: list[str], compression: bool = False) -> str | dict[s
@dispatcher.add_method @dispatcher.add_method
def saveParams(params_to_update: dict[str, str], compression: bool = False) -> None: def saveParams(params_to_update: dict[str, str], compression: bool = False) -> None:
params = Params() for key, value in params_to_update.items():
params_dict = {key: base64.b64decode(value) for key, value in params_to_update.items()}
if compression:
params_dict = {key: gzip.decompress(value) for key, value in params_dict.items()}
for key, value in params_dict.items():
try: try:
params.put(key, value) save_param_from_base64_encoded_string(key, value, compression)
except Exception as e: except Exception as e:
cloudlog.error(f"sunnylinkd.saveParams.exception {e}") cloudlog.error(f"sunnylinkd.saveParams.exception {e}")
+5 -24
View File
@@ -12,7 +12,7 @@ from enum import Enum
from typing import Any from typing import Any
from openpilot.common.git import get_branch from openpilot.common.git import get_branch
from openpilot.common.params import Params, ParamKeyType, ParamKeyFlag from openpilot.common.params import Params, ParamKeyFlag
from openpilot.common.realtime import Ratekeeper from openpilot.common.realtime import Ratekeeper
from openpilot.common.swaglog import cloudlog from openpilot.common.swaglog import cloudlog
from openpilot.system.version import get_version from openpilot.system.version import get_version
@@ -20,7 +20,7 @@ from openpilot.system.version import get_version
from cereal import messaging, custom from cereal import messaging, custom
from sunnypilot.sunnylink.api import SunnylinkApi from sunnypilot.sunnylink.api import SunnylinkApi
from sunnypilot.sunnylink.backups.utils import decrypt_compressed_data, encrypt_compress_data, SnakeCaseEncoder from sunnypilot.sunnylink.backups.utils import decrypt_compressed_data, encrypt_compress_data, SnakeCaseEncoder
from sunnypilot.sunnylink.utils import get_param_as_byte from sunnypilot.sunnylink.utils import get_param_as_byte, save_param_from_base64_encoded_string
class OperationType(Enum): class OperationType(Enum):
@@ -173,8 +173,7 @@ class BackupManagerSP:
self._update_progress(75.0, OperationType.RESTORE) self._update_progress(75.0, OperationType.RESTORE)
# Apply configuration # Apply configuration
all_values_encoded = self._get_metadata_value(backup_metadata, "all_values_encoded", "false") self._apply_config(config_data)
self._apply_config(config_data, str(all_values_encoded).lower() == "true")
self.restore_status = custom.BackupManagerSP.Status.completed self.restore_status = custom.BackupManagerSP.Status.completed
self._update_progress(100.0, OperationType.RESTORE) self._update_progress(100.0, OperationType.RESTORE)
@@ -187,7 +186,7 @@ class BackupManagerSP:
self._report_status() self._report_status()
return False return False
def _apply_config(self, config_data: dict[str, str], all_values_encoded: bool = False) -> None: def _apply_config(self, config_data: dict[str, str]) -> None:
"""Applies configuration data from a backup, but only for parameters marked as backupable.""" """Applies configuration data from a backup, but only for parameters marked as backupable."""
backupable_params = [k.decode('utf-8') for k in self.params.all_keys(ParamKeyFlag.BACKUP)] backupable_params = [k.decode('utf-8') for k in self.params.all_keys(ParamKeyFlag.BACKUP)]
backupable_set_lower = {p.lower() for p in backupable_params} backupable_set_lower = {p.lower() for p in backupable_params}
@@ -199,26 +198,8 @@ class BackupManagerSP:
if param.lower() in backupable_set_lower: if param.lower() in backupable_set_lower:
# Find real param name (with correct casing) # Find real param name (with correct casing)
real_param = next(p for p in backupable_params if p.lower() == param.lower()) real_param = next(p for p in backupable_params if p.lower() == param.lower())
param_type = self.params.get_type(real_param)
try: try:
value = base64.b64decode(encoded_value) if all_values_encoded else encoded_value save_param_from_base64_encoded_string(real_param, encoded_value)
if param_type != ParamKeyType.BYTES:
value = value.decode('utf-8') # type: ignore
if param_type == ParamKeyType.STRING:
value = value
elif param_type == ParamKeyType.BOOL:
value = value.lower() in ('true', '1', 'yes') # type: ignore
elif param_type == ParamKeyType.INT:
value = int(value) # type: ignore
elif param_type == ParamKeyType.FLOAT:
value = float(value) # type: ignore
elif param_type == ParamKeyType.TIME:
value = str(value)
elif param_type == ParamKeyType.JSON:
value = json.loads(value)
self.params.put(real_param, value)
restored_count += 1 restored_count += 1
except Exception as e: except Exception as e:
cloudlog.error(f"Failed to restore param {param}: {str(e)}") cloudlog.error(f"Failed to restore param {param}: {str(e)}")
+49 -3
View File
@@ -1,3 +1,5 @@
import base64
import gzip
import json import json
from sunnypilot.sunnylink.api import SunnylinkApi, UNREGISTERED_SUNNYLINK_DONGLE_ID from sunnypilot.sunnylink.api import SunnylinkApi, UNREGISTERED_SUNNYLINK_DONGLE_ID
from openpilot.common.params import Params, ParamKeyType from openpilot.common.params import Params, ParamKeyType
@@ -58,13 +60,57 @@ def get_api_token():
print(f"API Token: {token}") print(f"API Token: {token}")
def get_param_as_byte(param_name: str) -> bytes: def get_param_as_byte(param_name: str, params=None) -> bytes | None:
params = Params() """Get a parameter as bytes. Returns None if the parameter does not exist."""
params = params or Params() # Use existing Params instance if provided
param = params.get(param_name) param = params.get(param_name)
param_type = params.get_type(param_name) if param is None:
return None
param_type = params.get_type(param_name)
if param_type == ParamKeyType.BYTES: if param_type == ParamKeyType.BYTES:
return bytes(param) return bytes(param)
elif param_type == ParamKeyType.JSON: elif param_type == ParamKeyType.JSON:
return json.dumps(param).encode('utf-8') return json.dumps(param).encode('utf-8')
return str(param).encode('utf-8') return str(param).encode('utf-8')
def save_param_from_base64_encoded_string(param_name: str, base64_encoded_data: str, is_compressed=False) -> None:
"""Save a parameter from bytes. Overwrites the parameter if it already exists."""
params = Params()
# Find real param name (with correct casing)
param_type = params.get_type(param_name)
value = base64.b64decode(base64_encoded_data)
if is_compressed:
value = gzip.decompress(value)
# We convert to string anything that isn't bytes first. We later transform further.
param_value = _convert_param_to_type(value, param_type)
params.put(param_name, param_value)
def _convert_param_to_type(value: bytes, param_type: ParamKeyType) -> bytes | str | int | float | bool | dict | None:
"""
Convert a byte value to the specified param type. Used internally when getting a Param to convert it to the right type.
If this method looks familiar, it's because on SP we have a similar one in openpilot/sunnypilot/car/__init__.py.
"""
# We convert to string anything that isn't bytes first. We later transform further.
if param_type != ParamKeyType.BYTES:
value = value.decode('utf-8') # type: ignore
if param_type == ParamKeyType.STRING:
value = value
elif param_type == ParamKeyType.BOOL:
value = value.lower() in ('true', '1', 'yes') # type: ignore
elif param_type == ParamKeyType.INT:
value = int(value) # type: ignore
elif param_type == ParamKeyType.FLOAT:
value = float(value) # type: ignore
elif param_type == ParamKeyType.TIME:
value = str(value) # type: ignore
elif param_type == ParamKeyType.JSON:
value = json.loads(value)
return value
@@ -9,3 +9,5 @@ export VECLIB_MAXIMUM_THREADS=1
if [ -z "$AGNOS_VERSION" ]; then if [ -z "$AGNOS_VERSION" ]; then
export AGNOS_VERSION="12.8" export AGNOS_VERSION="12.8"
fi fi
export STAGING_ROOT="/data/safe_staging"
+1 -1
View File
@@ -331,7 +331,7 @@ def hardware_thread(end_event, hw_queue) -> None:
# - TIZI, or # - TIZI, or
# - TICI and channel_type is "tici" # - TICI and channel_type is "tici"
build_metadata = get_build_metadata() build_metadata = get_build_metadata()
is_unsupported_combo = TICI and build_metadata.channel_type != "tici" is_unsupported_combo = TICI and HARDWARE.get_device_type() == "tici" and build_metadata.channel_type != "tici"
startup_conditions["not_tici"] = not is_unsupported_combo startup_conditions["not_tici"] = not is_unsupported_combo
onroad_conditions["not_tici"] = not is_unsupported_combo onroad_conditions["not_tici"] = not is_unsupported_combo
set_offroad_alert("Offroad_TiciSupport", is_unsupported_combo, extra_text=build_metadata.channel) set_offroad_alert("Offroad_TiciSupport", is_unsupported_combo, extra_text=build_metadata.channel)
+1 -1
View File
@@ -11,7 +11,7 @@ from openpilot.common.swaglog import cloudlog
from openpilot.common.git import get_commit, get_origin, get_branch, get_short_branch, get_commit_date from openpilot.common.git import get_commit, get_origin, get_branch, get_short_branch, get_commit_date
RELEASE_SP_BRANCHES = ['release-c3'] RELEASE_SP_BRANCHES = ['release-c3']
TESTED_SP_BRANCHES = ['staging-c3', 'staging-c3-new'] TESTED_SP_BRANCHES = ['staging-c3', 'staging-c3-new', 'staging-tici']
MASTER_SP_BRANCHES = ['master'] MASTER_SP_BRANCHES = ['master']
RELEASE_BRANCHES = ['release3-staging', 'release3', 'release-tici', 'nightly'] + RELEASE_SP_BRANCHES RELEASE_BRANCHES = ['release3-staging', 'release3', 'release-tici', 'nightly'] + RELEASE_SP_BRANCHES
TESTED_BRANCHES = RELEASE_BRANCHES + ['devel', 'devel-staging', 'nightly-dev'] + TESTED_SP_BRANCHES TESTED_BRANCHES = RELEASE_BRANCHES + ['devel', 'devel-staging', 'nightly-dev'] + TESTED_SP_BRANCHES
Generated
+1 -1
View File
@@ -912,7 +912,7 @@ dependencies = [
{ name = "yapf" }, { name = "yapf" },
] ]
wheels = [ wheels = [
{ url = "https://github.com/commaai/metadrive/releases/download/MetaDrive-minimal-0.4.2.4/metadrive_simulator-0.4.2.4-py3-none-any.whl", hash = "sha256:fbf0ea9be67e65cd45d38ff930e3d49f705dd76c9ddbd1e1482e3f87b61efcef" }, { url = "https://github.com/commaai/metadrive/releases/download/MetaDrive-minimal-0.4.2.4/metadrive_simulator-0.4.2.4-py3-none-any.whl", hash = "sha256:d0afaf3b005e35e14b929d5491d2d5b64562d0c1cd5093ba969fb63908670dd4" },
] ]
[package.metadata] [package.metadata]