mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-09-15 02:13:43 +08:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7ccef2dd04 | |||
| 44d7a81806 | |||
| 7f69eca029 | |||
| ff4d1923f0 | |||
| 3a91ae08a9 |
@@ -29,11 +29,11 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
target: /^(?!master-tici$).*/
|
target: /^(?!master$).*/
|
||||||
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-tici` branch"
|
already-exists-comment: "Your PR should be made against the `master` branch"
|
||||||
|
|
||||||
update-pr-labels:
|
update-pr-labels:
|
||||||
name: Update fork's PR Labels
|
name: Update fork's PR Labels
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
BASE_IMAGE: sunnypilot-tici-base
|
BASE_IMAGE: sunnypilot-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
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ name: cereal validation
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master-tici
|
- master
|
||||||
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-tici' && 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.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -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-tici
|
uses: sunnypilot/sunnypilot/.github/workflows/ci_weekly_run.yaml@master
|
||||||
with:
|
with:
|
||||||
run_number: ${{ matrix.run_number }}
|
run_number: ${{ matrix.run_number }}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,6 @@ concurrency:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
selfdrive_tests:
|
selfdrive_tests:
|
||||||
uses: sunnypilot/sunnypilot/.github/workflows/selfdrive_tests.yaml@master-tici
|
uses: sunnypilot/sunnypilot/.github/workflows/selfdrive_tests.yaml@master
|
||||||
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-tici'
|
if: github.ref == 'refs/heads/master'
|
||||||
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 }}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ name: docs
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master-tici
|
- master
|
||||||
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-tici' && 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' && 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-tici' && github.repository == 'sunnypilot/sunnypilot'
|
if: github.ref == 'refs/heads/master' && 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-tici' && github.repository == 'sunnypilot/sunnypilot'
|
if: github.ref == 'refs/heads/master' && github.repository == 'sunnypilot/sunnypilot'
|
||||||
run: |
|
run: |
|
||||||
set -x
|
set -x
|
||||||
|
|
||||||
|
|||||||
@@ -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-tici
|
ref: master
|
||||||
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 __nightly).*).)*$
|
check-regexp: ^((?!.*(build master-ci).*).)*$
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ 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:
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
ImageOS: ubuntu24
|
ImageOS: ubuntu24
|
||||||
container:
|
container:
|
||||||
image: ghcr.io/sunnypilot/sunnypilot-tici-base:latest
|
image: ghcr.io/sunnypilot/sunnypilot-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-tici
|
ref: master
|
||||||
wait-interval: 30
|
wait-interval: 30
|
||||||
running-workflow-name: 'build __nightly'
|
running-workflow-name: 'build __nightly'
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
BASE_IMAGE: sunnypilot-tici-base
|
BASE_IMAGE: sunnypilot-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-tici"
|
base: "master"
|
||||||
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-tici-base:latest
|
image: ghcr.io/sunnypilot/sunnypilot-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-tici
|
base: master
|
||||||
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
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ name: selfdrive
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master-tici
|
- master
|
||||||
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-tici' && 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' && 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-tici-base
|
BASE_IMAGE: sunnypilot-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-tici" ]; then
|
if [ "${{ github.ref }}" != "refs/heads/master" ]; then
|
||||||
git fetch origin master-tici:refs/remotes/origin/master-tici
|
git fetch origin master:refs/remotes/origin/master
|
||||||
|
|
||||||
SUBMODULE_PATHS=$(git diff origin/master-tici HEAD --name-only | grep -E '^[^/]+$' | while read path; do
|
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
|
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-tici' && github.event_name != 'pull_request' && github.repository == 'sunnypilot/sunnypilot'
|
if: github.ref == 'refs/heads/master' && 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-tici'
|
if: github.ref == 'refs/heads/master'
|
||||||
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-tici'
|
if: github.ref == 'refs/heads/master'
|
||||||
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-tici' && 'master-tici' || github.event.number }}
|
name: report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
||||||
path: selfdrive/ui/tests/test_ui/report_1/screenshots
|
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-tici'
|
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 (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-tici'
|
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 (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: 'master-tici' # vars.STAGING_C3_SOURCE_BRANCH could be used, set on repo settings.
|
STAGING_C3_SOURCE_BRANCH: ${{ vars.STAGING_C3_SOURCE_BRANCH || 'master' }} # vars are 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-tici ]
|
branches: [ master, master-dev-c3-new ]
|
||||||
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-tici"
|
DEFAULT_SOURCE_BRANCH: "master"
|
||||||
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-tici
|
- master
|
||||||
pull_request_target:
|
pull_request_target:
|
||||||
types: [ labeled ]
|
types: [ labeled ]
|
||||||
branches:
|
branches:
|
||||||
- 'master-tici'
|
- '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-tici'
|
default: 'master'
|
||||||
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,7 +40,11 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
reset-and-squash:
|
reset-and-squash:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: False
|
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 == '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:
|
||||||
|
|||||||
@@ -2,19 +2,19 @@ name: "ui preview"
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master-tici
|
- master
|
||||||
pull_request_target:
|
pull_request_target:
|
||||||
types: [assigned, opened, synchronize, reopened, edited]
|
types: [assigned, opened, synchronize, reopened, edited]
|
||||||
branches:
|
branches:
|
||||||
- 'master-tici'
|
- 'master'
|
||||||
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-tici' && 'master-tici' || github.event.number }}
|
REPORT_NAME: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
||||||
SHA: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master-tici' && github.sha || github.event.pull_request.head.sha }}
|
SHA: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.sha || github.event.pull_request.head.sha }}
|
||||||
BRANCH_NAME: "openpilot/pr-${{ github.event.number }}"
|
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-tici ui
|
- name: Getting master 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-tici ui
|
- name: Saving new master ui
|
||||||
if: github.ref == 'refs/heads/master-tici' && github.event_name == 'push'
|
if: github.ref == 'refs/heads/master' && 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-tici file exists
|
# Check if the master 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-tici
|
# This is a new file in PR UI that doesn't exist in master
|
||||||
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-tici <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}_master_ref.png\"> </td>"
|
DIFF="${DIFF} <td> master <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}_master_ref.png\"> </td>"
|
||||||
DIFF="${DIFF} <td> proposed <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}.png\"> </td>"
|
DIFF="${DIFF} <td> proposed <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}.png\"> </td>"
|
||||||
DIFF="${DIFF}</tr>"
|
DIFF="${DIFF}</tr>"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM ghcr.io/sunnypilot/sunnypilot-tici-base:latest
|
FROM ghcr.io/sunnypilot/sunnypilot-base:latest
|
||||||
|
|
||||||
ENV PYTHONUNBUFFERED=1
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
|||||||
@@ -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-tici` 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-c3-new` branch.
|
||||||
|
|
||||||
### If you want to use our newest branches (our rewrite)
|
### If you want to use our newest branches (our rewrite)
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
@@ -31,24 +31,25 @@ 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-tici.sunnypilot.ai```.
|
3. Input the installation URL per [Recommended Branches](#-recommended-branches). Example: ```https://staging-c3-new.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-tici`
|
4. Scroll to select the desired branch per Recommended Branches (see below). Example: `staging-c3-new`
|
||||||
|
|
||||||
|
|
||||||
| Branch | Installation URL |
|
| Branch | Installation URL |
|
||||||
|:---------------:|:---------------------------------------------:|
|
|:----------------:|:---------------------------------------------:|
|
||||||
| `staging-tici` | `https://staging-tici.sunnypilot.ai` |
|
| `staging-c3-new` | `https://staging-c3-new.sunnypilot.ai` |
|
||||||
| `custom-branch` | `https://install.sunnypilot.ai/{branch_name}` |
|
| `dev-c3-new` | `https://dev-c3-new.sunnypilot.ai` |
|
||||||
| `release-tici` | **Not yet available**. |
|
| `custom-branch` | `https://install.sunnypilot.ai/{branch_name}` |
|
||||||
|
| `release-c3-new` | **Not yet available**. |
|
||||||
|
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
> You can use sunnypilot/targetbranch as an install URL. Example: 'sunnypilot/staging-tici'.
|
> You can use sunnypilot/targetbranch as an install URL. Example: 'sunnypilot/staging-c3-new'.
|
||||||
|
|
||||||
> [!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.
|
||||||
|
|||||||
+1
-14
@@ -202,20 +202,7 @@ struct CarControlSP @0xa5cd762cd951a455 {
|
|||||||
|
|
||||||
struct Param {
|
struct Param {
|
||||||
key @0 :Text;
|
key @0 :Text;
|
||||||
type @2 :ParamType;
|
value @1 :Text;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -146,7 +146,6 @@ 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}},
|
||||||
|
|||||||
+1
-1
Submodule opendbc_repo updated: 1a1fd1af7f...42bbd450b9
@@ -31,25 +31,13 @@ while read hash submodule ref; do
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
# Check against master-tici for specific submodules, master for others
|
git -C $submodule fetch --depth 100 origin master
|
||||||
if [ "$submodule" = "opendbc_repo" ] || [ "$submodule" = "panda" ]; then
|
git -C $submodule branch -r --contains $hash | grep "origin/master"
|
||||||
git -C $submodule fetch --depth 100 origin master-tici
|
if [ "$?" -eq 0 ]; then
|
||||||
remote_hash=$(git -C $submodule rev-parse FETCH_HEAD)
|
echo "$submodule ok"
|
||||||
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
|
||||||
git -C $submodule fetch --depth 100 origin master
|
echo "$submodule: $hash is not on master"
|
||||||
git -C $submodule branch -r --contains $hash | grep "origin/master"
|
exit 1
|
||||||
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)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
if [ "$1" = "base" ]; then
|
if [ "$1" = "base" ]; then
|
||||||
export DOCKER_IMAGE=sunnypilot-tici-base
|
export DOCKER_IMAGE=sunnypilot-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-tici-prebuilt
|
export DOCKER_IMAGE=sunnypilot-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'"
|
||||||
|
|||||||
@@ -4,9 +4,6 @@
|
|||||||
#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);
|
||||||
@@ -76,12 +73,6 @@ 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,7 +12,6 @@
|
|||||||
#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,11 +73,6 @@ 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)];
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ void ModelRenderer::draw(QPainter &painter, const QRect &surface_rect) {
|
|||||||
drawLead(painter, lead_two, lead_vertices[1], surface_rect);
|
drawLead(painter, lead_two, lead_vertices[1], surface_rect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
drawLeadStatus(painter, surface_rect.height(), surface_rect.width());
|
|
||||||
|
|
||||||
painter.restore();
|
painter.restore();
|
||||||
}
|
}
|
||||||
@@ -175,172 +174,6 @@ QColor ModelRenderer::blendColors(const QColor &start, const QColor &end, float
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ModelRenderer::drawLeadStatus(QPainter &painter, int height, int width) {
|
|
||||||
auto *s = uiState();
|
|
||||||
auto &sm = *(s->sm);
|
|
||||||
|
|
||||||
if (!sm.alive("radarState")) return;
|
|
||||||
|
|
||||||
const auto &radar_state = sm["radarState"].getRadarState();
|
|
||||||
const auto &lead_one = radar_state.getLeadOne();
|
|
||||||
const auto &lead_two = radar_state.getLeadTwo();
|
|
||||||
|
|
||||||
// Check if we have any active leads
|
|
||||||
bool has_lead_one = lead_one.getStatus();
|
|
||||||
bool has_lead_two = lead_two.getStatus();
|
|
||||||
|
|
||||||
if (!has_lead_one && !has_lead_two) {
|
|
||||||
// Fade out status display
|
|
||||||
lead_status_alpha = std::max(0.0f, lead_status_alpha - 0.05f);
|
|
||||||
if (lead_status_alpha <= 0.0f) return;
|
|
||||||
} else {
|
|
||||||
// Fade in status display
|
|
||||||
lead_status_alpha = std::min(1.0f, lead_status_alpha + 0.1f);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw status for each lead vehicle under its chevron
|
|
||||||
if (true) {
|
|
||||||
drawLeadStatusAtPosition(painter, lead_one, lead_vertices[0], height, width, "L1");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (has_lead_two && std::abs(lead_one.getDRel() - lead_two.getDRel()) > 3.0) {
|
|
||||||
drawLeadStatusAtPosition(painter, lead_two, lead_vertices[1], height, width, "L2");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModelRenderer::drawLeadStatusAtPosition(QPainter &painter,
|
|
||||||
const cereal::RadarState::LeadData::Reader &lead_data,
|
|
||||||
const QPointF &chevron_pos,
|
|
||||||
int height, int width,
|
|
||||||
const QString &label) {
|
|
||||||
|
|
||||||
float d_rel = lead_data.getDRel();
|
|
||||||
float v_rel = lead_data.getVRel();
|
|
||||||
auto *s = uiState();
|
|
||||||
auto &sm = *(s->sm);
|
|
||||||
float v_ego = sm["carState"].getCarState().getVEgo();
|
|
||||||
|
|
||||||
int chevron_data = std::atoi(Params().get("ChevronInfo").c_str());
|
|
||||||
|
|
||||||
// Calculate chevron size (same logic as drawLead)
|
|
||||||
float sz = std::clamp((25 * 30) / (d_rel / 3 + 30), 15.0f, 30.0f) * 2.35;
|
|
||||||
|
|
||||||
QFont content_font = painter.font();
|
|
||||||
content_font.setPixelSize(35);
|
|
||||||
content_font.setBold(true);
|
|
||||||
painter.setFont(content_font);
|
|
||||||
|
|
||||||
QFontMetrics fm(content_font);
|
|
||||||
bool is_metric = s->scene.is_metric;
|
|
||||||
|
|
||||||
QStringList text_lines;
|
|
||||||
|
|
||||||
const int chevron_types = 3;
|
|
||||||
const int chevron_all = chevron_types + 1; // All metrics (value 4)
|
|
||||||
QStringList chevron_text[chevron_types];
|
|
||||||
int position;
|
|
||||||
float val;
|
|
||||||
|
|
||||||
// Distance display (chevron_data == 1 or all)
|
|
||||||
if (chevron_data == 1 || chevron_data == chevron_all) {
|
|
||||||
position = 0;
|
|
||||||
val = std::max(0.0f, d_rel);
|
|
||||||
QString distance_unit = is_metric ? "m" : "ft";
|
|
||||||
if (!is_metric) {
|
|
||||||
val *= 3.28084f; // Convert meters to feet
|
|
||||||
}
|
|
||||||
chevron_text[position].append(QString::number(val, 'f', 0) + " " + distance_unit);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Absolute velocity display (chevron_data == 2 or all)
|
|
||||||
if (chevron_data == 2 || chevron_data == chevron_all) {
|
|
||||||
position = (chevron_data == 2) ? 0 : 1;
|
|
||||||
val = std::max(0.0f, (v_rel + v_ego) * (is_metric ? static_cast<float>(MS_TO_KPH) : static_cast<float>(MS_TO_MPH)));
|
|
||||||
chevron_text[position].append(QString::number(val, 'f', 0) + " " + (is_metric ? "km/h" : "mph"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Time-to-contact display (chevron_data == 3 or all)
|
|
||||||
if (chevron_data == 3 || chevron_data == chevron_all) {
|
|
||||||
position = (chevron_data == 3) ? 0 : 2;
|
|
||||||
val = (d_rel > 0 && v_ego > 0) ? std::max(0.0f, d_rel / v_ego) : 0.0f;
|
|
||||||
QString ttc_str = (val > 0 && val < 200) ? QString::number(val, 'f', 1) + "s" : "---";
|
|
||||||
chevron_text[position].append(ttc_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect all non-empty text lines
|
|
||||||
for (int i = 0; i < chevron_types; ++i) {
|
|
||||||
if (!chevron_text[i].isEmpty()) {
|
|
||||||
text_lines.append(chevron_text[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no text to display, return early
|
|
||||||
if (text_lines.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Text box dimensions
|
|
||||||
float str_w = 150; // Width of text area
|
|
||||||
float str_h = 45; // Height per line
|
|
||||||
|
|
||||||
// Position text below chevron, centered horizontally
|
|
||||||
float text_x = chevron_pos.x() - str_w / 2;
|
|
||||||
float text_y = chevron_pos.y() + sz + 15;
|
|
||||||
|
|
||||||
// Clamp to screen bounds
|
|
||||||
text_x = std::clamp(text_x, 10.0f, (float)width - str_w - 10);
|
|
||||||
|
|
||||||
// Shadow offset
|
|
||||||
QPoint shadow_offset(2, 2);
|
|
||||||
|
|
||||||
// Draw each line of text with shadow
|
|
||||||
for (int i = 0; i < text_lines.size(); ++i) {
|
|
||||||
if (!text_lines[i].isEmpty()) {
|
|
||||||
QRect textRect(text_x, text_y + (i * str_h), str_w, str_h);
|
|
||||||
|
|
||||||
// Draw shadow
|
|
||||||
painter.setPen(QColor(0x0, 0x0, 0x0, (int)(200 * lead_status_alpha)));
|
|
||||||
painter.drawText(textRect.translated(shadow_offset.x(), shadow_offset.y()),
|
|
||||||
Qt::AlignBottom | Qt::AlignHCenter, text_lines[i]);
|
|
||||||
|
|
||||||
// Determine text color based on content and danger level
|
|
||||||
QColor text_color;
|
|
||||||
|
|
||||||
// Check if this is a distance line (contains 'm' or 'ft')
|
|
||||||
if (text_lines[i].contains("m") || text_lines[i].contains("ft")) {
|
|
||||||
if (d_rel < 20.0f) {
|
|
||||||
text_color = QColor(255, 80, 80, (int)(255 * lead_status_alpha)); // Red - danger
|
|
||||||
} else if (d_rel < 40.0f) {
|
|
||||||
text_color = QColor(255, 200, 80, (int)(255 * lead_status_alpha)); // Yellow - caution
|
|
||||||
} else {
|
|
||||||
text_color = QColor(80, 255, 120, (int)(255 * lead_status_alpha)); // Green - safe
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Enhanced color coding for time-to-contact
|
|
||||||
else if (text_lines[i].contains("s") && !text_lines[i].contains("---")) {
|
|
||||||
float ttc_val = text_lines[i].left(text_lines[i].length() - 1).toFloat();
|
|
||||||
if (ttc_val < 3.0f) {
|
|
||||||
text_color = QColor(255, 80, 80, (int)(255 * lead_status_alpha)); // Red - urgent
|
|
||||||
} else if (ttc_val < 6.0f) {
|
|
||||||
text_color = QColor(255, 200, 80, (int)(255 * lead_status_alpha)); // Yellow - caution
|
|
||||||
} else {
|
|
||||||
text_color = QColor(0xff, 0xff, 0xff, (int)(255 * lead_status_alpha)); // White - safe
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
text_color = QColor(0xff, 0xff, 0xff, (int)(255 * lead_status_alpha)); // White for other lines
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw main text
|
|
||||||
painter.setPen(text_color);
|
|
||||||
painter.drawText(textRect, Qt::AlignBottom | Qt::AlignHCenter, text_lines[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset pen
|
|
||||||
painter.setPen(Qt::NoPen);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModelRenderer::drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data,
|
void ModelRenderer::drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data,
|
||||||
const QPointF &vd, const QRect &surface_rect) {
|
const QPointF &vd, const QRect &surface_rect) {
|
||||||
const float speedBuff = 10.;
|
const float speedBuff = 10.;
|
||||||
|
|||||||
@@ -34,12 +34,6 @@ protected:
|
|||||||
bool mapToScreen(float in_x, float in_y, float in_z, QPointF *out);
|
bool mapToScreen(float in_x, float in_y, float in_z, QPointF *out);
|
||||||
void mapLineToPolygon(const cereal::XYZTData::Reader &line, float y_off, float z_off,
|
void mapLineToPolygon(const cereal::XYZTData::Reader &line, float y_off, float z_off,
|
||||||
QPolygonF *pvd, int max_idx, bool allow_invert = true);
|
QPolygonF *pvd, int max_idx, bool allow_invert = true);
|
||||||
void drawLeadStatus(QPainter &painter, int height, int width);
|
|
||||||
void drawLeadStatusAtPosition(QPainter &painter,
|
|
||||||
const cereal::RadarState::LeadData::Reader &lead_data,
|
|
||||||
const QPointF &chevron_pos,
|
|
||||||
int height, int width,
|
|
||||||
const QString &label);
|
|
||||||
void drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd, const QRect &surface_rect);
|
void drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd, const QRect &surface_rect);
|
||||||
void update_leads(const cereal::RadarState::Reader &radar_state, const cereal::XYZTData::Reader &line);
|
void update_leads(const cereal::RadarState::Reader &radar_state, const cereal::XYZTData::Reader &line);
|
||||||
virtual void update_model(const cereal::ModelDataV2::Reader &model, const cereal::RadarState::LeadData::Reader &lead);
|
virtual void update_model(const cereal::ModelDataV2::Reader &model, const cereal::RadarState::LeadData::Reader &lead);
|
||||||
@@ -65,8 +59,4 @@ protected:
|
|||||||
Eigen::Matrix3f car_space_transform = Eigen::Matrix3f::Zero();
|
Eigen::Matrix3f car_space_transform = Eigen::Matrix3f::Zero();
|
||||||
QRectF clip_region;
|
QRectF clip_region;
|
||||||
|
|
||||||
float lead_status_alpha = 0.0f;
|
|
||||||
QPointF lead_status_pos;
|
|
||||||
QString lead_status_text;
|
|
||||||
QColor lead_status_color;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ 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,15 +72,6 @@ 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);
|
||||||
|
|
||||||
@@ -99,7 +90,4 @@ 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,5 +28,4 @@ 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,8 +14,3 @@ 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,7 +15,4 @@ 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;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,227 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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);
|
|
||||||
};
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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) {}
|
|
||||||
};
|
|
||||||
@@ -7,202 +7,12 @@
|
|||||||
|
|
||||||
#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);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,9 @@
|
|||||||
|
|
||||||
#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
|
||||||
@@ -17,40 +18,4 @@ 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;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -86,5 +86,154 @@ void ModelRendererSP::drawPath(QPainter &painter, const cereal::ModelDataV2::Rea
|
|||||||
} else {
|
} else {
|
||||||
// Normal path rendering
|
// Normal path rendering
|
||||||
ModelRenderer::drawPath(painter, model, surface_rect.height());
|
ModelRenderer::drawPath(painter, model, surface_rect.height());
|
||||||
|
|
||||||
|
drawLeadStatus(painter, surface_rect.height(), surface_rect.width());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModelRendererSP::drawLeadStatus(QPainter &painter, int height, int width) {
|
||||||
|
auto *s = uiState();
|
||||||
|
auto &sm = *(s->sm);
|
||||||
|
|
||||||
|
bool longitudinal_control = sm["carParams"].getCarParams().getOpenpilotLongitudinalControl();
|
||||||
|
if (!longitudinal_control) {
|
||||||
|
lead_status_alpha = std::max(0.0f, lead_status_alpha - 0.05f);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sm.alive("radarState")) {
|
||||||
|
lead_status_alpha = std::max(0.0f, lead_status_alpha - 0.05f);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &radar_state = sm["radarState"].getRadarState();
|
||||||
|
const auto &lead_one = radar_state.getLeadOne();
|
||||||
|
const auto &lead_two = radar_state.getLeadTwo();
|
||||||
|
|
||||||
|
bool has_lead_one = lead_one.getStatus();
|
||||||
|
bool has_lead_two = lead_two.getStatus();
|
||||||
|
|
||||||
|
if (!has_lead_one && !has_lead_two) {
|
||||||
|
lead_status_alpha = std::max(0.0f, lead_status_alpha - 0.05f);
|
||||||
|
if (lead_status_alpha <= 0.0f) return;
|
||||||
|
} else {
|
||||||
|
lead_status_alpha = std::min(1.0f, lead_status_alpha + 0.1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_lead_one) {
|
||||||
|
drawLeadStatusAtPosition(painter, lead_one, lead_vertices[0], height, width, "L1");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_lead_two && std::abs(lead_one.getDRel() - lead_two.getDRel()) > 3.0) {
|
||||||
|
drawLeadStatusAtPosition(painter, lead_two, lead_vertices[1], height, width, "L2");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelRendererSP::drawLeadStatusAtPosition(QPainter &painter,
|
||||||
|
const cereal::RadarState::LeadData::Reader &lead_data,
|
||||||
|
const QPointF &chevron_pos,
|
||||||
|
int height, int width,
|
||||||
|
const QString &label) {
|
||||||
|
float d_rel = lead_data.getDRel();
|
||||||
|
float v_rel = lead_data.getVRel();
|
||||||
|
auto *s = uiState();
|
||||||
|
auto &sm = *(s->sm);
|
||||||
|
float v_ego = sm["carState"].getCarState().getVEgo();
|
||||||
|
|
||||||
|
int chevron_data = std::atoi(Params().get("ChevronInfo").c_str());
|
||||||
|
float sz = std::clamp((25 * 30) / (d_rel / 3 + 30), 15.0f, 30.0f) * 2.35;
|
||||||
|
|
||||||
|
QFont content_font = painter.font();
|
||||||
|
content_font.setPixelSize(42);
|
||||||
|
content_font.setBold(true);
|
||||||
|
painter.setFont(content_font);
|
||||||
|
|
||||||
|
bool is_metric = s->scene.is_metric;
|
||||||
|
QStringList text_lines;
|
||||||
|
|
||||||
|
const int chevron_all = 4;
|
||||||
|
QStringList chevron_text[3];
|
||||||
|
|
||||||
|
// Distance display
|
||||||
|
if (chevron_data == 1 || chevron_data == chevron_all) {
|
||||||
|
int pos = 0;
|
||||||
|
float val = std::max(0.0f, d_rel);
|
||||||
|
QString unit = is_metric ? "m" : "ft";
|
||||||
|
if (!is_metric) val *= 3.28084f;
|
||||||
|
chevron_text[pos].append(QString::number(val, 'f', 0) + " " + unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Speed display
|
||||||
|
if (chevron_data == 2 || chevron_data == chevron_all) {
|
||||||
|
int pos = (chevron_data == 2) ? 0 : 1;
|
||||||
|
float multiplier = is_metric ? static_cast<float>(MS_TO_KPH) : static_cast<float>(MS_TO_MPH);
|
||||||
|
float val = std::max(0.0f, (v_rel + v_ego) * multiplier);
|
||||||
|
QString unit = is_metric ? "km/h" : "mph";
|
||||||
|
chevron_text[pos].append(QString::number(val, 'f', 0) + " " + unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time to contact
|
||||||
|
if (chevron_data == 3 || chevron_data == chevron_all) {
|
||||||
|
int pos = (chevron_data == 3) ? 0 : 2;
|
||||||
|
float val = (d_rel > 0 && v_ego > 0) ? std::max(0.0f, d_rel / v_ego) : 0.0f;
|
||||||
|
QString ttc = (val > 0 && val < 200) ? QString::number(val, 'f', 1) + "s" : "---";
|
||||||
|
chevron_text[pos].append(ttc);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
if (!chevron_text[i].isEmpty()) {
|
||||||
|
text_lines.append(chevron_text[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text_lines.isEmpty()) return;
|
||||||
|
|
||||||
|
QFontMetrics fm(content_font);
|
||||||
|
float text_width = 120.0f;
|
||||||
|
for (const QString &line : text_lines) {
|
||||||
|
text_width = std::max(text_width, fm.horizontalAdvance(line) + 20.0f);
|
||||||
|
}
|
||||||
|
text_width = std::min(text_width, 250.0f);
|
||||||
|
|
||||||
|
float line_height = 45.0f;
|
||||||
|
float total_height = text_lines.size() * line_height;
|
||||||
|
float margin = 20.0f;
|
||||||
|
|
||||||
|
float text_y = chevron_pos.y() + sz + 15;
|
||||||
|
if (text_y + total_height > height - margin) {
|
||||||
|
text_y = chevron_pos.y() - sz - 15 - total_height;
|
||||||
|
text_y = std::max(margin, text_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
float text_x = chevron_pos.x() - text_width / 2;
|
||||||
|
text_x = std::clamp(text_x, margin, (float)width - text_width - margin);
|
||||||
|
|
||||||
|
QPoint shadow_offset(2, 2);
|
||||||
|
|
||||||
|
for (int i = 0; i < text_lines.size(); ++i) {
|
||||||
|
float y = text_y + (i * line_height);
|
||||||
|
if (y + line_height > height - margin) break;
|
||||||
|
|
||||||
|
QRect rect(text_x, y, text_width, line_height);
|
||||||
|
|
||||||
|
// Draw shadow
|
||||||
|
painter.setPen(QColor(0, 0, 0, (int)(200 * lead_status_alpha)));
|
||||||
|
painter.drawText(rect.translated(shadow_offset), Qt::AlignCenter, text_lines[i]);
|
||||||
|
|
||||||
|
QColor text_color = QColor(255, 255, 255, (int)(255 * lead_status_alpha));
|
||||||
|
if (text_lines[i].contains("m") || text_lines[i].contains("ft")) {
|
||||||
|
if (d_rel < 20.0f) {
|
||||||
|
text_color = QColor(255, 80, 80, (int)(255 * lead_status_alpha));
|
||||||
|
} else if (d_rel < 40.0f) {
|
||||||
|
text_color = QColor(255, 200, 80, (int)(255 * lead_status_alpha));
|
||||||
|
} else {
|
||||||
|
text_color = QColor(80, 255, 120, (int)(255 * lead_status_alpha));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
painter.setPen(text_color);
|
||||||
|
painter.drawText(rect, Qt::AlignCenter, text_lines[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
painter.setPen(Qt::NoPen);
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,6 +17,17 @@ private:
|
|||||||
void update_model(const cereal::ModelDataV2::Reader &model, const cereal::RadarState::LeadData::Reader &lead) override;
|
void update_model(const cereal::ModelDataV2::Reader &model, const cereal::RadarState::LeadData::Reader &lead) override;
|
||||||
void drawPath(QPainter &painter, const cereal::ModelDataV2::Reader &model, const QRect &rect) override;
|
void drawPath(QPainter &painter, const cereal::ModelDataV2::Reader &model, const QRect &rect) override;
|
||||||
|
|
||||||
|
// Lead status display methods
|
||||||
|
void drawLeadStatus(QPainter &painter, int height, int width);
|
||||||
|
void drawLeadStatusAtPosition(QPainter &painter,
|
||||||
|
const cereal::RadarState::LeadData::Reader &lead_data,
|
||||||
|
const QPointF &chevron_pos,
|
||||||
|
int height, int width,
|
||||||
|
const QString &label);
|
||||||
|
|
||||||
QPolygonF left_blindspot_vertices;
|
QPolygonF left_blindspot_vertices;
|
||||||
QPolygonF right_blindspot_vertices;
|
QPolygonF right_blindspot_vertices;
|
||||||
|
|
||||||
|
// Lead status animation
|
||||||
|
float lead_status_alpha = 0.0f;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,22 +18,13 @@ 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 ¶m_name, const QString ¶m_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.
|
||||||
@@ -48,11 +39,6 @@ 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);
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
#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
|
||||||
@@ -74,7 +73,6 @@ 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();
|
||||||
@@ -94,5 +92,3 @@ private:
|
|||||||
|
|
||||||
DeviceSP *deviceSP();
|
DeviceSP *deviceSP();
|
||||||
inline DeviceSP *device() { return deviceSP(); }
|
inline DeviceSP *device() { return deviceSP(); }
|
||||||
|
|
||||||
void ui_update_params_sp(UIStateSP *s);
|
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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;
|
|
||||||
@@ -66,11 +66,6 @@ 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.param_list
|
CC_SP.params = self.param_store.publish()
|
||||||
|
|
||||||
return CC_SP
|
return CC_SP
|
||||||
|
|
||||||
|
|||||||
@@ -4,41 +4,39 @@ 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]
|
||||||
_params: dict[str, custom.CarControlSP.Param]
|
values: dict[str, str]
|
||||||
|
|
||||||
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._params = {}
|
self.values = {}
|
||||||
|
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
|
|
||||||
|
|
||||||
for key in self.keys:
|
def publish(self) -> list[capnp.lib.capnp._DynamicStructBuilder]:
|
||||||
param_type = params.get_type(key).name.lower() # Using string instead of number because its "loose" dependency, and could change by OP at anytime.
|
if self.cached_params_list is None:
|
||||||
|
# TODO-SP: Why are we doing a list instead of a dictionary here?
|
||||||
# Over engineering opportunity: It's possible this conversion is slow, we may check the value as params returns it for cache purposes. Not today.
|
self.cached_params_list = [custom.CarControlSP.Param(key=k, value=self.values[k]) for k in self.keys]
|
||||||
param_value = get_param_as_byte(key, params)
|
return self.cached_params_list
|
||||||
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()]
|
|
||||||
|
|||||||
@@ -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, save_param_from_base64_encoded_string
|
from sunnypilot.sunnylink.utils import sunnylink_need_register, sunnylink_ready, get_param_as_byte
|
||||||
|
|
||||||
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"))
|
||||||
@@ -50,7 +50,7 @@ def handle_long_poll(ws: WebSocket, exit_event: threading.Event | None) -> None:
|
|||||||
threading.Thread(target=ws_ping, args=(ws, end_event), name='ws_ping'),
|
threading.Thread(target=ws_ping, args=(ws, end_event), name='ws_ping'),
|
||||||
threading.Thread(target=ws_queue, args=(end_event,), name='ws_queue'),
|
threading.Thread(target=ws_queue, args=(end_event,), name='ws_queue'),
|
||||||
threading.Thread(target=upload_handler, args=(end_event,), name='upload_handler'),
|
threading.Thread(target=upload_handler, args=(end_event,), name='upload_handler'),
|
||||||
# threading.Thread(target=sunny_log_handler, args=(end_event, comma_prime_cellular_end_event), name='log_handler'),
|
threading.Thread(target=sunny_log_handler, args=(end_event, comma_prime_cellular_end_event), name='log_handler'),
|
||||||
# threading.Thread(target=stat_handler, args=(end_event,), name='stat_handler'),
|
# threading.Thread(target=stat_handler, args=(end_event,), name='stat_handler'),
|
||||||
] + [
|
] + [
|
||||||
threading.Thread(target=jsonrpc_handler, args=(end_event, partial(startLocalProxy, end_event),), name=f'worker_{x}')
|
threading.Thread(target=jsonrpc_handler, args=(end_event, partial(startLocalProxy, end_event),), name=f'worker_{x}')
|
||||||
@@ -184,18 +184,14 @@ 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(value) if compression else value).decode('utf-8'),
|
"value": base64.b64encode(gzip.compress(get_param_as_byte(key)) if compression else get_param_as_byte(key)).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
|
||||||
@@ -208,9 +204,15 @@ 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:
|
||||||
for key, value in params_to_update.items():
|
params = Params()
|
||||||
|
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:
|
||||||
save_param_from_base64_encoded_string(key, value, compression)
|
params.put(key, value)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
cloudlog.error(f"sunnylinkd.saveParams.exception {e}")
|
cloudlog.error(f"sunnylinkd.saveParams.exception {e}")
|
||||||
|
|
||||||
|
|||||||
@@ -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, ParamKeyFlag
|
from openpilot.common.params import Params, ParamKeyType, 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, save_param_from_base64_encoded_string
|
from sunnypilot.sunnylink.utils import get_param_as_byte
|
||||||
|
|
||||||
|
|
||||||
class OperationType(Enum):
|
class OperationType(Enum):
|
||||||
@@ -173,7 +173,8 @@ class BackupManagerSP:
|
|||||||
self._update_progress(75.0, OperationType.RESTORE)
|
self._update_progress(75.0, OperationType.RESTORE)
|
||||||
|
|
||||||
# Apply configuration
|
# Apply configuration
|
||||||
self._apply_config(config_data)
|
all_values_encoded = self._get_metadata_value(backup_metadata, "all_values_encoded", "false")
|
||||||
|
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)
|
||||||
@@ -186,7 +187,7 @@ class BackupManagerSP:
|
|||||||
self._report_status()
|
self._report_status()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _apply_config(self, config_data: dict[str, str]) -> None:
|
def _apply_config(self, config_data: dict[str, str], all_values_encoded: bool = False) -> 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}
|
||||||
@@ -198,8 +199,26 @@ 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:
|
||||||
save_param_from_base64_encoded_string(real_param, encoded_value)
|
value = base64.b64decode(encoded_value) if all_values_encoded else 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)}")
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
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
|
||||||
@@ -60,57 +58,13 @@ def get_api_token():
|
|||||||
print(f"API Token: {token}")
|
print(f"API Token: {token}")
|
||||||
|
|
||||||
|
|
||||||
def get_param_as_byte(param_name: str, params=None) -> bytes | None:
|
def get_param_as_byte(param_name: str) -> bytes:
|
||||||
"""Get a parameter as bytes. Returns None if the parameter does not exist."""
|
params = Params()
|
||||||
params = params or Params() # Use existing Params instance if provided
|
|
||||||
param = params.get(param_name)
|
param = params.get(param_name)
|
||||||
if param is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
param_type = params.get_type(param_name)
|
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
|
|
||||||
|
|||||||
+1
-1
@@ -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', 'staging-tici']
|
TESTED_SP_BRANCHES = ['staging-c3', 'staging-c3-new']
|
||||||
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
|
||||||
|
|||||||
Vendored
BIN
Binary file not shown.
@@ -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:d0afaf3b005e35e14b929d5491d2d5b64562d0c1cd5093ba969fb63908670dd4" },
|
{ 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" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
|
|||||||
Reference in New Issue
Block a user